CI - Structured data validation #11
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: CI - Structured data validation | |
| on: | |
| workflow_call: {} | |
| # Allow nightly runs on `main` so regressions in already-merged content | |
| # don't go undetected for a full release cycle. | |
| schedule: | |
| - cron: '0 8 * * *' | |
| permissions: | |
| contents: read | |
| pull-requests: write # required to post / update the sticky PR comment | |
| concurrency: | |
| group: structured-data-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| # Opt into Node 24 for bundled-JS actions ahead of GitHub's June 16, 2026 cutoff. | |
| # https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/ | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| # The PR-incremental mode needs git history to diff against origin/main. | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| cache: npm | |
| - name: Docusaurus Cache | |
| uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 | |
| with: | |
| path: node_modules/.cache | |
| key: ${{ runner.os }}-docusaurus-${{ hashFiles('**/package-lock.json') }}-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-docusaurus-${{ hashFiles('**/package-lock.json') }}- | |
| ${{ runner.os }}-docusaurus- | |
| - name: Install | |
| run: npm install | |
| - name: Build | |
| run: npm run build | |
| - name: Validate structured data (PR incremental) | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| node scripts/validate-structured-data.mjs \ | |
| --pr origin/${{ github.event.pull_request.base.ref }} \ | |
| --json-summary /tmp/structured-data-summary.json | |
| - name: Validate structured data (full sweep) | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| node scripts/validate-structured-data.mjs \ | |
| --json-summary /tmp/structured-data-summary.json | |
| # Sticky PR comment summarizing errors + warnings, edited in place | |
| # on every push so the PR thread doesn't bloat. Skipped on non-PR runs. | |
| - name: Compose PR comment body | |
| if: github.event_name == 'pull_request' && always() | |
| id: compose | |
| run: | | |
| node - <<'NODE' | |
| const fs = require('fs'); | |
| const path = '/tmp/structured-data-summary.json'; | |
| const out = '/tmp/comment-body.md'; | |
| if (!fs.existsSync(path)) { | |
| fs.writeFileSync(out, '⚠️ Structured-data validator did not run. The build may have failed before validation started — check the workflow logs.\n'); | |
| process.exit(0); | |
| } | |
| const s = JSON.parse(fs.readFileSync(path, 'utf8')); | |
| const mode = s.mode === 'pr' ? `PR-incremental (base: \`${s.base_ref}\`)` : 'full sweep'; | |
| let body = `### 🔎 Structured data validation\n\n`; | |
| body += `- **Mode:** ${mode}\n`; | |
| body += `- **Files checked:** ${s.files_checked}\n`; | |
| body += `- **JSON-LD payloads:** ${s.payloads_checked}\n`; | |
| body += `- **Errors:** ${s.error_count}\n`; | |
| body += `- **Warnings:** ${s.warning_count}\n\n`; | |
| if (s.error_count > 0) { | |
| body += `#### ❌ Errors by kind\n\n`; | |
| body += `| Kind | Count |\n|---|---:|\n`; | |
| for (const e of s.errors_by_kind.slice(0, 15)) { | |
| body += `| \`${e.kind.replace(/\|/g, '\\|')}\` | ${e.count} |\n`; | |
| } | |
| if (s.sample_errors.length > 0) { | |
| body += `\n<details><summary>Sample errors (up to 20)</summary>\n\n`; | |
| for (const e of s.sample_errors) body += `- \`${e}\`\n`; | |
| body += `\n</details>\n\n`; | |
| } | |
| } | |
| if (s.warning_count > 0) { | |
| body += `#### ⚠️ Warnings by kind\n\n`; | |
| body += `| Kind | Count |\n|---|---:|\n`; | |
| for (const w of s.warnings_by_kind.slice(0, 15)) { | |
| body += `| \`${w.kind.replace(/\|/g, '\\|')}\` | ${w.count} |\n`; | |
| } | |
| body += `\n_Warnings don't fail the build — they surface authoring gaps that should be backfilled when convenient (typically missing M12 \`about\`/\`mentions\` entity refs)._\n\n`; | |
| } | |
| if (s.error_count === 0 && s.warning_count === 0) { | |
| body += `✅ All checks pass and no authoring warnings to backfill.\n`; | |
| } | |
| body += `\n<sub>⚙️ Generated by [\`ci_structured_data.yml\`](https://github.com/wasmCloud/wasmcloud.com/blob/main/.github/workflows/ci_structured_data.yml) • [structured-data spike](https://github.com/wasmCloud/community-meetings/blob/main/2026-6-wasmcloud.com-structured-data-spike.md)</sub>\n`; | |
| fs.writeFileSync(out, body); | |
| NODE | |
| - name: Post / update sticky PR comment | |
| # Skip on fork-originated PRs: GitHub issues a read-only GITHUB_TOKEN | |
| # for fork-PR workflows, so marocchino/sticky-pull-request-comment | |
| # fails with "Resource not accessible by integration" — which would | |
| # then red-X the structured-data check even when validation itself | |
| # passed. Same-repo PRs continue to post and edit the sticky comment. | |
| if: | | |
| always() | |
| && github.event_name == 'pull_request' | |
| && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 | |
| with: | |
| header: structured-data-validation | |
| path: /tmp/comment-body.md |