name: Build and Test Workflow # Top-level PR / merge-queue gate. Detects which paths changed and dispatches # to the dedicated reusable workflows under .github/workflows/. Each child # workflow keeps its own setup/teardown so this file stays a routing layer. # # The final `all-checks-passed` job is the single status check that branch # protection should require — it succeeds only if every required upstream # job either succeeded or was legitimately skipped by its path filter. on: pull_request: branches: ["main"] merge_group: branches: ["main"] workflow_dispatch: # cancel in-progress jobs if a new job is triggered # This is useful to avoid running multiple builds for the same branch if a new commit is pushed # or a pull request is updated. # It helps to save resources and time by ensuring that only the latest commit is built and tested # This is particularly useful for long-running jobs that may take a while to complete. # The `group` is set to a combination of the workflow name, event name, and branch name. # This ensures that jobs are grouped by the workflow and branch, allowing for cancellation of # in-progress jobs when a new commit is pushed to the same branch or a new pull request is opened. concurrency: group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref_name || github.ref }} cancel-in-progress: true permissions: contents: read jobs: files-changed: name: detect what files changed runs-on: ubuntu-latest timeout-minutes: 3 outputs: build: ${{ steps.changes.outputs.build }} project: ${{ steps.changes.outputs.project }} openapi: ${{ steps.changes.outputs.openapi }} frontend: ${{ steps.changes.outputs.frontend }} docker-base: ${{ steps.changes.outputs.docker-base }} tauri: ${{ steps.changes.outputs.tauri }} engine: ${{ steps.changes.outputs.engine }} proprietary: ${{ steps.changes.outputs.proprietary }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3 with: egress-policy: audit - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check for file changes uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 id: changes with: filters: .github/config/.files.yaml build: needs: [files-changed] permissions: actions: read contents: read security-events: write pull-requests: write uses: ./.github/workflows/backend-build.yml secrets: inherit db-migration-test: # Boots the current bootJar against H2 fixtures captured from past # releases (v2.0.0 / v2.5.0 / v2.10.0) and verifies admin login still # works after Hibernate's ddl-auto=update migrates the schema. Gated on # the `project` filter so doc-only PRs skip this ~5-minute job. if: needs.files-changed.outputs.project == 'true' needs: [files-changed] permissions: contents: read uses: ./.github/workflows/db-migration-test.yml secrets: inherit check-generateOpenApiDocs: if: needs.files-changed.outputs.openapi == 'true' needs: [files-changed] permissions: contents: read uses: ./.github/workflows/check-openapi.yml secrets: inherit frontend-validation: if: needs.files-changed.outputs.frontend == 'true' needs: [files-changed] permissions: contents: read pull-requests: write uses: ./.github/workflows/frontend-validation.yml secrets: inherit playwright-e2e: if: needs.files-changed.outputs.frontend == 'true' needs: [files-changed] permissions: contents: read uses: ./.github/workflows/e2e-stubbed.yml secrets: inherit playwright-e2e-live: if: needs.files-changed.outputs.frontend == 'true' needs: [files-changed] permissions: contents: read uses: ./.github/workflows/e2e-live.yml secrets: inherit playwright-e2e-enterprise: if: needs.files-changed.outputs.proprietary == 'true' needs: [files-changed] permissions: contents: read uses: ./.github/workflows/build-enterprise.yml secrets: inherit check-licence: if: needs.files-changed.outputs.build == 'true' needs: [files-changed, build] permissions: contents: read uses: ./.github/workflows/check-licence.yml secrets: inherit docker-compose-tests: if: needs.files-changed.outputs.project == 'true' needs: [files-changed] permissions: actions: write contents: read checks: write uses: ./.github/workflows/docker-compose-tests.yml secrets: inherit with: docker-base-changed: ${{ needs.files-changed.outputs.docker-base }} test-build-docker-images: if: github.event_name == 'pull_request' && needs.files-changed.outputs.project == 'true' needs: [files-changed, build, check-generateOpenApiDocs, check-licence] permissions: contents: read packages: read id-token: write uses: ./.github/workflows/test-build-docker.yml secrets: inherit with: docker-base-changed: ${{ needs.files-changed.outputs.docker-base }} tauri-build: if: needs.files-changed.outputs.tauri == 'true' needs: [files-changed] permissions: contents: read pull-requests: write uses: ./.github/workflows/tauri-build.yml secrets: inherit ai-engine: if: needs.files-changed.outputs.engine == 'true' needs: [files-changed] permissions: contents: read pull-requests: write uses: ./.github/workflows/ai-engine.yml secrets: inherit pre-commit: needs: [files-changed] permissions: contents: read uses: ./.github/workflows/pre_commit.yml secrets: inherit dependency-review: needs: [files-changed] permissions: contents: read uses: ./.github/workflows/dependency-review.yml secrets: inherit # Coverage aggregate: merges the JUnit + e2e:live + cucumber .exec # artifacts produced by the jobs above into one report, plus pulls # in vitest + Playwright frontend coverage for the per-area matrix. # `if: always()` so a producer failing partway still gets credit # for whatever did record. Advisory only - intentionally NOT in # all-checks-passed, so a flaky aggregate run never blocks merging. coverage-aggregate: if: always() needs: - build - playwright-e2e-live - docker-compose-tests - frontend-validation permissions: contents: read uses: ./.github/workflows/coverage-aggregate.yml secrets: inherit # Single status check that branch protection should mark as required. # Succeeds when every upstream job is either `success` or `skipped` (path- # gated jobs that didn't apply this run). Any `failure` or `cancelled` # result fails the gate. `if: always()` ensures the gate evaluates even # when an upstream job fails. all-checks-passed: name: All checks passed if: always() needs: - files-changed - build - db-migration-test - check-generateOpenApiDocs - frontend-validation - playwright-e2e - playwright-e2e-live - playwright-e2e-enterprise - check-licence - docker-compose-tests - test-build-docker-images - tauri-build - ai-engine - pre-commit - dependency-review runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2.19.3 with: egress-policy: audit - name: Verify every required job passed (or was legitimately skipped) env: RESULTS: | files-changed=${{ needs.files-changed.result }} build=${{ needs.build.result }} db-migration-test=${{ needs.db-migration-test.result }} check-generateOpenApiDocs=${{ needs.check-generateOpenApiDocs.result }} frontend-validation=${{ needs.frontend-validation.result }} playwright-e2e=${{ needs.playwright-e2e.result }} playwright-e2e-live=${{ needs.playwright-e2e-live.result }} playwright-e2e-enterprise=${{ needs.playwright-e2e-enterprise.result }} check-licence=${{ needs.check-licence.result }} docker-compose-tests=${{ needs.docker-compose-tests.result }} test-build-docker-images=${{ needs.test-build-docker-images.result }} tauri-build=${{ needs.tauri-build.result }} ai-engine=${{ needs.ai-engine.result }} pre-commit=${{ needs.pre-commit.result }} dependency-review=${{ needs.dependency-review.result }} run: | ok=true while IFS='=' read -r name result; do [ -z "$name" ] && continue case "$result" in success|skipped) printf ' %-30s %s\n' "$name" "$result" ;; *) printf '✗ %-30s %s\n' "$name" "$result"; ok=false ;; esac done <<< "$RESULTS" if [ "$ok" != "true" ]; then echo "" echo "One or more required checks failed or were cancelled." exit 1 fi echo "" echo "All required checks passed."