-
Notifications
You must be signed in to change notification settings - Fork 109
Ci fast fail on job failure #5759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,13 +7,116 @@ on: | |
| jobs: | ||
| # NOTE: For every job added here, add a matching cancel-if-<name>-failed | ||
| # sentinel job in the cancel-if-* section below. | ||
|
|
||
| # Checks whether all build artifacts already exist in a prior run for this | ||
| # commit. If so, downstream build jobs are skipped and tests download | ||
| # artifacts from that prior run instead of the current one. | ||
| # NOTE: No cancel sentinel is needed for this job — it is designed to always | ||
| # succeed. Individual steps use continue-on-error and the final 'result' | ||
| # step runs unconditionally, so the job never leaves the workflow in a | ||
| # failed state. | ||
| check-prior-build: | ||
| name: Check for Prior Build Artifacts | ||
| runs-on: ubuntu-latest-amd64 | ||
| permissions: | ||
| actions: read | ||
| # Outputs are set by the final 'result' step so this job always succeeds | ||
| # and never blocks downstream jobs even if individual checks fail. | ||
| outputs: | ||
| artifacts_run_id: ${{ steps.result.outputs.artifacts_run_id }} | ||
| skip_docker: ${{ steps.result.outputs.skip_docker }} | ||
| steps: | ||
| - name: Find prior run with all build artifacts for this commit | ||
| id: find | ||
| continue-on-error: true | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| set +e | ||
| rust_java_required=( | ||
| "fda-x86_64-unknown-linux-gnu" | ||
| "fda-aarch64-unknown-linux-gnu" | ||
| "pipeline-manager-x86_64-unknown-linux-gnu" | ||
| "pipeline-manager-aarch64-unknown-linux-gnu" | ||
| "feldera-test-binaries-x86_64-unknown-linux-gnu" | ||
| "feldera-test-binaries-aarch64-unknown-linux-gnu" | ||
| "feldera-sql-compiler" | ||
| ) | ||
| # This artifact is uploaded by merge-manifests only after the image is | ||
| # verified in GHCR, so its presence guarantees the sha-{sha} tag exists. | ||
| docker_required=( | ||
| "docker-image-ready" | ||
| ) | ||
|
|
||
| run_ids=$(curl -fsSL \ | ||
| -H "Authorization: Bearer $GH_TOKEN" \ | ||
| -H "Accept: application/vnd.github+json" \ | ||
| "https://api.github.com/repos/${{ github.repository }}/actions/workflows/ci.yml/runs?head_sha=${{ github.sha }}&per_page=20" \ | ||
| | jq -r --argjson cur '${{ github.run_id }}' \ | ||
| '.workflow_runs[] | select(.id != $cur) | .id') || true | ||
|
|
||
| for run_id in $run_ids; do | ||
| names=$(curl -fsSL \ | ||
| -H "Authorization: Bearer $GH_TOKEN" \ | ||
| -H "Accept: application/vnd.github+json" \ | ||
| "https://api.github.com/repos/${{ github.repository }}/actions/runs/${run_id}/artifacts?per_page=200" \ | ||
| | jq -r '.artifacts[] | select(.expired == false) | .name') || continue | ||
|
|
||
| all_rust_java=true | ||
| for artifact in "${rust_java_required[@]}"; do | ||
| if ! printf '%s\n' $names | grep -qx "$artifact"; then | ||
| all_rust_java=false | ||
| break | ||
| fi | ||
| done | ||
|
|
||
| if $all_rust_java; then | ||
| echo "Found all Rust/Java build artifacts in prior run $run_id" | ||
| echo "run_id=$run_id" >> "$GITHUB_OUTPUT" | ||
|
|
||
| all_docker=true | ||
| for artifact in "${docker_required[@]}"; do | ||
| if ! printf '%s\n' $names | grep -qx "$artifact"; then | ||
| all_docker=false | ||
| break | ||
| fi | ||
| done | ||
|
|
||
| if $all_docker; then | ||
| echo "Docker digest artifacts also found, Docker build will be skipped" | ||
| echo "skip_docker=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "Docker digest artifacts not found, Docker build will run" | ||
| echo "skip_docker=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| exit 0 | ||
| fi | ||
| done | ||
|
|
||
| echo "No prior run found with all build artifacts, builds will run" | ||
| echo "run_id=" >> "$GITHUB_OUTPUT" | ||
| echo "skip_docker=false" >> "$GITHUB_OUTPUT" | ||
|
|
||
| # Consolidates outputs so both are always set even if the find step failed. | ||
| # Defaults: artifacts_run_id='' (run builds), skip_docker=false (run Docker build). | ||
| - name: Consolidate outputs | ||
| id: result | ||
| if: always() | ||
| run: | | ||
| echo "artifacts_run_id=${{ steps.find.outputs.run_id }}" >> "$GITHUB_OUTPUT" | ||
| echo "skip_docker=${{ steps.find.outputs.skip_docker || 'false' }}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| invoke-build-rust: | ||
| name: Build Rust | ||
| needs: [check-prior-build] | ||
| if: needs.check-prior-build.outputs.artifacts_run_id == '' | ||
| uses: ./.github/workflows/build-rust.yml | ||
| secrets: inherit | ||
|
|
||
| invoke-build-java: | ||
| name: Build Java | ||
| needs: [check-prior-build] | ||
| if: needs.check-prior-build.outputs.artifacts_run_id == '' | ||
| uses: ./.github/workflows/build-java.yml | ||
| secrets: inherit | ||
|
|
||
|
|
@@ -24,49 +127,84 @@ jobs: | |
|
|
||
| invoke-tests-unit: | ||
| name: Unit Tests | ||
| needs: [invoke-build-rust, invoke-build-java] | ||
| needs: [check-prior-build, invoke-build-rust, invoke-build-java] | ||
| if: | | ||
| always() && | ||
| (needs.invoke-build-rust.result == 'success' || needs.invoke-build-rust.result == 'skipped') && | ||
| (needs.invoke-build-java.result == 'success' || needs.invoke-build-java.result == 'skipped') | ||
| uses: ./.github/workflows/test-unit.yml | ||
| with: | ||
| artifacts_run_id: ${{ needs.check-prior-build.outputs.artifacts_run_id }} | ||
| secrets: inherit | ||
|
|
||
| invoke-tests-adapter: | ||
| name: Adapter Tests | ||
| needs: [invoke-build-rust] | ||
| needs: [check-prior-build, invoke-build-rust] | ||
| if: | | ||
| always() && | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same q as above |
||
| (needs.invoke-build-rust.result == 'success' || needs.invoke-build-rust.result == 'skipped') | ||
| uses: ./.github/workflows/test-adapters.yml | ||
| with: | ||
| artifacts_run_id: ${{ needs.check-prior-build.outputs.artifacts_run_id }} | ||
| secrets: inherit | ||
|
|
||
| invoke-build-docker: | ||
| name: Build Docker | ||
| needs: [invoke-build-rust, invoke-build-java] | ||
| needs: [check-prior-build, invoke-build-rust, invoke-build-java] | ||
| if: | | ||
| always() && | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
| needs.check-prior-build.outputs.skip_docker != 'true' && | ||
| (needs.invoke-build-rust.result == 'success' || needs.invoke-build-rust.result == 'skipped') && | ||
| (needs.invoke-build-java.result == 'success' || needs.invoke-build-java.result == 'skipped') | ||
| uses: ./.github/workflows/build-docker.yml | ||
| with: | ||
| artifacts_run_id: ${{ needs.check-prior-build.outputs.artifacts_run_id }} | ||
| secrets: inherit | ||
|
|
||
| invoke-generate-sbom: | ||
| name: Generate SBOMs | ||
| needs: [invoke-build-docker] | ||
| if: | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
| always() && | ||
| (needs.invoke-build-docker.result == 'success' || needs.invoke-build-docker.result == 'skipped') | ||
| uses: ./.github/workflows/generate-sbom.yml | ||
| secrets: inherit | ||
|
|
||
| invoke-tests-integration-platform: | ||
| name: Integration Tests | ||
| needs: [invoke-build-docker] | ||
| needs: [check-prior-build, invoke-build-docker] | ||
| if: | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
| always() && | ||
| (needs.invoke-build-docker.result == 'success' || needs.invoke-build-docker.result == 'skipped') | ||
| uses: ./.github/workflows/test-integration-platform.yml | ||
| with: | ||
| artifacts_run_id: ${{ needs.check-prior-build.outputs.artifacts_run_id }} | ||
| secrets: inherit | ||
|
|
||
| invoke-tests-integration-runtime: | ||
| name: Integration Tests | ||
| needs: [invoke-build-java] | ||
| needs: [check-prior-build, invoke-build-java] | ||
| if: | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
| always() && | ||
| (needs.invoke-build-java.result == 'success' || needs.invoke-build-java.result == 'skipped') | ||
| uses: ./.github/workflows/test-integration-runtime.yml | ||
| secrets: inherit | ||
|
|
||
| invoke-tests-java: | ||
| name: Java Tests | ||
| needs: [invoke-build-java] | ||
| needs: [check-prior-build, invoke-build-java] | ||
| if: | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
| always() && | ||
| (needs.invoke-build-java.result == 'success' || needs.invoke-build-java.result == 'skipped') | ||
| uses: ./.github/workflows/test-java.yml | ||
| secrets: inherit | ||
|
|
||
| invoke-publish-crates-dry-run: | ||
| name: Publish Crates (Dry Run) | ||
| needs: [invoke-build-rust] | ||
| needs: [check-prior-build, invoke-build-rust] | ||
| if: | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same q |
||
| always() && | ||
| (needs.invoke-build-rust.result == 'success' || needs.invoke-build-rust.result == 'skipped') | ||
| uses: ./.github/workflows/publish-crates.yml | ||
| with: | ||
| environment: ci | ||
|
|
@@ -234,6 +372,7 @@ jobs: | |
| if: always() | ||
| runs-on: ubuntu-latest-amd64 | ||
| needs: | ||
| - check-prior-build | ||
| - invoke-build-rust | ||
| - invoke-build-java | ||
| - invoke-build-docs | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,21 @@ name: Unit Tests | |
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| artifacts_run_id: | ||
| description: "Run ID to download build artifacts from (empty = current run)" | ||
| type: string | ||
| required: false | ||
| default: "" | ||
| workflow_dispatch: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this also need a run id similar to test-integration-platform.yaml?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, added in the latest commit. |
||
|
|
||
| env: | ||
| RUST_BACKTRACE: 1 | ||
|
|
||
| permissions: | ||
| actions: read | ||
| contents: read | ||
|
|
||
| jobs: | ||
| rust-unit-tests: | ||
| name: Rust Unit Tests | ||
|
|
@@ -33,12 +43,16 @@ jobs: | |
| with: | ||
| name: feldera-test-binaries-${{ matrix.target }} | ||
| path: build | ||
| run-id: ${{ inputs.artifacts_run_id || github.run_id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Download Compiler Binaries | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: feldera-sql-compiler | ||
| path: sql-build | ||
| run-id: ${{ inputs.artifacts_run_id || github.run_id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| # Remove if https://github.com/actions/upload-artifact/issues/38 ever gets fixed | ||
| - name: Make binaries executable | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there some special semantic associated with always() in gha? otherwise this is just
if true && a && band can be rewritten asif a && bThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
always() is needed here, without it, GHA silently skips dependent jobs when a needs: job is skipped, so the result == 'skipped' checks would never be reached and test jobs would be silently skipped whenever the build phase is skipped.
always() is not true, it prevents GHA from skipping the job before evaluating the condition.