Skip to content

Commit 54ba773

Browse files
authored
Merge pull request #304 from Titas-Ghosh/fix-validate-exit-code
2 parents 60e00cc + 88138af commit 54ba773

4 files changed

Lines changed: 42 additions & 9 deletions

File tree

concore_cli/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ Checks:
7979
- File references and naming conventions
8080
- ZMQ vs file-based communication
8181

82+
**Options:**
83+
- `-s, --source <dir>` - Source directory (default: src)
84+
8285
**Example:**
8386
```bash
8487
concore validate workflow.graphml

concore_cli/cli.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,13 @@ def run(workflow_file, source, output, type, auto_build):
4545

4646
@cli.command()
4747
@click.argument('workflow_file', type=click.Path(exists=True))
48-
def validate(workflow_file):
48+
@click.option('--source', '-s', default='src', help='Source directory')
49+
def validate(workflow_file, source):
4950
"""Validate a workflow file"""
5051
try:
51-
validate_workflow(workflow_file, console)
52+
ok = validate_workflow(workflow_file, source, console)
53+
if not ok:
54+
sys.exit(1)
5255
except Exception as e:
5356
console.print(f"[red]Error:[/red] {str(e)}")
5457
sys.exit(1)

concore_cli/commands/validate.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
import re
66
import xml.etree.ElementTree as ET
77

8-
def validate_workflow(workflow_file, console):
8+
def validate_workflow(workflow_file, source_dir, console):
99
workflow_path = Path(workflow_file)
10+
source_root = (workflow_path.parent / source_dir)
1011

1112
console.print(f"[cyan]Validating:[/cyan] {workflow_path.name}")
1213
console.print()
@@ -15,31 +16,35 @@ def validate_workflow(workflow_file, console):
1516
warnings = []
1617
info = []
1718

19+
def finalize():
20+
show_results(console, errors, warnings, info)
21+
return len(errors) == 0
22+
1823
try:
1924
with open(workflow_path, 'r') as f:
2025
content = f.read()
2126

2227
if not content.strip():
2328
errors.append("File is empty")
24-
return show_results(console, errors, warnings, info)
29+
return finalize()
2530

2631
# strict XML syntax check
2732
try:
2833
ET.fromstring(content)
2934
except ET.ParseError as e:
3035
errors.append(f"Invalid XML: {str(e)}")
31-
return show_results(console, errors, warnings, info)
36+
return finalize()
3237

3338
try:
3439
soup = BeautifulSoup(content, 'xml')
3540
except Exception as e:
3641
errors.append(f"Invalid XML: {str(e)}")
37-
return show_results(console, errors, warnings, info)
42+
return finalize()
3843

3944
root = soup.find('graphml')
4045
if not root:
4146
errors.append("Not a valid GraphML file - missing <graphml> root element")
42-
return show_results(console, errors, warnings, info)
47+
return finalize()
4348

4449
# check the graph attributes
4550
graph = soup.find('graph')
@@ -64,6 +69,9 @@ def validate_workflow(workflow_file, console):
6469
warnings.append("No edges found in workflow")
6570
else:
6671
info.append(f"Found {len(edges)} edge(s)")
72+
73+
if not source_root.exists():
74+
warnings.append(f"Source directory not found: {source_root}")
6775

6876
node_labels = []
6977
for node in nodes:
@@ -101,6 +109,10 @@ def validate_workflow(workflow_file, console):
101109
errors.append(f"Node '{label}' has no filename")
102110
elif not any(filename.endswith(ext) for ext in ['.py', '.cpp', '.m', '.v', '.java']):
103111
warnings.append(f"Node '{label}' has unusual file extension")
112+
elif source_root.exists():
113+
file_path = source_root / filename
114+
if not file_path.exists():
115+
errors.append(f"Missing source file: {filename}")
104116
else:
105117
warnings.append(f"Node {node_id} has no label")
106118
except Exception as e:
@@ -143,12 +155,14 @@ def validate_workflow(workflow_file, console):
143155
if file_edges > 0:
144156
info.append(f"File-based edges: {file_edges}")
145157

146-
show_results(console, errors, warnings, info)
147-
158+
return finalize()
159+
148160
except FileNotFoundError:
149161
console.print(f"[red]Error:[/red] File not found: {workflow_path}")
162+
return False
150163
except Exception as e:
151164
console.print(f"[red]Validation failed:[/red] {str(e)}")
165+
return False
152166

153167
def show_results(console, errors, warnings, info):
154168
if errors:

tests/test_cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ def test_validate_valid_file(self):
5858
result = self.runner.invoke(cli, ['validate', 'test-project/workflow.graphml'])
5959
self.assertEqual(result.exit_code, 0)
6060
self.assertIn('Validation passed', result.output)
61+
62+
def test_validate_missing_node_file(self):
63+
with self.runner.isolated_filesystem(temp_dir=self.temp_dir):
64+
result = self.runner.invoke(cli, ['init', 'test-project'])
65+
self.assertEqual(result.exit_code, 0)
66+
67+
missing_file = Path('test-project/src/script.py')
68+
if missing_file.exists():
69+
missing_file.unlink()
70+
71+
result = self.runner.invoke(cli, ['validate', 'test-project/workflow.graphml'])
72+
self.assertNotEqual(result.exit_code, 0)
73+
self.assertIn('Missing source file', result.output)
6174

6275
def test_status_command(self):
6376
result = self.runner.invoke(cli, ['status'])

0 commit comments

Comments
 (0)