chore(release): version packages #4241
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/CD | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["**"] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: {} | |
| jobs: | |
| detect-changes: | |
| name: "Detect changes" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: read | |
| contents: read | |
| outputs: | |
| webapp: ${{ steps.filter.outputs.webapp }} | |
| webapp-image: ${{ steps.filter.outputs.webapp-image == 'true' || steps.webapp_image_source.outputs.webapp-image-source == 'true' }} | |
| application-server: ${{ steps.filter.outputs.application-server }} | |
| application-server-image: ${{ steps.filter.outputs.application-server-image }} | |
| e2e: ${{ steps.filter.outputs.e2e }} | |
| agent-images: ${{ steps.filter.outputs.agent-images }} | |
| postgres-image: ${{ steps.filter.outputs.postgres-image }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| ci-config: ${{ steps.filter.outputs.ci-config }} | |
| docker-config: ${{ steps.filter.outputs.docker-config }} | |
| pmd-canary: ${{ steps.filter.outputs.pmd-canary }} | |
| any-code: ${{ steps.filter.outputs.webapp == 'true' || steps.filter.outputs.application-server == 'true' || steps.filter.outputs.agent-images == 'true' }} | |
| should_skip: ${{ steps.skip_check.outputs.should_skip }} | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - id: skip_check | |
| uses: fkirc/skip-duplicate-actions@b974a9395958c231af965b70070979a577efa578 # v5.3.2 | |
| with: | |
| do_not_skip: '["workflow_dispatch", "push", "merge_group"]' | |
| - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 | |
| id: filter | |
| with: | |
| filters: | | |
| webapp: | |
| - 'webapp/**' | |
| - 'docs/images/readme/**' | |
| - '.oxlintrc.json' | |
| - '.oxfmtrc.json' | |
| # check:components, check:stories and check:story-sort live here, run on the Webapp leg. | |
| - 'scripts/**' | |
| - 'package.json' | |
| - 'bun.lock' | |
| - 'bunfig.toml' | |
| webapp-image: | |
| - 'webapp/public/**' | |
| - 'webapp/docker/**' | |
| - 'webapp/Dockerfile' | |
| - 'webapp/index.html' | |
| - 'webapp/package.json' | |
| - 'webapp/tsconfig.json' | |
| - 'webapp/vite.config.ts' | |
| - 'webapp/vite.shared.ts' | |
| - 'docs/package.json' | |
| - 'package.json' | |
| - 'bun.lock' | |
| - 'bunfig.toml' | |
| application-server: | |
| - 'server/**' | |
| # Repository tooling runs on the App Server or Database leg. | |
| - 'scripts/**' | |
| # test:agents and typecheck:agents cover the precompute tree. | |
| - 'docker/agents/**' | |
| - 'tsconfig.agents.json' | |
| - '.oxlintrc.json' | |
| - '.oxfmtrc.json' | |
| - 'tsconfig.json' | |
| - 'package.json' | |
| - 'bun.lock' | |
| # Linted by the same leg (docs/.oxlintrc.json decides that verdict), markdown-linted by | |
| # docs:lint, and parsed by check:diagrams. The `docs` filter below feeds no job, so this | |
| # entry is the only thing that puts a docs-only PR into `any-code`. | |
| - 'docs/**' | |
| - '.changeset/**' | |
| - 'commitlint.config.ts' | |
| - '.github/scripts/**' | |
| # check:env compares application.yml against the deployment that has to deliver it. | |
| - 'docker/compose.app.yaml' | |
| - 'docker/compose.core.yaml' | |
| # check:instructions runs on this leg and reads every one of these. The two markdown | |
| # globs are named even though webapp/** and server/** already carry them, because | |
| # webapp/** is behind the *webapp* filter — a PR touching only webapp/CLAUDE.md would | |
| # otherwise run the leg that does not hold this gate. | |
| - '**/AGENTS.md' | |
| - '**/CLAUDE.md' | |
| - '.claude/**' | |
| - '.opencode/**' | |
| - '.agents/**' | |
| - 'opencode.json' | |
| - 'renovate.json' | |
| - 'openapitools.json' | |
| - 'jean.json' | |
| - 'project.code-workspace' | |
| - '.vscode/settings.json' | |
| application-server-image: | |
| - 'server/application/src/main/**' | |
| - 'server/application/pom.xml' | |
| - 'server/generated-clients/**' | |
| - 'server/pom.xml' | |
| - 'server/mvnw' | |
| - 'server/.mvn/**' | |
| e2e: | |
| - 'webapp/e2e/**' | |
| - 'webapp/src/**' | |
| - 'webapp/public/**' | |
| - 'webapp/index.html' | |
| - 'webapp/package.json' | |
| - 'webapp/playwright.config.ts' | |
| - 'server/application/src/main/**' | |
| - 'server/application/pom.xml' | |
| - 'server/generated-clients/**' | |
| - 'server/pom.xml' | |
| - 'server/compose.yaml' | |
| - 'docker/postgres/**' | |
| - 'package.json' | |
| - 'bun.lock' | |
| - 'bunfig.toml' | |
| - '.github/workflows/cicd.yml' | |
| - '.github/workflows/ci-tests.yml' | |
| - '.github/actions/setup-caches/**' | |
| - '.github/actions/setup-bun/**' | |
| agent-images: | |
| - 'docker/agents/**' | |
| postgres-image: | |
| - 'docker/postgres/**' | |
| docs: | |
| - 'docs/**' | |
| ci-config: | |
| - '.github/workflows/**' | |
| - '.github/actions/**' | |
| - '.github/actionlint.yaml' | |
| docker-config: | |
| - '.github/workflows/ci-docker-build.yml' | |
| - '.github/workflows/reusable-docker-build.yml' | |
| pmd-canary: | |
| - 'server/pmd-ruleset.xml' | |
| - 'server/pom.xml' | |
| - 'package.json' | |
| - 'scripts/run-mvnw.ts' | |
| - '.github/workflows/ci-quality-gates.yml' | |
| - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 | |
| id: webapp_image_source | |
| with: | |
| filters: | | |
| webapp-image-source: | |
| - 'webapp/src/**' | |
| - '!webapp/src/**/*.test.*' | |
| - '!webapp/src/**/*.stories.*' | |
| - '!webapp/src/test/**' | |
| workflow-lint: | |
| name: "Workflow syntax" | |
| runs-on: ubuntu-latest | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| permissions: | |
| checks: write | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: reviewdog/action-actionlint@dbe5299849118fd6f099ba563d263d770955a64a # v1.73.2 | |
| with: | |
| actionlint_flags: -shellcheck= | |
| fail_level: error | |
| filter_mode: nofilter | |
| reporter: github-check | |
| Quality: | |
| uses: ./.github/workflows/ci-quality-gates.yml | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| secrets: inherit | |
| permissions: | |
| checks: write | |
| contents: read | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| webapp_changed: ${{ (needs.detect-changes.outputs.webapp == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| application_server_changed: ${{ (needs.detect-changes.outputs.application-server == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| pmd_canary: ${{ (needs.detect-changes.outputs.pmd-canary == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| Security: | |
| uses: ./.github/workflows/ci-security-scan.yml | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| secrets: inherit | |
| permissions: | |
| contents: read | |
| security-events: write | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| Test: | |
| uses: ./.github/workflows/ci-tests.yml | |
| needs: [detect-changes] | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' || | |
| github.event_name != 'pull_request' | |
| ) | |
| secrets: inherit | |
| permissions: | |
| contents: read | |
| checks: write | |
| pull-requests: write | |
| statuses: write | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| webapp_changed: ${{ (needs.detect-changes.outputs.webapp == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| application_server_changed: ${{ (needs.detect-changes.outputs.application-server == 'true' || needs.detect-changes.outputs.ci-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| e2e_changed: ${{ (needs.detect-changes.outputs.e2e == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| Changesets: | |
| if: github.event_name == 'pull_request' | |
| uses: ./.github/workflows/verify-changesets.yml | |
| permissions: | |
| contents: read | |
| Docker: | |
| uses: ./.github/workflows/ci-docker-build.yml | |
| # Image builds consume the detected source tree, not Quality outputs. Running both branches at | |
| # once keeps image and preview confidence without adding Docker as a second CI stage. | |
| needs: [detect-changes] | |
| # A preview runs the images CI published for its exact head commit, so a same-repository pull | |
| # request needs commit-addressed tags even when it touches only docs — `Tag unchanged images` | |
| # re-tags rather than rebuilds. The path filters therefore gate fork pull requests only; forks | |
| # never receive a preview. | |
| if: | | |
| needs.detect-changes.outputs.should_skip != 'true' && ( | |
| github.event_name != 'pull_request' || | |
| github.event.pull_request.head.repo.full_name == github.repository || | |
| needs.detect-changes.outputs.any-code == 'true' || | |
| needs.detect-changes.outputs.ci-config == 'true' | |
| ) | |
| secrets: inherit | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| attestations: write | |
| with: | |
| should_skip: ${{ needs.detect-changes.outputs.should_skip }} | |
| webapp_changed: ${{ (needs.detect-changes.outputs.webapp-image == 'true' || needs.detect-changes.outputs.docker-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| application_server_changed: ${{ (needs.detect-changes.outputs.application-server-image == 'true' || needs.detect-changes.outputs.docker-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| agent_images_changed: ${{ (needs.detect-changes.outputs.agent-images == 'true' || needs.detect-changes.outputs.docker-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| postgres_image_changed: ${{ (needs.detect-changes.outputs.postgres-image == 'true' || needs.detect-changes.outputs.docker-config == 'true' || github.event_name != 'pull_request') && 'true' || 'false' }} | |
| all-ci-passed: | |
| name: "CI Status Gate" | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| statuses: write | |
| needs: [detect-changes, workflow-lint, Quality, Security, Test, Changesets, Docker] | |
| if: always() | |
| steps: | |
| - name: Generate workflow timeline | |
| uses: Kesin11/actions-timeline@7c7e0821d38f27460f4a71ef874a1c8cf23602e4 # v2 | |
| with: | |
| show-waiting-runner: true | |
| - name: Summarize workflow performance | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const run = await github.rest.actions.getWorkflowRun({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.runId, | |
| }); | |
| const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.runId, | |
| per_page: 100, | |
| filter: 'latest', | |
| }); | |
| const finished = jobs.filter((job) => job.started_at && job.completed_at); | |
| const seconds = (start, end) => (Date.parse(end) - Date.parse(start)) / 1000; | |
| const elapsed = seconds(run.data.created_at, new Date().toISOString()); | |
| const runnerSeconds = finished.reduce( | |
| (total, job) => total + seconds(job.started_at, job.completed_at), | |
| 0, | |
| ); | |
| const startDelays = finished.map((job) => seconds(job.created_at, job.started_at)); | |
| const format = (value) => `${Math.floor(value / 60)}m ${Math.round(value % 60)}s`; | |
| await core.summary | |
| .addHeading('Workflow performance', 3) | |
| .addTable([ | |
| [{ data: 'Metric', header: true }, { data: 'Value', header: true }], | |
| ['Workflow-to-gate sample', format(elapsed)], | |
| ['Completed runner time', format(runnerSeconds)], | |
| ['Longest job creation-to-start delay', format(Math.max(0, ...startDelays))], | |
| ]) | |
| .addRaw( | |
| 'The creation-to-start delay includes dependency waiting; use the timeline above to distinguish dependency time from runner waiting. Cache hits and misses remain visible in each native cache action log.', | |
| ) | |
| .write(); | |
| - name: Evaluate CI results | |
| id: evaluate | |
| run: | | |
| echo "detect-changes: ${{ needs.detect-changes.result }}" | |
| echo "workflow-lint: ${{ needs.workflow-lint.result }}" | |
| echo "Quality: ${{ needs.Quality.result }}" | |
| echo "Security: ${{ needs.Security.result }}" | |
| echo "Test: ${{ needs.Test.result }}" | |
| echo "Changesets: ${{ needs.Changesets.result }}" | |
| echo "Docker: ${{ needs.Docker.result }}" | |
| if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then | |
| echo "status=failure" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| if [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then | |
| echo "status=cancelled" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| echo "status=success" >> $GITHUB_OUTPUT | |
| - name: Generate CI Summary | |
| if: always() | |
| run: | | |
| echo "## 🔍 CI Pipeline Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ "${{ steps.evaluate.outputs.status }}" == "success" ]]; then | |
| echo "✅ **All checks passed!**" >> $GITHUB_STEP_SUMMARY | |
| elif [[ "${{ steps.evaluate.outputs.status }}" == "failure" ]]; then | |
| echo "❌ **Some checks failed.** See details below." >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ **CI was cancelled or encountered an issue.**" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Workflow Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Workflow | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY | |
| result_to_emoji() { | |
| case "$1" in | |
| success) echo "✅ Passed" ;; | |
| failure) echo "❌ Failed" ;; | |
| skipped) echo "⏭️ Skipped" ;; | |
| cancelled) echo "🚫 Cancelled" ;; | |
| *) echo "❓ Unknown" ;; | |
| esac | |
| } | |
| echo "| Workflow syntax | $(result_to_emoji '${{ needs.workflow-lint.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Quality | $(result_to_emoji '${{ needs.Quality.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Test | $(result_to_emoji '${{ needs.Test.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Changesets | $(result_to_emoji '${{ needs.Changesets.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Security | $(result_to_emoji '${{ needs.Security.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Docker | $(result_to_emoji '${{ needs.Docker.result }}') |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📁 Components Changed" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Component | Changed |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-----------|---------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webapp | ${{ needs.detect-changes.outputs.webapp == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Application Server | ${{ needs.detect-changes.outputs.application-server == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Agent Images | ${{ needs.detect-changes.outputs.agent-images == 'true' && '✅ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| CI Config | ${{ needs.detect-changes.outputs.ci-config == 'true' && '⚙️ Yes' || '➖ No' }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ "${{ steps.evaluate.outputs.status }}" == "failure" ]]; then | |
| echo "### 💡 Troubleshooting Guide" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ "${{ needs.Quality.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Quality Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Issue | Fix Command |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|-------------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Formatting errors | \`bun run format\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| TypeScript errors | \`bun run typecheck\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Lint errors (webapp) | \`bun run check:webapp:fix\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Lint errors (outside webapp) | \`bun run check:agents:fix\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Java formatting | \`bun run format:java\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Java lint (PMD) | \`bun run lint:java:report\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Storybook sidebar order | \`bun run check:story-sort\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Mermaid diagram parse errors | \`bun run check:diagrams\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Stale \`routeTree.gen.ts\` | \`cd webapp && bun run build\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| OpenAPI out of sync | \`bun run generate:api\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Database schema drift | \`bun run db:draft-changelog\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [[ "${{ needs.Test.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Tests Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Test Suite | Run Locally |" >> $GITHUB_STEP_SUMMARY | |
| echo "|------------|-------------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webapp unit | \`bun run test:webapp\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Webapp Storybook | \`bun run --filter webapp test:storybook\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Application server | \`cd server && ./mvnw test\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Tip:** Check the **Test Results** tab above for specific failures." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [[ "${{ needs.Changesets.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Changesets Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Run \`bun run check:changesets\` for policy-test failures; otherwise correct the changeset reported by the job." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [[ "${{ needs.Docker.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Docker Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Common causes:" >> $GITHUB_STEP_SUMMARY | |
| echo "- Build errors in the application code" >> $GITHUB_STEP_SUMMARY | |
| echo "- Missing dependencies" >> $GITHUB_STEP_SUMMARY | |
| echo "- Dockerfile syntax errors" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Test locally: \`docker build -f <component>/Dockerfile .\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [[ "${{ needs.Security.result }}" == "failure" ]]; then | |
| echo "<details><summary><b>❌ Security Failed</b></summary>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Check the **Security** tab for details on vulnerabilities." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "</details>" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Quick fix:** Run \`bun run format && bun run check\` before pushing." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [[ "${{ steps.evaluate.outputs.status }}" == "success" ]]; then | |
| echo "### ⚡ Performance" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| SKIPPED_COUNT=0 | |
| [[ "${{ needs.Quality.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| [[ "${{ needs.Test.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| [[ "${{ needs.Security.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| [[ "${{ needs.Docker.result }}" == "skipped" ]] && SKIPPED_COUNT=$((SKIPPED_COUNT + 1)) || true | |
| if [[ $SKIPPED_COUNT -gt 0 ]]; then | |
| echo "🚀 **Path-based filtering saved time!** $SKIPPED_COUNT workflow(s) skipped because no relevant files changed." >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "All workflows ran (CI config or main branch push)." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "*Generated by CI Status Gate • [View workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})*" >> $GITHUB_STEP_SUMMARY | |
| - name: Create commit status | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const sha = context.payload.pull_request?.head?.sha || context.sha; | |
| const outcome = '${{ steps.evaluate.outputs.status }}' || 'failure'; | |
| // createCommitStatus accepts error, failure, pending or success and 422s on anything | |
| // else, so a cancelled run has to be reported as one of them rather than by its name. | |
| const REPORTED = { | |
| success: ['success', 'All CI checks passed'], | |
| failure: ['failure', 'One or more CI checks failed'], | |
| cancelled: ['error', 'CI was cancelled before it finished'], | |
| }; | |
| const [state, description] = REPORTED[outcome] ?? REPORTED.failure; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: sha, | |
| state: state, | |
| description: description, | |
| context: 'All CI Passed' | |
| }); |