delete(docs) remove infraestructura index duplicates #939
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Validate Requirements Traceability | ||
| on: | ||
| pull_request: | ||
| paths: | ||
| - 'implementacion/**/requisitos/**/*.md' | ||
| push: | ||
| branches: | ||
| - main | ||
| - develop | ||
| paths: | ||
| - 'implementacion/**/requisitos/**/*.md' | ||
| workflow_dispatch: | ||
| jobs: | ||
| validate-traceability: | ||
| name: Validate Traceability Links | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
| - name: Validate traceability | ||
| run: | | ||
| python3 << 'EOF' | ||
| import os | ||
| import re | ||
| import sys | ||
| from collections import defaultdict | ||
| print("Validating requirements traceability...") | ||
| print("=" * 80) | ||
| all_req_ids = set() | ||
| requirements = {} | ||
| broken_links = [] | ||
| orphaned_requirements = [] | ||
| # First pass: collect all requirement IDs | ||
| for root, dirs, files in os.walk('implementacion'): | ||
| if 'requisitos' not in root: | ||
| continue | ||
| for file in files: | ||
| if not file.endswith('.md') or file.startswith('_'): | ||
| continue | ||
| filepath = os.path.join(root, file) | ||
| with open(filepath, 'r', encoding='utf-8') as f: | ||
| content = f.read() | ||
| match = re.match(r'^---\s* | ||
| (.*?) | ||
| ---\s* | ||
| ', content, re.DOTALL) | ||
| if not match: | ||
| continue | ||
| yaml_content = match.group(1) | ||
| fields = {} | ||
| current_list = None | ||
| for line in yaml_content.split(' | ||
| '): | ||
| line = line.rstrip() | ||
| if current_list and line.startswith(' - '): | ||
| value = line[4:].strip() | ||
| fields[current_list].append(value) | ||
| else: | ||
| current_list = None | ||
| if ':' in line and not line.startswith(' '): | ||
| key, value = line.split(':', 1) | ||
| key = key.strip() | ||
| value = value.strip() | ||
| if value == '[]' or not value: | ||
| fields[key] = [] | ||
| current_list = key | ||
| else: | ||
| fields[key] = value | ||
| if 'id' in fields: | ||
| req_id = fields['id'] | ||
| all_req_ids.add(req_id) | ||
| requirements[req_id] = { | ||
| 'path': filepath, | ||
| 'tipo': fields.get('tipo', ''), | ||
| 'upward': fields.get('trazabilidad_upward', []), | ||
| 'downward': fields.get('trazabilidad_downward', []) | ||
| } | ||
| print(f"Found {len(all_req_ids)} requirements") | ||
| # Second pass: validate traceability links | ||
| for req_id, data in requirements.items(): | ||
| # Check upward references | ||
| for parent_id in data['upward']: | ||
| if parent_id not in all_req_ids: | ||
| broken_links.append({ | ||
| 'req_id': req_id, | ||
| 'path': data['path'], | ||
| 'missing': parent_id, | ||
| 'direction': 'upward' | ||
| }) | ||
| # Check downward references | ||
| for child_id in data['downward']: | ||
| if child_id not in all_req_ids: | ||
| broken_links.append({ | ||
| 'req_id': req_id, | ||
| 'path': data['path'], | ||
| 'missing': child_id, | ||
| 'direction': 'downward' | ||
| }) | ||
| # Check for orphaned requirements (no upward traceability) | ||
| if data['tipo'] not in ['necesidad'] and not data['upward']: | ||
| orphaned_requirements.append({ | ||
| 'req_id': req_id, | ||
| 'path': data['path'], | ||
| 'tipo': data['tipo'] | ||
| }) | ||
| print("") | ||
| print("Results:") | ||
| print(f" Broken links: {len(broken_links)}") | ||
| print(f" Orphaned requirements: {len(orphaned_requirements)}") | ||
| if broken_links: | ||
| print("") | ||
| print("BROKEN LINKS:") | ||
| for link in broken_links: | ||
| print(f" {link['req_id']} ({link['path']})") | ||
| print(f" -> References non-existent {link['direction']} link: {link['missing']}") | ||
| if orphaned_requirements: | ||
| print("") | ||
| print("ORPHANED REQUIREMENTS (no upward traceability):") | ||
| for req in orphaned_requirements: | ||
| print(f" {req['req_id']} ({req['tipo']}) - {req['path']}") | ||
| print("") | ||
| if broken_links: | ||
| print("VALIDATION FAILED: Broken traceability links found") | ||
| sys.exit(1) | ||
| else: | ||
| print("VALIDATION PASSED: All traceability links are valid") | ||
| if orphaned_requirements: | ||
| print(f"WARNING: {len(orphaned_requirements)} orphaned requirements (informational)") | ||
| sys.exit(0) | ||
| EOF | ||
| - name: Generate traceability report | ||
| if: always() | ||
| run: | | ||
| echo "## Traceability Validation Report" >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "Validates that all trazabilidad_upward and trazabilidad_downward references" >> $GITHUB_STEP_SUMMARY | ||
| echo "point to existing requirements." >> $GITHUB_STEP_SUMMARY | ||
| echo "" >> $GITHUB_STEP_SUMMARY | ||
| echo "ISO/IEC/IEEE 29148:2018 - Clause 5.2.8: Traceability" >> $GITHUB_STEP_SUMMARY | ||