indentation fix(3) -- heredoc in tmp file #14
Workflow file for this run
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
| # ============================================================================== | |
| # This workflow: | |
| # 1. Checks out cdisc-rules-engine (the engine itself) | |
| # 2. Checks out cdisc-open-rules (rules + test data) into ./open-rules/ | |
| # 3. Installs engine Python dependencies | |
| # 4. Iterates every Published/ rule from cdisc-open-rules | |
| # 5. Runs the engine against each test case | |
| # 6. Compares output with committed results.csv baseline | |
| # 7. Publishes a Markdown report to Job Summary and as an artifact | |
| # ============================================================================== | |
| name: Validate Published Rules | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - 798-test-against-published | |
| workflow_dispatch: | |
| inputs: | |
| rules_ref: | |
| description: 'Branch/tag/SHA of cdisc-open-rules to validate against' | |
| required: false | |
| default: 'main' | |
| jobs: | |
| validate-published-rules: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| # ----------------------------------------------------------------------- | |
| # 1. Checkout cdisc-rules-engine | |
| # ----------------------------------------------------------------------- | |
| - name: Checkout cdisc-rules-engine | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: cdisc-org/cdisc-rules-engine | |
| path: engine | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| # ----------------------------------------------------------------------- | |
| # 2. Checkout cdisc-open-rules (rules + test data + helper scripts) | |
| # ----------------------------------------------------------------------- | |
| - name: Checkout cdisc-open-rules | |
| uses: actions/checkout@v6 | |
| with: | |
| repository: cdisc-org/cdisc-open-rules | |
| ref: ${{ inputs.rules_ref || 'rules_2' }} | |
| path: open-rules | |
| # If cdisc-open-rules is private, add a PAT secret: | |
| # token: ${{ secrets.CDISC_OPEN_RULES_TOKEN }} | |
| # ----------------------------------------------------------------------- | |
| # 2b. Debug — verify directory layout | |
| # ----------------------------------------------------------------------- | |
| - name: Debug — list workspace layout | |
| run: | | |
| echo "=== Workspace root ===" | |
| ls -la | |
| echo "=== open-rules/ ===" | |
| ls -la open-rules/ || echo "open-rules/ NOT FOUND" | |
| echo "=== open-rules/Published/ (first 10) ===" | |
| ls open-rules/Published/ 2>/dev/null | head -10 || echo "Published/ NOT FOUND" | |
| echo "=== engine/ ===" | |
| ls engine/ | head -10 || echo "engine/ NOT FOUND" | |
| # ----------------------------------------------------------------------- | |
| # 3. Set up Python | |
| # ----------------------------------------------------------------------- | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| # ----------------------------------------------------------------------- | |
| # 4. Install engine dependencies | |
| # ----------------------------------------------------------------------- | |
| - name: Install engine dependencies | |
| run: | | |
| python -m venv venv | |
| ./venv/bin/pip install --upgrade pip | |
| ./venv/bin/pip install -r engine/requirements.txt | |
| # ----------------------------------------------------------------------- | |
| # 5. Run validation for every Published rule | |
| # ----------------------------------------------------------------------- | |
| - name: Run validation for all Published rules | |
| id: validate | |
| continue-on-error: true | |
| run: | | |
| chmod +x open-rules/.github/scripts/run_validation.sh | |
| # Write the JSON-line parser once; called once per test case in the loop below | |
| cat > /tmp/parse_case.py << 'PYEOF' | |
| import sys, json, shlex | |
| d = json.load(sys.stdin) | |
| for k, v in [ | |
| ('CASE_RULE', d['rule']), | |
| ('CASE_TYPE', d['type']), | |
| ('CASE_NUM', str(d['num'])), | |
| ('EXEC_OK', '\u2705' if d['exec'] else '\u274c'), | |
| ('EXPECTED', str(d.get('expected', ''))), | |
| ('GOT', str(d.get('got', ''))), | |
| ('MATCH', '\u2705' if d.get('match') else '\u274c'), | |
| ('DIFF_FILE', str(d.get('diff', ''))), | |
| ('STDERR_FILE', str(d.get('stderr', ''))), | |
| ]: | |
| print(k + '=' + shlex.quote(v)) | |
| PYEOF | |
| PYTHON_CMD="$(pwd)/venv/bin/python" | |
| ENGINE_DIR="$(pwd)/engine" | |
| RULES_ROOT="$(pwd)/open-rules" | |
| PUBLISHED_DIR="$RULES_ROOT/Published" | |
| SCRIPTS_DIR="$RULES_ROOT/.github/scripts" | |
| SUMMARY_TABLE="$(pwd)/summary_table.md" | |
| DETAIL_REPORT="$(pwd)/detail_report.md" | |
| OVERALL_EXIT=0 | |
| RULE_PASS=0 | |
| RULE_FAIL=0 | |
| mapfile -t RULE_DIRS < <(find "$PUBLISHED_DIR" -mindepth 1 -maxdepth 1 -type d | sort) | |
| if [ ${#RULE_DIRS[@]} -eq 0 ]; then | |
| echo "::warning::No rule directories found under Published/" | |
| exit 0 | |
| fi | |
| echo "Found ${#RULE_DIRS[@]} rule(s) under Published/" | |
| # -- Initialise summary table | |
| { | |
| echo "# Published Rules Validation — Summary" | |
| echo "" | |
| echo "| Rule | Type | Number | Execution | Expected | Got | Match |" | |
| echo "|------|------|--------|-----------|----------|-----|-------|" | |
| } > "$SUMMARY_TABLE" | |
| # -- Initialise detail report | |
| { | |
| echo "# Published Rules Validation — Failure Details" | |
| echo "" | |
| } > "$DETAIL_REPORT" | |
| for RULE_DIR in "${RULE_DIRS[@]}"; do | |
| RULE_ID=$(basename "$RULE_DIR") | |
| RULE_YML=$(find "$RULE_DIR" -maxdepth 1 -name "*.yml" | head -1) | |
| if [ -z "$RULE_YML" ]; then | |
| echo "::warning::Skipping $RULE_ID — no .yml file found" | |
| continue | |
| fi | |
| echo "========================================" | |
| echo " Validating $RULE_ID" | |
| echo "========================================" | |
| RULE_EXIT=0 | |
| ENGINE_DIR_OVERRIDE="$ENGINE_DIR" \ | |
| bash "$SCRIPTS_DIR/run_validation.sh" \ | |
| "Published/$RULE_ID" \ | |
| "$PYTHON_CMD" \ | |
| "$RULES_ROOT" \ | |
| || RULE_EXIT=$? | |
| # -- Parse per-test-case results produced by run_validation.sh | |
| CASE_RESULTS="$RULES_ROOT/case_results.jsonl" | |
| RULE_ROW_FAILED=0 | |
| if [ -f "$CASE_RESULTS" ]; then | |
| while IFS= read -r line; do | |
| # Parse all fields in a single python3 call — script written once above | |
| eval "$(echo "$line" | python3 /tmp/parse_case.py)" | |
| echo "| $CASE_RULE | $CASE_TYPE | $CASE_NUM | $EXEC_OK | $EXPECTED | $GOT | $MATCH |" >> "$SUMMARY_TABLE" | |
| # Collect detail only for failures | |
| if [[ "$EXEC_OK" == "❌" || "$MATCH" == "❌" ]]; then | |
| RULE_ROW_FAILED=1 | |
| { | |
| echo "## $CASE_RULE — $CASE_TYPE / $CASE_NUM" | |
| if [[ "$EXEC_OK" == "❌" ]]; then | |
| echo "**Execution failed.**" | |
| if [ -f "$STDERR_FILE" ]; then | |
| echo '```' | |
| cat "$STDERR_FILE" | |
| echo '```' | |
| fi | |
| else | |
| echo "**Expected:** $EXPECTED **Got:** $GOT" | |
| if [ -n "$DIFF_FILE" ] && [ -f "$DIFF_FILE" ]; then | |
| echo '```diff' | |
| cat "$DIFF_FILE" | |
| echo '```' | |
| fi | |
| fi | |
| echo "" | |
| } >> "$DETAIL_REPORT" | |
| fi | |
| done < "$CASE_RESULTS" | |
| rm -f "$CASE_RESULTS" | |
| else | |
| # write a single aggregate row | |
| EXEC_OK=$( [ $RULE_EXIT -eq 0 ] && echo "✅" || echo "❌" ) | |
| echo "| $RULE_ID | — | — | $EXEC_OK | — | — | — |" >> "$SUMMARY_TABLE" | |
| if [ $RULE_EXIT -ne 0 ]; then | |
| RULE_ROW_FAILED=1 | |
| # Append whatever markdown run_validation.sh produced | |
| if [ -f "$RULES_ROOT/validation_report.md" ]; then | |
| { | |
| echo "## $RULE_ID" | |
| cat "$RULES_ROOT/validation_report.md" | |
| echo "" | |
| } >> "$DETAIL_REPORT" | |
| fi | |
| fi | |
| fi | |
| rm -f "$RULES_ROOT/validation_report.md" | |
| if [ $RULE_ROW_FAILED -eq 0 ] && [ $RULE_EXIT -eq 0 ]; then | |
| RULE_PASS=$((RULE_PASS + 1)) | |
| echo " → $RULE_ID: PASSED" | |
| else | |
| RULE_FAIL=$((RULE_FAIL + 1)) | |
| OVERALL_EXIT=1 | |
| echo " → $RULE_ID: FAILED" | |
| fi | |
| done | |
| # -- Insert totals line into summary table | |
| TOTALS="**Total:** $((RULE_PASS + RULE_FAIL)) | ✅ Passed: $RULE_PASS | ❌ Failed: $RULE_FAIL" | |
| sed -i "2s|^|$TOTALS\n\n|" "$SUMMARY_TABLE" | |
| exit $OVERALL_EXIT | |
| # ----------------------------------------------------------------------- | |
| # 6. Upload both reports + raw results as artifacts | |
| # ----------------------------------------------------------------------- | |
| - name: Upload validation artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: published-rules-validation-${{ github.run_id }} | |
| path: | | |
| open-rules/Published/**/results/results.json | |
| summary_table.md | |
| detail_report.md | |
| if-no-files-found: warn | |
| # ----------------------------------------------------------------------- | |
| # 7. Write ONLY the summary table to GitHub Actions Job Summary | |
| # ----------------------------------------------------------------------- | |
| - name: Write summary table to workflow summary | |
| if: always() | |
| run: | | |
| [ -f summary_table.md ] && cat summary_table.md >> $GITHUB_STEP_SUMMARY || true | |
| # ----------------------------------------------------------------------- | |
| # 8. Fail the job if any rule failed | |
| # ----------------------------------------------------------------------- | |
| - name: Check overall status | |
| if: steps.validate.outcome == 'failure' | |
| run: | | |
| echo "One or more published rules failed validation — see the artifacts for detail_report.md." | |
| exit 1 |