diff --git a/.github/workflows/fhir-validation.yml b/.github/workflows/fhir-validation.yml index 5089fda..e2a0e12 100644 --- a/.github/workflows/fhir-validation.yml +++ b/.github/workflows/fhir-validation.yml @@ -148,6 +148,7 @@ jobs: echo "No relevant FHIR JSON/XML changes were detected in tracked spec folders. Validation skipped." >> "$GITHUB_STEP_SUMMARY" - name: Validate meta.profile presence and prefix + id: precheck if: steps.collect_files.outputs.has_target_files == 'true' shell: bash run: | @@ -156,6 +157,8 @@ jobs: REQUIRED_PREFIX="http://hl7.eu/fhir/" MISSING_PROFILE=() MISSING_REQUIRED_PREFIX=() + : > .ci/precheck-missing-profile.txt + : > .ci/precheck-missing-prefix.txt while IFS= read -r file; do [[ -z "$file" ]] && continue @@ -185,6 +188,7 @@ jobs: if [[ -z "$PROFILE_LINES" ]]; then MISSING_PROFILE+=("$file") + echo "$file" >> .ci/precheck-missing-profile.txt continue fi @@ -199,6 +203,7 @@ jobs: if [[ "$HAS_REQUIRED_PREFIX" != "true" ]]; then MISSING_REQUIRED_PREFIX+=("$file") + echo "$file" >> .ci/precheck-missing-prefix.txt fi done < .ci/target-files.txt @@ -223,13 +228,18 @@ jobs: echo fi - exit 1 + echo "precheck_exit_code=1" >> "$GITHUB_OUTPUT" + echo "missing_profile_count=${#MISSING_PROFILE[@]}" >> "$GITHUB_OUTPUT" + echo "missing_required_prefix_count=${#MISSING_REQUIRED_PREFIX[@]}" >> "$GITHUB_OUTPUT" + else + echo "meta.profile precheck passed for all candidate resources." + echo "precheck_exit_code=0" >> "$GITHUB_OUTPUT" + echo "missing_profile_count=0" >> "$GITHUB_OUTPUT" + echo "missing_required_prefix_count=0" >> "$GITHUB_OUTPUT" fi - echo "meta.profile precheck passed for all candidate resources." - - name: Set up Java - if: steps.collect_files.outputs.has_target_files == 'true' + if: steps.collect_files.outputs.has_target_files == 'true' && steps.precheck.outputs.precheck_exit_code == '0' uses: actions/setup-java@v5 with: distribution: temurin @@ -237,7 +247,7 @@ jobs: - name: Resolve validator download URL id: validator_meta - if: steps.collect_files.outputs.has_target_files == 'true' + if: steps.collect_files.outputs.has_target_files == 'true' && steps.precheck.outputs.precheck_exit_code == '0' shell: bash run: | set -euo pipefail @@ -263,7 +273,7 @@ jobs: echo "cache_key=validator-cli-${CACHE_SUFFIX}" >> "$GITHUB_OUTPUT" - name: Restore cached validator - if: steps.collect_files.outputs.has_target_files == 'true' + if: steps.collect_files.outputs.has_target_files == 'true' && steps.precheck.outputs.precheck_exit_code == '0' id: validator_cache uses: actions/cache@v5 with: @@ -273,7 +283,7 @@ jobs: ${{ runner.os }}-validator-cli- - name: Download validator if cache miss - if: steps.collect_files.outputs.has_target_files == 'true' && steps.validator_cache.outputs.cache-hit != 'true' + if: steps.collect_files.outputs.has_target_files == 'true' && steps.precheck.outputs.precheck_exit_code == '0' && steps.validator_cache.outputs.cache-hit != 'true' shell: bash run: | set -euo pipefail @@ -283,7 +293,7 @@ jobs: - name: Run Java FHIR validator id: run_validator - if: steps.collect_files.outputs.has_target_files == 'true' + if: steps.collect_files.outputs.has_target_files == 'true' && steps.precheck.outputs.precheck_exit_code == '0' shell: bash run: | set -euo pipefail @@ -323,7 +333,7 @@ jobs: - name: Render validation summary id: render_summary - if: steps.collect_files.outputs.has_target_files == 'true' + if: steps.collect_files.outputs.has_target_files == 'true' && steps.precheck.outputs.precheck_exit_code == '0' continue-on-error: false uses: patrick-werner/validation-outcome-markdown-renderer@v1 with: @@ -341,11 +351,87 @@ jobs: printf '%s\n' \ '# FHIR Validation Summary' \ '' \ - 'The markdown summary could not be rendered from `validation.json`.' \ + 'The markdown summary could not be rendered from `validation.json` or validation was skipped due to precheck issues.' \ 'Please check the workflow logs and the uploaded HTML artifact for details.' \ > pr-summary.md fi + - name: Build precheck summary + if: always() && steps.collect_files.outputs.has_target_files == 'true' + shell: bash + run: | + set -euo pipefail + + PRECHECK_EXIT="${{ steps.precheck.outputs.precheck_exit_code }}" + MISSING_PROFILE_COUNT="${{ steps.precheck.outputs.missing_profile_count }}" + MISSING_PREFIX_COUNT="${{ steps.precheck.outputs.missing_required_prefix_count }}" + REQUIRED_PREFIX="http://hl7.eu/fhir/" + MAX_LIST_ITEMS=80 + + if [[ -z "$PRECHECK_EXIT" ]]; then + PRECHECK_EXIT="0" + fi + if [[ -z "$MISSING_PROFILE_COUNT" ]]; then + MISSING_PROFILE_COUNT="0" + fi + if [[ -z "$MISSING_PREFIX_COUNT" ]]; then + MISSING_PREFIX_COUNT="0" + fi + + { + echo "## meta.profile precheck" + echo + if [[ "$PRECHECK_EXIT" == "0" ]]; then + echo "" + echo + echo "✅ Passed: every checked resource contains \`meta.profile\` and has at least one entry starting with \`${REQUIRED_PREFIX}\`." + else + echo "" + echo + echo "❌ Failed." + echo + echo "- Missing \`meta.profile\`: ${MISSING_PROFILE_COUNT}" + echo "- No \`meta.profile\` entry with required prefix \`${REQUIRED_PREFIX}\`: ${MISSING_PREFIX_COUNT}" + echo + + if [[ -s .ci/precheck-missing-profile.txt ]]; then + echo "### Missing \`meta.profile\`" + count=0 + while IFS= read -r file; do + [[ -z "$file" ]] && continue + echo "- \`$file\`" + count=$((count + 1)) + if [[ "$count" -ge "$MAX_LIST_ITEMS" ]]; then + break + fi + done < .ci/precheck-missing-profile.txt + total="$(wc -l < .ci/precheck-missing-profile.txt | tr -d ' ')" + if [[ "$total" -gt "$MAX_LIST_ITEMS" ]]; then + echo "- _...truncated (${MAX_LIST_ITEMS} of ${total})_" + fi + echo + fi + + if [[ -s .ci/precheck-missing-prefix.txt ]]; then + echo "### No required prefix in \`meta.profile\`" + count=0 + while IFS= read -r file; do + [[ -z "$file" ]] && continue + echo "- \`$file\`" + count=$((count + 1)) + if [[ "$count" -ge "$MAX_LIST_ITEMS" ]]; then + break + fi + done < .ci/precheck-missing-prefix.txt + total="$(wc -l < .ci/precheck-missing-prefix.txt | tr -d ' ')" + if [[ "$total" -gt "$MAX_LIST_ITEMS" ]]; then + echo "- _...truncated (${MAX_LIST_ITEMS} of ${total})_" + fi + echo + fi + fi + } > precheck-summary.md + - name: Write PR metadata if: always() shell: bash @@ -376,6 +462,14 @@ jobs: path: pr-summary.md if-no-files-found: warn + - name: Upload precheck summary artifact + if: always() && steps.collect_files.outputs.has_target_files == 'true' + uses: actions/upload-artifact@v7 + with: + name: fhir-validation-precheck-summary + path: precheck-summary.md + if-no-files-found: warn + - name: Upload validation reports (HTML + JSON) id: upload_html_report if: always() && steps.collect_files.outputs.has_target_files == 'true' @@ -393,7 +487,20 @@ jobs: run: | set -euo pipefail + PRECHECK_EXIT="${{ steps.precheck.outputs.precheck_exit_code }}" EXIT_CODE="${{ steps.run_validator.outputs.validator_exit_code }}" + if [[ -z "$PRECHECK_EXIT" ]]; then + PRECHECK_EXIT="0" + fi + if [[ -z "$EXIT_CODE" ]]; then + EXIT_CODE="0" + fi + + if [[ "$PRECHECK_EXIT" != "0" ]]; then + echo "FHIR validation precheck failed (meta.profile requirements not met)." + exit 1 + fi + if [[ "$EXIT_CODE" != "0" ]]; then echo "FHIR validation finished with errors (exit code: $EXIT_CODE)." exit 1 diff --git a/.github/workflows/main-validation-readme.yml b/.github/workflows/main-validation-readme.yml index ec008ee..a2b0420 100644 --- a/.github/workflows/main-validation-readme.yml +++ b/.github/workflows/main-validation-readme.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -195,7 +195,7 @@ jobs: - name: Set up Java if: steps.collect_files.outputs.has_target_files == 'true' - uses: actions/setup-java@v5 + uses: actions/setup-java@v4 with: distribution: temurin java-version: '21' @@ -230,7 +230,7 @@ jobs: - name: Restore cached validator if: steps.collect_files.outputs.has_target_files == 'true' id: validator_cache - uses: actions/cache@v5 + uses: actions/cache@v4 with: path: .cache/fhir-validator/validator_cli.jar key: ${{ runner.os }}-${{ steps.validator_meta.outputs.cache_key }} @@ -543,7 +543,7 @@ jobs: steps: - name: Download main validation artifacts id: download_artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@v4 continue-on-error: true with: name: main-validation-artifacts @@ -605,7 +605,7 @@ jobs: } > .ci-pages/site/index.html - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v5 + uses: actions/upload-pages-artifact@v3 with: path: .ci-pages/site diff --git a/.github/workflows/pr-comment.yml b/.github/workflows/pr-comment.yml index 69d8bc1..5b7cfe2 100644 --- a/.github/workflows/pr-comment.yml +++ b/.github/workflows/pr-comment.yml @@ -69,6 +69,7 @@ jobs: META_ARTIFACT_ID="$(echo "$ARTIFACTS_JSON" | jq -r '.artifacts[] | select(.name == "fhir-validation-pr-meta" and .expired == false) | .id' | head -n 1)" SUMMARY_ARTIFACT_ID="$(echo "$ARTIFACTS_JSON" | jq -r '.artifacts[] | select(.name == "fhir-validation-pr-summary" and .expired == false) | .id' | head -n 1)" + PRECHECK_ARTIFACT_ID="$(echo "$ARTIFACTS_JSON" | jq -r '.artifacts[] | select(.name == "fhir-validation-precheck-summary" and .expired == false) | .id' | head -n 1)" HTML_ARTIFACT_ID="$(echo "$ARTIFACTS_JSON" | jq -r '.artifacts[] | select(.name == "fhir-validation-html-report" and .expired == false) | .id' | head -n 1)" if [[ -z "$META_ARTIFACT_ID" ]]; then @@ -133,6 +134,18 @@ jobs: fi fi + PRECHECK_SUMMARY_FILE="" + if [[ -n "$PRECHECK_ARTIFACT_ID" ]]; then + mkdir -p .ci-comment/source-precheck + curl -fsSL \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/$REPOSITORY/actions/artifacts/$PRECHECK_ARTIFACT_ID/zip" \ + -o .ci-comment/precheck-summary.zip + unzip -qo .ci-comment/precheck-summary.zip -d .ci-comment/source-precheck + PRECHECK_SUMMARY_FILE="$(find .ci-comment/source-precheck -type f -name 'precheck-summary.md' | head -n 1 || true)" + fi + # Prefer full renderer summary first. if [[ -s "$FULL_SUMMARY_FILE" ]]; then cp "$FULL_SUMMARY_FILE" "$COMMENT_FILE" @@ -168,6 +181,7 @@ jobs: echo "html_artifact_url=$HTML_ARTIFACT_URL" >> "$GITHUB_OUTPUT" echo "run_url=$RUN_URL" >> "$GITHUB_OUTPUT" echo "full_summary_file=$FULL_SUMMARY_FILE" >> "$GITHUB_OUTPUT" + echo "precheck_summary_file=$PRECHECK_SUMMARY_FILE" >> "$GITHUB_OUTPUT" echo "has_pr=true" >> "$GITHUB_OUTPUT" echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" @@ -190,6 +204,7 @@ jobs: COMMENT_FILE=".ci-comment/pr-summary.md" FINAL_FILE=".ci-comment/pr-summary-final.md" FULL_FILE="${{ steps.build_comment.outputs.full_summary_file }}" + PRECHECK_FILE="${{ steps.build_comment.outputs.precheck_summary_file }}" FILTERED_RENDERED_FILE=".ci-comment/pr-summary-errors-only-rendered.md" MAX_COMMENT_BYTES=62000 HTML_ARTIFACT_URL="${{ steps.build_comment.outputs.html_artifact_url }}" @@ -226,6 +241,15 @@ jobs: > "$FINAL_FILE" fi + if [[ -n "$PRECHECK_FILE" && -s "$PRECHECK_FILE" ]] && grep -q 'PRECHECK_STATUS:FAILED' "$PRECHECK_FILE"; then + { + cat "$PRECHECK_FILE" + printf '\n---\n\n' + cat "$FINAL_FILE" + } > .ci-comment/pr-summary-with-precheck.md + mv .ci-comment/pr-summary-with-precheck.md "$FINAL_FILE" + fi + printf '\n## Artifacts\n\n[Download `fhir-validation-html-report` (includes `validation.html` and `validation.json`)](%s)\n' "$HTML_ARTIFACT_URL" >> "$FINAL_FILE" printf '\nValidation run: %s\n' "$RUN_URL" >> "$FINAL_FILE"