From a7dbb736862e0bd9921bf57c7aedb552942819d3 Mon Sep 17 00:00:00 2001 From: Saul Beck Date: Mon, 13 Jul 2026 07:18:09 +0100 Subject: [PATCH 1/2] ci: CI hardening * ci: Bump actions/checkout to v7.0.0 * ci: Add explicit permissions to jobs * ci: Conditionally install host dependencies without cache * ci: Conditionally set rust-cache lookup-only * ci: Skip publish jobs for forks * ci: Remove checkout step from notify job --- .github/workflows/generate_prs.yml | 52 ++++++++++--------- .github/workflows/pr_prek.yml | 4 +- playbook/playbook.yaml | 2 + template/.github/workflows/build.yaml.j2 | 36 +++++++++---- .../workflows/general_daily_security.yml | 6 ++- .../.github/workflows/integration-test.yml | 4 +- template/.github/workflows/pr_prek.yaml.j2 | 4 +- test.sh | 2 +- 8 files changed, 71 insertions(+), 39 deletions(-) diff --git a/.github/workflows/generate_prs.yml b/.github/workflows/generate_prs.yml index 34078e61..f38dc202 100644 --- a/.github/workflows/generate_prs.yml +++ b/.github/workflows/generate_prs.yml @@ -25,6 +25,8 @@ permissions: {} jobs: create-prs: runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: ${{ inputs.fail-fast }} matrix: @@ -48,7 +50,7 @@ jobs: - trino-operator - zookeeper-operator steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: cachix/install-nix-action@fd24c48048070c1be9acd18c9d369a83f0fe94d7 # v31.8.1 @@ -76,33 +78,35 @@ jobs: - name: Install jinja2 run: pip install -r requirements.txt - # Create commit message depending on whether this is run manually or due to a scheduled run - - name: Set commit message for manual dispatch - if: ${{ github.event_name == 'workflow_dispatch' }} - env: - REASON: ${{ github.event.inputs.message }} - AUTHOR: ${{ github.event.sender.login }} - run: | - echo "AUTHOR=$AUTHOR" >> "$GITHUB_ENV" - echo "REASON=$REASON" >> "$GITHUB_ENV" - - name: Set commit message for schedule - if: ${{ github.event_name == 'schedule' }} - run: | - echo "AUTHOR=stackabletech/developers" - echo "REASON=Daily run triggered" >> "$GITHUB_ENV" - + # Create commit message and run the playbook - name: Run playbook env: CUSTOM_PR_TITLE: ${{ inputs.custom-pr-title }} GH_ACCESS_TOKEN: ${{ secrets.gh_access_token }} REPOSITORY: ${{ matrix.repository }} - # tags local excludes all actions that actually generate PRs - DRY_RUN_FLAGS: ${{ inputs.dry-run && '--tags=local' || '' }} - # shellsheck disable=SC2086 + DRY_RUN_FLAGS: ${{ (github.event_name != 'schedule' && inputs.dry-run != false) && '--tags=local' || '' }} + # Safely pass inputs/context via environment variables to prevent injection + INPUT_MESSAGE: ${{ github.event.inputs.message }} + SENDER_LOGIN: ${{ github.event.sender.login }} + EVENT_NAME: ${{ github.event_name }} run: | - echo '{}' | jq '{commit_hash: $ENV.GITHUB_SHA, author: $ENV.AUTHOR, reason: $ENV.REASON, custom_pr_title: $ENV.CUSTOM_PR_TITLE, base_dir: $pwd, gh_access_token: $ENV.GH_ACCESS_TOKEN, shard_repositories: [$ENV.REPOSITORY]} | with_entries(select(.value != null and .value != ""))' --arg pwd "$(pwd)" > vars.json - # $DRY_RUN_FLAGS is intentionally not quoted, since we need to avoid - # passing an argument if we're not in dry mode. - # This is safe, since it always has one of two hard-coded values. - # shellcheck disable=SC2086 + if [ "$EVENT_NAME" = "schedule" ]; then + AUTHOR="stackabletech/developers" + REASON="Weekly run triggered" + else + AUTHOR="$SENDER_LOGIN" + REASON="$INPUT_MESSAGE" + fi + + # Encode parameters safely using jq, without serializing the GH_ACCESS_TOKEN + jq -n \ + --arg commit_hash "$GITHUB_SHA" \ + --arg author "$AUTHOR" \ + --arg reason "$REASON" \ + --arg custom_pr_title "$CUSTOM_PR_TITLE" \ + --arg base_dir "$(pwd)" \ + --arg repository "$REPOSITORY" \ + '{commit_hash: $commit_hash, author: $author, reason: $reason, custom_pr_title: $custom_pr_title, base_dir: $base_dir, shard_repositories: [$repository]} | with_entries(select(.value != null and .value != ""))' > vars.json + + # Run the Ansible playbook. The token is read directly from the GH_ACCESS_TOKEN environment variable. ansible-playbook playbook/playbook.yaml --extra-vars "@vars.json" $DRY_RUN_FLAGS diff --git a/.github/workflows/pr_prek.yml b/.github/workflows/pr_prek.yml index d7107b71..5fc6c7fe 100644 --- a/.github/workflows/pr_prek.yml +++ b/.github/workflows/pr_prek.yml @@ -12,8 +12,10 @@ permissions: {} jobs: prek: runs-on: ubuntu-latest + permissions: + contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false fetch-depth: 0 diff --git a/playbook/playbook.yaml b/playbook/playbook.yaml index 1e98e590..91fc9900 100644 --- a/playbook/playbook.yaml +++ b/playbook/playbook.yaml @@ -2,6 +2,8 @@ - name: Synchronize operator template files with operator repositories hosts: localhost connection: local + vars: + gh_access_token: "{{ lookup('env', 'GH_ACCESS_TOKEN') }}" tasks: - name: Include data which repositories to check diff --git a/template/.github/workflows/build.yaml.j2 b/template/.github/workflows/build.yaml.j2 index de1ca64f..8705bdbc 100644 --- a/template/.github/workflows/build.yaml.j2 +++ b/template/.github/workflows/build.yaml.j2 @@ -39,9 +39,11 @@ jobs: detect-changes: name: Detect relevant changed files runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false fetch-depth: 0 @@ -67,6 +69,8 @@ jobs: if: needs.detect-changes.outputs.detected == 'true' needs: [detect-changes] runs-on: ubuntu-latest + permissions: + contents: read env: RUSTC_BOOTSTRAP: 1 steps: @@ -77,7 +81,7 @@ jobs: version: ubuntu-latest - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false submodules: recursive @@ -91,6 +95,7 @@ jobs: with: cache-all-crates: "true" key: udeps + lookup-only: ${{ github.event_name == 'pull_request' }} - name: Install cargo-udeps uses: stackabletech/cargo-install-action@8f7dbbcd2ebe22717efc132d0dd61e80841994b9 # cargo-udeps @@ -103,6 +108,7 @@ jobs: if: (github.event_name != 'merge_group') && needs.detect-changes.outputs.detected == 'true' needs: [detect-changes] permissions: + contents: read id-token: write strategy: fail-fast: false @@ -114,14 +120,25 @@ jobs: outputs: operator-version: ${{ steps.version.outputs.OPERATOR_VERSION }} steps: + # Use cached apt packages on PRs. + # For merges to main and releases, bypass the cache and install fresh package to ensure we have the absolute latest versions. - name: Install host dependencies + if: github.event_name == 'pull_request' uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # v1.5.3 with: packages: protobuf-compiler krb5-user libkrb5-dev libclang-dev liblzma-dev libssl-dev pkg-config apt-transport-https version: ${{ matrix.runner.name }} + - name: Install host dependencies (no cache) + if: github.event_name != 'pull_request' + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler krb5-user libkrb5-dev \ + libclang-dev liblzma-dev libssl-dev pkg-config apt-transport-https + + - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false submodules: recursive @@ -203,6 +220,7 @@ jobs: - detect-changes - build-container-image permissions: + contents: read id-token: write runs-on: ubuntu-latest outputs: @@ -210,7 +228,7 @@ jobs: quay-index-digest: ${{ steps.publish-quay.outputs.image-index-manifest-digest }} steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false @@ -297,15 +315,17 @@ jobs: if: | (github.event_name != 'merge_group') && needs.detect-changes.outputs.detected == 'true' + && !github.event.pull_request.head.repo.fork needs: - detect-changes - build-container-image permissions: + contents: read id-token: write runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false submodules: recursive @@ -394,13 +414,9 @@ jobs: - provenance-oci - provenance-quay - publish-helm-chart + - finished runs-on: ubuntu-latest steps: - - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - persist-credentials: false - - name: Send Notification uses: stackabletech/actions/send-slack-notification@a8af17a19bdcc3b5da0065f76e73827ba0c072ce # v0.16.0 with: diff --git a/template/.github/workflows/general_daily_security.yml b/template/.github/workflows/general_daily_security.yml index d4c7825c..1b5d22f1 100644 --- a/template/.github/workflows/general_daily_security.yml +++ b/template/.github/workflows/general_daily_security.yml @@ -15,8 +15,12 @@ permissions: {} jobs: audit: runs-on: ubuntu-latest + permissions: + contents: read + checks: write + issues: write steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0 diff --git a/template/.github/workflows/integration-test.yml b/template/.github/workflows/integration-test.yml index 6d54cb8e..d47bc385 100644 --- a/template/.github/workflows/integration-test.yml +++ b/template/.github/workflows/integration-test.yml @@ -29,13 +29,15 @@ jobs: test: name: Run Integration Test runs-on: ubuntu-latest + permissions: + contents: read # services: # otel-collector: # image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-k8s:0.131.1 # volumes: # - .:/mnt steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false submodules: recursive diff --git a/template/.github/workflows/pr_prek.yaml.j2 b/template/.github/workflows/pr_prek.yaml.j2 index 172ea7e4..79ea96b6 100644 --- a/template/.github/workflows/pr_prek.yaml.j2 +++ b/template/.github/workflows/pr_prek.yaml.j2 @@ -17,13 +17,15 @@ env: jobs: prek: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Install host dependencies uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0 with: packages: protobuf-compiler krb5-user libkrb5-dev libclang-dev liblzma-dev libssl-dev pkg-config apt-transport-https version: ubuntu-latest - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false submodules: recursive diff --git a/test.sh b/test.sh index 8f7e5ec1..2d0b4f20 100755 --- a/test.sh +++ b/test.sh @@ -2,4 +2,4 @@ rm -fr work || true mkdir -p work -ansible-playbook playbook/playbook.yaml --tags="local" --extra-vars "gh_access_token=unneeded base_dir=$(pwd) commit_hash=12345 reason='original message'" "$@" +GH_ACCESS_TOKEN=unneeded ansible-playbook playbook/playbook.yaml --tags="local" --extra-vars "base_dir=$(pwd) commit_hash=12345 reason='original message'" "$@" From 2d00d303e66cfd9de449103b2adfb84b3a9f28f3 Mon Sep 17 00:00:00 2001 From: Saul Beck Date: Tue, 14 Jul 2026 10:14:13 +0100 Subject: [PATCH 2/2] ci: Improvement * ci: Prevent notification on cancelled workflows * ci: Update failure condition to check job results * ci: Add missing job dependencies to notify job * ci: Validate required jobs in finished job --- template/.github/workflows/build.yaml.j2 | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/template/.github/workflows/build.yaml.j2 b/template/.github/workflows/build.yaml.j2 index 8705bdbc..1d2f283e 100644 --- a/template/.github/workflows/build.yaml.j2 +++ b/template/.github/workflows/build.yaml.j2 @@ -392,28 +392,44 @@ jobs: # WARNING: Do not change the name unless you will also be changing the # Required Checks (in branch protections) in GitHub settings. name: Finished Build and Publish + if: always() needs: + - detect-changes - cargo-udeps + - build-container-image + - publish-index-manifest - openshift-preflight-check - publish-helm-chart + - provenance-oci + - provenance-quay runs-on: ubuntu-latest steps: - - run: echo "We are done here" + - name: Fail if a required job failed or was cancelled + if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} + run: | + echo "One or more required jobs failed or were cancelled." + exit 1 + + - name: Finished + run: echo "All required jobs succeeded or were intentionally skipped." notify: name: Failure Notification if: | - (failure() || github.run_attempt > 1) + !cancelled() + && (contains(needs.*.result, 'failure') || github.run_attempt > 1) && github.event_name != 'merge_group' && needs.detect-changes.outputs.detected == 'true' && !github.event.pull_request.head.repo.fork needs: - detect-changes + - cargo-udeps - build-container-image - publish-index-manifest - provenance-oci - provenance-quay - publish-helm-chart + - openshift-preflight-check - finished runs-on: ubuntu-latest steps: