Skip to content

Commit d6203c2

Browse files
authored
Merge pull request #3 from tbrandenburg/codex/add-bash-n-syntax-checking-to-compile-command
2 parents 7d1b063 + 2672477 commit d6203c2

3 files changed

Lines changed: 81 additions & 0 deletions

File tree

src/cli/index.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ describe('CLI Output Option', () => {
107107
});
108108
});
109109

110+
describe('CLI Bash Syntax Validation', () => {
111+
it('should fail compilation when generated script has bash syntax errors', () => {
112+
const command = 'node dist/cli/index.js compile tests/workflows/invalid-bash-test.yaml';
113+
114+
try {
115+
execSync(command, { encoding: 'utf8' });
116+
expect(false).toBe(true);
117+
} catch (error: any) {
118+
const errorOutput = error.stderr?.toString() ?? error.stdout?.toString() ?? error.message;
119+
expect(errorOutput).toContain('Compilation failed:');
120+
expect(errorOutput).toContain('Bash syntax validation failed');
121+
}
122+
});
123+
});
124+
110125
describe('CLI DSL Command', () => {
111126
it('should execute dsl command and show DSL structure', () => {
112127
const command = 'node dist/cli/index.js dsl';

src/cli/index.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import { initCommand } from '../templates/init-command.js';
1414
import { DSLIntrospector } from '../dsl/introspection.js';
1515
import { parseWorkflowFile } from '../parsing/parser.js';
1616
import { Command } from 'commander';
17+
import { execFileSync } from 'child_process';
1718
import * as path from 'path';
1819
import * as fs from 'fs';
20+
import * as os from 'os';
1921

2022
// Simple error handling - no fancy logging or correlation IDs
2123
function handleError(error: unknown, operation: string): never {
@@ -53,6 +55,29 @@ async function writeScriptToFile(script: string, outputFile: string): Promise<vo
5355
}
5456
}
5557

58+
/**
59+
* Validate generated script for bash syntax errors.
60+
*/
61+
function validateBashSyntax(script: string): void {
62+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'flowsh-syntax-check-'));
63+
const tempFile = path.join(tempDir, 'script.sh');
64+
65+
try {
66+
fs.writeFileSync(tempFile, script, 'utf8');
67+
execFileSync('bash', ['-n', tempFile], { stdio: 'pipe' });
68+
} catch (error: any) {
69+
const errorOutput = error?.stderr?.toString?.() || error?.message || 'Unknown bash error';
70+
throw new Error(`Bash syntax validation failed:\n${errorOutput}`);
71+
} finally {
72+
try {
73+
fs.unlinkSync(tempFile);
74+
fs.rmdirSync(tempDir);
75+
} catch {
76+
// Best-effort cleanup
77+
}
78+
}
79+
}
80+
5681
/**
5782
* Compile command: Convert YAML workflow to shell script
5883
*/
@@ -123,6 +148,16 @@ async function compileCommand(
123148
finalScript = warnings.join('\n') + '\n' + finalScript;
124149
}
125150

151+
if (options.verbose) {
152+
console.error('🔍 Validating bash syntax...');
153+
}
154+
155+
validateBashSyntax(finalScript);
156+
157+
if (options.verbose) {
158+
console.error('✅ Bash syntax validation passed');
159+
}
160+
126161
// Dry-run mode: validate and compile but don't output
127162
if (options.dryRun) {
128163
const nodeCount = parseResult.workflow?.graph?.nodes?.length || 0;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
workflow:
2+
name: 'Invalid Bash'
3+
description: 'Workflow with intentionally invalid bash to test syntax validation'
4+
5+
graph:
6+
nodes:
7+
- id: 'start'
8+
type: 'start'
9+
data:
10+
title: 'Start'
11+
12+
- id: 'invalid_command'
13+
type: 'code'
14+
data:
15+
title: 'Invalid Command'
16+
description: 'Command with unterminated quote'
17+
command: 'echo "unterminated'
18+
19+
- id: 'end'
20+
type: 'end'
21+
data:
22+
title: 'End'
23+
24+
edges:
25+
- id: 'start_to_invalid'
26+
source: 'start'
27+
target: 'invalid_command'
28+
29+
- id: 'invalid_to_end'
30+
source: 'invalid_command'
31+
target: 'end'

0 commit comments

Comments
 (0)