Skip to content

Commit 0d6b7b3

Browse files
authored
Merge pull request #279 from 2-Coatl/feature/review-files-in-.github/workflows-22-58-17
Add actionlint workflow and fix requirements traceability validator
2 parents 406368e + 7bb785a commit 0d6b7b3

3 files changed

Lines changed: 123 additions & 54 deletions

File tree

.github/workflows/REVIEW.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Revisión de workflows
2+
3+
Resumen automático de los triggers y jobs definidos en `.github/workflows`.
4+
5+
| Workflow | Triggers | Jobs principales |
6+
| --- | --- | --- |
7+
| actionlint.yml | push, pull_request, workflow_dispatch | actionlint |
8+
| agents-ci.yml | push, pull_request, workflow_dispatch | code-quality, tests, module-tests, performance, security-tests, integration-tests, build-status |
9+
| backend-ci.yml | push, pull_request | lint, test-mysql, test-postgresql, validate-restrictions, integration-tests, summary |
10+
| code-quality.yml | pull_request, workflow_dispatch | smoke-checks |
11+
| codeql.yml | push, pull_request, schedule | analyze |
12+
| dependency-review.yml | pull_request | review |
13+
| deploy.yml | push, workflow_dispatch | pre-deployment-checks, run-tests, build-backend, build-frontend, deploy-staging, deploy-production, post-deployment-monitoring |
14+
| docs-validation.yml | pull_request, push | validate-structure, check-old-references, check-markdown-links, validate-auto-generated-docs, count-docs-stats, summary |
15+
| docs.yml | push, pull_request, workflow_dispatch | build, deploy, check-links |
16+
| emoji-validation.yml | pull_request, push | check-emojis |
17+
| frontend-ci.yml | push, pull_request | lint, test-unit, test-integration, test-e2e, build, accessibility, security, summary |
18+
| incident-response.yml | workflow_dispatch | create-incident-issue, gather-diagnostics, execute-incident-playbook, notify-team, summary |
19+
| infrastructure-ci.yml | push, pull_request | validate-shell-scripts, test-validation-scripts, validate-terraform, validate-docker, validate-configurations, test-health-check, summary |
20+
| lint.yml | pull_request, push | lint-frontmatter |
21+
| meta-architecture-check.yml | pull_request, push, workflow_dispatch | architecture-analysis, code-quality-gate |
22+
| migrations.yml | pull_request, push | detect-migrations, validate-migrations, check-migration-safety, generate-migration-report, summary |
23+
| pr-review.yml | issue_comment | pr-validation |
24+
| python_ci.yml | push, pull_request, workflow_dispatch | code-quality, tests, performance, dependency-check, build-status |
25+
| release.yml | push, workflow_dispatch | validate-version, generate-changelog, create-release-packages, update-version-files, create-github-release, notify-stakeholders, release-summary |
26+
| requirements_index.yml | push, pull_request, workflow_dispatch | generate-indices |
27+
| requirements_validate_traceability.yml | pull_request, push, workflow_dispatch | validate-traceability |
28+
| security-scan.yml | push, pull_request, schedule | bandit-scan, npm-audit, safety-check, django-security-check, trivy-scan, secrets-scan, sql-injection-check, xss-check, csrf-check, generate-security-report, summary |
29+
| sync-docs.yml | schedule, workflow_dispatch | sync-documentation, notify-failure |
30+
| test-pyramid.yml | push, pull_request, schedule | analyze-test-pyramid, test-execution-time, summary |
31+
| validate-guides.yml | pull_request, push, workflow_dispatch | validate-structure, check-broken-links, generate-coverage-report, quality-checks, summary |
32+
33+
## Hallazgos destacados
34+
35+
- **requirements_validate_traceability.yml**: el script de validación estaba mal indentado y sin parsing robusto del front matter, lo que podía provocar errores de ejecución. Se reescribió con PyYAML, normalización de listas y reporte explícito de errores para evitar falsos positivos.
36+
- **Cobertura de calidad de YAML**: no existía un guardrail automático para los workflows. Se añadió `actionlint.yml` para validar sintaxis y convenciones de GitHub Actions en `push`, `pull_request` y `workflow_dispatch`.
37+
- **Optimización pendiente**: `deploy.yml` puede beneficiarse de cachear dependencias Python (setup-python con `cache: 'pip'`) para acelerar las ejecuciones. `pr-review.yml` funciona solo con `issue_comment`; si se requiere validación previa al comentario, añadir `workflow_dispatch` como trigger manual daría más control.

.github/workflows/actionlint.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Lint GitHub Actions
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- develop
8+
paths:
9+
- '.github/workflows/**'
10+
pull_request:
11+
branches:
12+
- main
13+
- develop
14+
paths:
15+
- '.github/workflows/**'
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: read
20+
21+
jobs:
22+
actionlint:
23+
name: Validate workflow syntax
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Checkout repository
27+
uses: actions/checkout@v4
28+
29+
- name: Run actionlint
30+
uses: docker://ghcr.io/rhysd/actionlint:1.7.1
31+
with:
32+
args: -color

.github/workflows/requirements_validate_traceability.yml

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ jobs:
2121
- name: Checkout repository
2222
uses: actions/checkout@v4
2323

24+
- name: Install validation dependencies
25+
run: pip install pyyaml
26+
2427
- name: Setup Python
2528
uses: actions/setup-python@v5
2629
with:
@@ -32,7 +35,18 @@ jobs:
3235
import os
3336
import re
3437
import sys
35-
from collections import defaultdict
38+
from typing import Iterable, List
39+
40+
import yaml
41+
42+
FRONT_MATTER_PATTERN = re.compile(r"^---\s*\n(.*?)\n---\s*", re.DOTALL)
43+
44+
def ensure_list(value: Iterable | str | None) -> List[str]:
45+
if value is None:
46+
return []
47+
if isinstance(value, str):
48+
return [value.strip()] if value.strip() else []
49+
return [str(item).strip() for item in value if str(item).strip()]
3650
3751
print("Validating requirements traceability...")
3852
print("=" * 80)
@@ -41,9 +55,10 @@ jobs:
4155
requirements = {}
4256
broken_links = []
4357
orphaned_requirements = []
58+
invalid_front_matter = []
4459
4560
# First pass: collect all requirement IDs
46-
for root, dirs, files in os.walk('implementacion'):
61+
for root, _, files in os.walk('implementacion'):
4762
if 'requisitos' not in root:
4863
continue
4964
@@ -56,84 +71,62 @@ jobs:
5671
with open(filepath, 'r', encoding='utf-8') as f:
5772
content = f.read()
5873
59-
match = re.match(r'^---\s*
60-
(.*?)
61-
---\s*
62-
', content, re.DOTALL)
74+
match = FRONT_MATTER_PATTERN.match(content)
6375
if not match:
6476
continue
6577
66-
yaml_content = match.group(1)
67-
fields = {}
68-
current_list = None
69-
70-
for line in yaml_content.split('
71-
'):
72-
line = line.rstrip()
73-
74-
if current_list and line.startswith(' - '):
75-
value = line[4:].strip()
76-
fields[current_list].append(value)
77-
else:
78-
current_list = None
79-
80-
if ':' in line and not line.startswith(' '):
81-
key, value = line.split(':', 1)
82-
key = key.strip()
83-
value = value.strip()
84-
85-
if value == '[]' or not value:
86-
fields[key] = []
87-
current_list = key
88-
else:
89-
fields[key] = value
90-
91-
if 'id' in fields:
92-
req_id = fields['id']
93-
all_req_ids.add(req_id)
94-
requirements[req_id] = {
95-
'path': filepath,
96-
'tipo': fields.get('tipo', ''),
97-
'upward': fields.get('trazabilidad_upward', []),
98-
'downward': fields.get('trazabilidad_downward', [])
99-
}
78+
try:
79+
metadata = yaml.safe_load(match.group(1)) or {}
80+
except yaml.YAMLError as exc:
81+
invalid_front_matter.append({'path': filepath, 'error': str(exc)})
82+
continue
83+
84+
req_id = metadata.get('id')
85+
if not req_id:
86+
continue
87+
88+
all_req_ids.add(req_id)
89+
requirements[req_id] = {
90+
'path': filepath,
91+
'tipo': metadata.get('tipo', ''),
92+
'upward': ensure_list(metadata.get('trazabilidad_upward')),
93+
'downward': ensure_list(metadata.get('trazabilidad_downward')),
94+
}
10095
10196
print(f"Found {len(all_req_ids)} requirements")
10297
10398
# Second pass: validate traceability links
10499
for req_id, data in requirements.items():
105-
# Check upward references
106100
for parent_id in data['upward']:
107101
if parent_id not in all_req_ids:
108102
broken_links.append({
109103
'req_id': req_id,
110104
'path': data['path'],
111105
'missing': parent_id,
112-
'direction': 'upward'
106+
'direction': 'upward',
113107
})
114108
115-
# Check downward references
116109
for child_id in data['downward']:
117110
if child_id not in all_req_ids:
118111
broken_links.append({
119112
'req_id': req_id,
120113
'path': data['path'],
121114
'missing': child_id,
122-
'direction': 'downward'
115+
'direction': 'downward',
123116
})
124117
125-
# Check for orphaned requirements (no upward traceability)
126118
if data['tipo'] not in ['necesidad'] and not data['upward']:
127119
orphaned_requirements.append({
128120
'req_id': req_id,
129-
'path': data['path'],
130-
'tipo': data['tipo']
121+
'path': data['path'],
122+
'tipo': data['tipo'],
131123
})
132124
133125
print("")
134126
print("Results:")
135127
print(f" Broken links: {len(broken_links)}")
136128
print(f" Orphaned requirements: {len(orphaned_requirements)}")
129+
print(f" Invalid front matter: {len(invalid_front_matter)}")
137130
138131
if broken_links:
139132
print("")
@@ -148,15 +141,22 @@ jobs:
148141
for req in orphaned_requirements:
149142
print(f" {req['req_id']} ({req['tipo']}) - {req['path']}")
150143
144+
if invalid_front_matter:
145+
print("")
146+
print("INVALID FRONT MATTER:")
147+
for item in invalid_front_matter:
148+
print(f" {item['path']}")
149+
print(f" -> {item['error']}")
150+
151151
print("")
152-
if broken_links:
153-
print("VALIDATION FAILED: Broken traceability links found")
152+
if broken_links or invalid_front_matter:
153+
print("VALIDATION FAILED: Broken traceability links or invalid front matter found")
154154
sys.exit(1)
155-
else:
156-
print("VALIDATION PASSED: All traceability links are valid")
157-
if orphaned_requirements:
158-
print(f"WARNING: {len(orphaned_requirements)} orphaned requirements (informational)")
159-
sys.exit(0)
155+
156+
print("VALIDATION PASSED: All traceability links are valid")
157+
if orphaned_requirements:
158+
print(f"WARNING: {len(orphaned_requirements)} orphaned requirements (informational)")
159+
sys.exit(0)
160160
EOF
161161
162162
- name: Generate traceability report

0 commit comments

Comments
 (0)