refactor(frontend): analyze PDFs with PDFium instead of pdf.js in FileAnalyzer #15749
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
| name: Build and Test Workflow | ||
|
Check failure on line 1 in .github/workflows/build.yml
|
||
| # 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@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 | ||
| 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 | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| jdk-version: [21, 25] | ||
| spring-security: [true, false] | ||
| steps: | ||
| - name: Harden Runner | ||
| uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 | ||
| with: | ||
| egress-policy: audit | ||
| - name: Checkout repository | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| - name: Set up JDK ${{ matrix.jdk-version }} | ||
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 | ||
| with: | ||
| java-version: ${{ matrix.jdk-version }} | ||
| distribution: "temurin" | ||
| - name: Cache Gradle dependency artifacts | ||
| uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 | ||
| with: | ||
| path: | | ||
| ~/.gradle/wrapper | ||
| ~/.gradle/caches/modules-2/files-2.1 | ||
| ~/.gradle/caches/modules-2/metadata-2.* | ||
| key: gradle-deps-${{ runner.os }}-jdk-${{ matrix.jdk-version }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/*.gradle', '**/*.gradle.kts', 'settings.gradle', 'settings.gradle.kts', 'gradle/libs.versions.toml') }} | ||
| - name: Setup Gradle | ||
| uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 | ||
| with: | ||
| gradle-version: 9.3.1 | ||
| cache-disabled: true | ||
| - name: Install Task | ||
| uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0 | ||
| - name: Check Java formatting (Spotless) | ||
| if: matrix.jdk-version == 25 && matrix.spring-security == false | ||
| id: spotless-check | ||
| run: task backend:format:check | ||
| continue-on-error: true | ||
| env: | ||
| MAVEN_USER: ${{ secrets.MAVEN_USER }} | ||
| MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} | ||
| MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }} | ||
| - name: Comment on Java formatting failure | ||
| # Only post a comment on PRs. github-script's PR helpers need an | ||
| # issue/PR number, which doesn't exist on merge_group runs. | ||
| if: steps.spotless-check.outcome == 'failure' && github.event_name == 'pull_request' | ||
| continue-on-error: true | ||
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 | ||
| with: | ||
| script: | | ||
| const marker = '<!-- java-formatting-check -->'; | ||
| const body = [ | ||
| marker, | ||
| '### Java Formatting Check Failed', | ||
| '', | ||
| 'Your code has formatting issues. Run the following command to fix them:', | ||
| '', | ||
| '```bash', | ||
| 'task backend:format', | ||
| '```', | ||
| '', | ||
| 'Then commit and push the changes.', | ||
| ].join('\n'); | ||
| const { data: comments } = await github.rest.issues.listComments({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| }); | ||
| const existing = comments.find(c => c.body.includes(marker)); | ||
| if (existing) { | ||
| await github.rest.issues.updateComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| comment_id: existing.id, | ||
| body, | ||
| }); | ||
| } else { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body, | ||
| }); | ||
| } | ||
| - name: Fail if Java formatting issues found | ||
| if: steps.spotless-check.outcome == 'failure' | ||
| run: | | ||
| echo "============================================" | ||
| echo " Java Formatting Check Failed" | ||
| echo "============================================" | ||
| echo "" | ||
| echo "Your code has formatting issues." | ||
| echo "Run the following command to fix them:" | ||
| echo "" | ||
| echo " task backend:format" | ||
| echo "" | ||
| echo "Then commit and push the changes." | ||
| echo "============================================" | ||
| exit 1 | ||
| - name: Build with Gradle and spring security ${{ matrix.spring-security }} | ||
| run: task backend:build:ci | ||
| env: | ||
| MAVEN_USER: ${{ secrets.MAVEN_USER }} | ||
| MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} | ||
| MAVEN_PUBLIC_URL: ${{ secrets.MAVEN_PUBLIC_URL }} | ||
| DISABLE_ADDITIONAL_FEATURES: ${{ matrix.spring-security }} | ||
| - name: Check Test Reports Exist | ||
| if: success() | ||
| run: | | ||
| declare -a dirs=( | ||
| "app/core/build/reports/tests/" | ||
| "app/core/build/test-results/" | ||
| "app/common/build/reports/tests/" | ||
| "app/common/build/test-results/" | ||
| "app/proprietary/build/reports/tests/" | ||
| "app/proprietary/build/test-results/" | ||
| ) | ||
| for dir in "${dirs[@]}"; do | ||
| if [ ! -d "$dir" ]; then | ||
| echo "Missing $dir" | ||
| exit 1 | ||
| fi | ||
| done | ||
| - name: Upload Test Reports | ||
| if: always() | ||
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | ||
| with: | ||
| name: test-reports-jdk-${{ matrix.jdk-version }}-spring-security-${{ matrix.spring-security }} | ||
| path: | | ||
| app/**/build/reports/jacoco/test | ||
| app/**/build/reports/tests/ | ||
| app/**/build/test-results/ | ||
| app/**/build/reports/problems/ | ||
| build/reports/problems/ | ||
| retention-days: 3 | ||
| if-no-files-found: warn | ||
| - name: Add coverage to PR with spring security ${{ matrix.spring-security }} and JDK ${{ matrix.jdk-version }} | ||
| # The action only supports the pull_request event (it posts a PR comment), | ||
| # so skip it for merge_group runs and workflow_dispatch. | ||
| if: github.event_name == 'pull_request' | ||
| id: jacoco | ||
| uses: madrapps/jacoco-report@50d3aff4548aa991e6753342d9ba291084e63848 # v1.7.2 | ||
| with: | ||
| paths: | | ||
| ${{ github.workspace }}/**/build/reports/jacoco/test/jacocoTestReport.xml | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| min-coverage-overall: 10 | ||
| min-coverage-changed-files: 0 | ||
| comment-type: summary | ||
| uses: ./.github/workflows/backend-build.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 | ||
| 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 | ||
| # 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 | ||
| - 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: Verify every required job passed (or was legitimately skipped) | ||
| env: | ||
| RESULTS: | | ||
| files-changed=${{ needs.files-changed.result }} | ||
| build=${{ needs.build.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." | ||