Skip to content

Commit 3d5211f

Browse files
committed
Refactor Go command runner into modular architecture
- Refactored 956-line go/command-runner.js into 6 focused modules - Created go-command-runner-refactored.js (299 lines, 68.7% reduction) - Modules: command-executor, build-manager, test-runner, code-quality, dependency-manager, utility-runner - Updated 5 Go command files to use refactored version - Maintained 100% backward compatibility - All 97 tests pass - Follows same pattern as Clojure command runner refactoring
1 parent f753e14 commit 3d5211f

12 files changed

Lines changed: 1565 additions & 5 deletions

File tree

scripts/commands/go-build.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Build Go projects with Go-specific improvements
66
*/
77

8-
const GoCommandRunner = require('../go/command-runner');
8+
const GoCommandRunner = require("../go/go-command-runner-refactored");
99

1010
async function main() {
1111
const args = process.argv.slice(2);

scripts/commands/go-deps.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
const path = require('path');
99
const fs = require('fs');
10-
const GoCommandRunner = require('../go/command-runner');
10+
const GoCommandRunner = require("../go/go-command-runner-refactored");
1111

1212
async function main() {
1313
const args = process.argv.slice(2);

scripts/commands/go-fmt.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Format Go code with Go-specific improvements
66
*/
77

8-
const GoCommandRunner = require('../go/command-runner');
8+
const GoCommandRunner = require("../go/go-command-runner-refactored");
99

1010
async function main() {
1111
const args = process.argv.slice(2);

scripts/commands/go-lint.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Lint Go code with Go-specific improvements
66
*/
77

8-
const GoCommandRunner = require('../go/command-runner');
8+
const GoCommandRunner = require("../go/go-command-runner-refactored");
99

1010
async function main() {
1111
const args = process.argv.slice(2);

scripts/commands/go-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Run Go tests with Go-specific improvements
66
*/
77

8-
const GoCommandRunner = require('../go/command-runner');
8+
const GoCommandRunner = require("../go/go-command-runner-refactored");
99

1010
async function main() {
1111
const args = process.argv.slice(2);
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Go Build Manager Module
4+
*
5+
* Handles Go build operations including cross-compilation
6+
*/
7+
8+
const path = require('path');
9+
const { runCommand } = require('../../lib/utils');
10+
const { FileUtils, LoggingUtils, ensureDir } = require('../../lib');
11+
12+
class GoBuildManager {
13+
constructor(projectPath, commandExecutor, goConfig, detectedTools) {
14+
this.projectPath = projectPath;
15+
this.commandExecutor = commandExecutor;
16+
this.goConfig = goConfig;
17+
this.detectedTools = detectedTools;
18+
}
19+
20+
/**
21+
* Build Go project with Go-specific improvements
22+
*/
23+
async build(options = {}) {
24+
const args = [];
25+
26+
// Add build flags from config
27+
if (this.goConfig.build?.flags) {
28+
args.push(...this.goConfig.build.flags);
29+
}
30+
31+
// Add output directory
32+
if (options.output) {
33+
args.push('-o', options.output);
34+
} else {
35+
// Default output to ./bin/
36+
const binDir = path.join(this.projectPath, 'bin');
37+
38+
// Use ensureDir to create directory if it doesn't exist
39+
ensureDir(binDir);
40+
41+
const outputName = this.getOutputName();
42+
args.push('-o', path.join(binDir, outputName));
43+
}
44+
45+
// Add ldflags
46+
if (this.goConfig.build?.ldflags && this.goConfig.build.ldflags.length > 0) {
47+
args.push('-ldflags', this.goConfig.build.ldflags.join(' '));
48+
}
49+
50+
// Add tags
51+
if (options.tags) {
52+
args.push('-tags', options.tags);
53+
}
54+
55+
// Add race detector
56+
if (options.race) {
57+
args.push('-race');
58+
}
59+
60+
// Add build mode
61+
if (options.buildMode) {
62+
args.push('-buildmode', options.buildMode);
63+
}
64+
65+
// Handle cross-compilation via environment variables
66+
const target = options.target || this.detectBuildTarget();
67+
if (target) {
68+
const [goos, goarch] = target.split('/');
69+
if (goos && goarch) {
70+
// Set environment variables for cross-compilation
71+
options.env = {
72+
...(options.env || {}),
73+
GOOS: goos,
74+
GOARCH: goarch,
75+
CGO_ENABLED: '0',
76+
};
77+
}
78+
}
79+
80+
// Add verbose flag
81+
if (options.verbose) {
82+
args.push('-v');
83+
}
84+
85+
try {
86+
// Log some build information before starting
87+
const goFiles = this.findGoFiles();
88+
if (goFiles.length > 0) {
89+
LoggingUtils.debug(`Found ${goFiles.length} Go files to build`);
90+
}
91+
92+
const moduleInfo = this.getGoModuleInfo();
93+
if (moduleInfo) {
94+
LoggingUtils.debug(`Building module: ${moduleInfo}`);
95+
}
96+
97+
const result = await this.commandExecutor.executeGoCommand('build', args, options);
98+
99+
// Go-specific: Show build information
100+
if (result.success) {
101+
await this.showBuildInfo(options);
102+
}
103+
104+
return result;
105+
} catch (error) {
106+
// Go-specific: Provide helpful build error suggestions
107+
this.suggestBuildFix(error.message);
108+
throw error;
109+
}
110+
}
111+
112+
/**
113+
* Get output name based on project type
114+
*/
115+
getOutputName() {
116+
if (this.goConfig.projectType === 'cli') {
117+
const moduleParts = (this.goConfig.module || 'app').split('/');
118+
return moduleParts[moduleParts.length - 1];
119+
}
120+
121+
// Default to directory name
122+
return path.basename(this.projectPath);
123+
}
124+
125+
/**
126+
* Find Go files in the project
127+
*/
128+
findGoFiles(pattern = '**/*.go', excludePatterns = []) {
129+
try {
130+
return FileUtils.findFilesByPattern(pattern, {
131+
cwd: this.projectPath,
132+
exclude: excludePatterns,
133+
language: 'go',
134+
});
135+
} catch (error) {
136+
LoggingUtils.warn('Failed to find Go files:', error.message);
137+
return [];
138+
}
139+
}
140+
141+
/**
142+
* Get Go module information
143+
*/
144+
getGoModuleInfo() {
145+
try {
146+
const goModPath = path.join(this.projectPath, 'go.mod');
147+
if (FileUtils.fileExists(goModPath)) {
148+
const content = FileUtils.readFile(goModPath);
149+
const moduleMatch = content.match(/module\s+(\S+)/);
150+
return moduleMatch ? moduleMatch[1] : null;
151+
}
152+
return null;
153+
} catch (error) {
154+
LoggingUtils.debug('Failed to read go.mod:', error.message);
155+
return null;
156+
}
157+
}
158+
159+
/**
160+
* Detect build target based on environment
161+
*/
162+
detectBuildTarget() {
163+
const platform = process.platform;
164+
const arch = process.arch;
165+
166+
const targetMap = {
167+
darwin: {
168+
x64: 'darwin/amd64',
169+
arm64: 'darwin/arm64',
170+
},
171+
linux: {
172+
x64: 'linux/amd64',
173+
arm64: 'linux/arm64',
174+
arm: 'linux/arm',
175+
},
176+
win32: {
177+
x64: 'windows/amd64',
178+
ia32: 'windows/386',
179+
},
180+
};
181+
182+
return targetMap[platform]?.[arch] || null;
183+
}
184+
185+
/**
186+
* Show build information
187+
*/
188+
async showBuildInfo(options) {
189+
try {
190+
// Get Go version
191+
const versionResult = runCommand('go version', { cwd: this.projectPath });
192+
193+
// Get module info
194+
const moduleResult = runCommand('go list -m', { cwd: this.projectPath });
195+
196+
// Get build constraints
197+
const constraintsResult = runCommand('go list -f "{{.GoFiles}}" ./...', {
198+
cwd: this.projectPath,
199+
});
200+
201+
LoggingUtils.info('📦 Build Information:');
202+
LoggingUtils.info(` • Go Version: ${versionResult.stdout?.trim() || 'Unknown'}`);
203+
LoggingUtils.info(` • Module: ${moduleResult.stdout?.trim() || 'Not a module'}`);
204+
205+
if (constraintsResult.stdout) {
206+
const fileCount = constraintsResult.stdout.split(/\s+/).filter(Boolean).length;
207+
LoggingUtils.info(` • Go Files: ${fileCount}`);
208+
}
209+
210+
// Show cross-compilation info if applicable
211+
if (options.env?.GOOS || options.env?.GOARCH) {
212+
LoggingUtils.info(
213+
` • Target: ${options.env.GOOS || 'current'}/${options.env.GOARCH || 'current'}`
214+
);
215+
}
216+
217+
// Show output location
218+
const outputDir = path.join(this.projectPath, 'bin');
219+
if (FileUtils.directoryExists(outputDir)) {
220+
const files = FileUtils.findFilesByPattern('*', { cwd: outputDir });
221+
LoggingUtils.info(` • Output Directory: ${outputDir} (${files.length} files)`);
222+
}
223+
} catch (error) {
224+
LoggingUtils.debug('Failed to show build info:', error.message);
225+
}
226+
}
227+
228+
/**
229+
* Suggest fixes for common build errors
230+
*/
231+
suggestBuildFix(errorMessage) {
232+
const suggestions = [];
233+
234+
if (errorMessage.includes('cannot find package')) {
235+
suggestions.push('Run /go-deps to download dependencies');
236+
suggestions.push('Check if go.mod file exists and is valid');
237+
suggestions.push('Verify module path in go.mod matches import statements');
238+
}
239+
240+
if (errorMessage.includes('undefined')) {
241+
suggestions.push('Check for typos in function/variable names');
242+
suggestions.push('Verify imports are correct');
243+
suggestions.push('Run /go-fmt to format code and catch syntax errors');
244+
}
245+
246+
if (errorMessage.includes('syntax error')) {
247+
suggestions.push('Run /go-fmt to format code');
248+
suggestions.push('Check for missing parentheses, braces, or semicolons');
249+
suggestions.push('Verify Go version compatibility');
250+
}
251+
252+
if (errorMessage.includes('CGO')) {
253+
suggestions.push('Set CGO_ENABLED=0 for pure Go builds');
254+
suggestions.push('Install C compiler if CGO is required');
255+
suggestions.push('Check cross-compilation settings');
256+
}
257+
258+
if (suggestions.length > 0) {
259+
LoggingUtils.info('💡 Build error suggestions:');
260+
suggestions.forEach((suggestion, i) => {
261+
LoggingUtils.info(` ${i + 1}. ${suggestion}`);
262+
});
263+
}
264+
}
265+
}
266+
267+
module.exports = GoBuildManager;

0 commit comments

Comments
 (0)