From ef04e89d7538685ec067f6b231eb21d0f5eb575b Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 18:14:25 -0400 Subject: [PATCH 1/8] ci: stop spending the build pipeline on documentation-only changes The documentation site is already excluded from the server release, so a change confined to it cannot affect what the pipeline verifies. Signed-off-by: Yordis Prieto --- .../mise/tasks/github-actions/changed-scope | 57 +++++++++++++++++++ .../workflows/build-container-ubuntu-lts.yml | 21 ++++++- .github/workflows/build-ubuntu-lts-arm64.yml | 14 ++++- .github/workflows/build-ubuntu-lts-x64.yml | 14 ++++- .github/workflows/changes-reusable.yml | 48 ++++++++++++++++ .github/workflows/common.yml | 26 ++++++++- 6 files changed, 175 insertions(+), 5 deletions(-) create mode 100755 .config/mise/tasks/github-actions/changed-scope create mode 100644 .github/workflows/changes-reusable.yml diff --git a/.config/mise/tasks/github-actions/changed-scope b/.config/mise/tasks/github-actions/changed-scope new file mode 100755 index 000000000..555155e8f --- /dev/null +++ b/.config/mise/tasks/github-actions/changed-scope @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +#MISE description="Write whether the change set reaches past the documentation site to GITHUB_OUTPUT" + +set -euo pipefail + +: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" +: "${EVENT_NAME:?EVENT_NAME is required}" + +# Kept in step with the paths release-please excludes from the server release. +documentation_paths=('docs/') + +emit() { + printf 'code=%s\n' "$1" >>"$GITHUB_OUTPUT" + echo "code=$1" + exit 0 +} + +if [ "$EVENT_NAME" != "pull_request" ] && [ "$EVENT_NAME" != "merge_group" ]; then + echo "Event '$EVENT_NAME' always exercises the full pipeline." + emit true +fi + +: "${BASE_REF:?BASE_REF is required}" +head_ref="${HEAD_REF:-HEAD}" + +if [[ "$BASE_REF" =~ ^0+$ ]]; then + echo "The base ref is unresolved." + emit true +fi + +comparison_base="$BASE_REF" +if merge_base="$(git merge-base "$BASE_REF" "$head_ref" 2>/dev/null)"; then + comparison_base="$merge_base" +fi + +if ! changed_files="$(git diff --name-only --diff-filter=ACMRD "$comparison_base" "$head_ref")"; then + echo "The change set could not be resolved." + emit true +fi + +if [ -z "$changed_files" ]; then + echo "The change set is empty." + emit true +fi + +while IFS= read -r file; do + for prefix in "${documentation_paths[@]}"; do + if [[ "$file" == "$prefix"* ]]; then + continue 2 + fi + done + echo "Change outside the documentation site: $file" + emit true +done <<<"$changed_files" + +echo "Documentation-only change set." +emit false diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index 75ca24883..c0171e152 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -17,7 +17,13 @@ concurrency: cancel-in-progress: true jobs: + changes: + name: Changes + uses: ./.github/workflows/changes-reusable.yml + build-images: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} name: Build Images uses: ./.github/workflows/build-container-reusable.yml with: @@ -30,7 +36,10 @@ jobs: test: name: Build (${{ matrix.test-group-name }}) - needs: build-images + needs: + - changes + - build-images + if: ${{ needs.changes.outputs.code == 'true' }} strategy: fail-fast: true matrix: @@ -58,8 +67,10 @@ jobs: client-compatibility: name: Client Compatibility needs: + - changes - build-images - test + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest permissions: contents: read @@ -137,6 +148,7 @@ jobs: name: Container CI if: ${{ always() }} needs: + - changes - build-images - test - client-compatibility @@ -146,4 +158,9 @@ jobs: - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - run: jq -e 'all(.[]; .result == "success")' <<< "$REQUIRED_JOBS" + EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} + run: | + jq -e --arg expected "$EXPECTED_RESULT" ' + .changes.result == "success" and + (del(.changes) | all(.[]; .result == $expected)) + ' <<< "$REQUIRED_JOBS" diff --git a/.github/workflows/build-ubuntu-lts-arm64.yml b/.github/workflows/build-ubuntu-lts-arm64.yml index 2c7e9e281..44c0e1158 100644 --- a/.github/workflows/build-ubuntu-lts-arm64.yml +++ b/.github/workflows/build-ubuntu-lts-arm64.yml @@ -17,7 +17,13 @@ concurrency: cancel-in-progress: true jobs: + changes: + name: Changes + uses: ./.github/workflows/changes-reusable.yml + build: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} name: Build uses: ./.github/workflows/build-reusable.yml with: @@ -29,6 +35,7 @@ jobs: name: ARM64 CI if: ${{ always() }} needs: + - changes - build runs-on: ubuntu-latest permissions: {} @@ -36,4 +43,9 @@ jobs: - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - run: jq -e 'all(.[]; .result == "success")' <<< "$REQUIRED_JOBS" + EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} + run: | + jq -e --arg expected "$EXPECTED_RESULT" ' + .changes.result == "success" and + (del(.changes) | all(.[]; .result == $expected)) + ' <<< "$REQUIRED_JOBS" diff --git a/.github/workflows/build-ubuntu-lts-x64.yml b/.github/workflows/build-ubuntu-lts-x64.yml index 0e08a8f23..dba47b25b 100644 --- a/.github/workflows/build-ubuntu-lts-x64.yml +++ b/.github/workflows/build-ubuntu-lts-x64.yml @@ -13,7 +13,13 @@ on: - v* jobs: + changes: + name: Changes + uses: ./.github/workflows/changes-reusable.yml + build: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} name: Build uses: ./.github/workflows/build-reusable.yml with: @@ -25,6 +31,7 @@ jobs: name: X64 CI if: ${{ always() }} needs: + - changes - build runs-on: ubuntu-latest permissions: {} @@ -32,4 +39,9 @@ jobs: - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - run: jq -e 'all(.[]; .result == "success")' <<< "$REQUIRED_JOBS" + EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} + run: | + jq -e --arg expected "$EXPECTED_RESULT" ' + .changes.result == "success" and + (del(.changes) | all(.[]; .result == $expected)) + ' <<< "$REQUIRED_JOBS" diff --git a/.github/workflows/changes-reusable.yml b/.github/workflows/changes-reusable.yml new file mode 100644 index 000000000..44dbae5af --- /dev/null +++ b/.github/workflows/changes-reusable.yml @@ -0,0 +1,48 @@ +name: Changes - Reusable + +on: + workflow_call: + outputs: + code: + description: Whether the change set reaches past the documentation site + value: ${{ jobs.detect.outputs.code }} + +permissions: + contents: read + +jobs: + detect: + runs-on: ubuntu-latest + name: Changes + outputs: + code: ${{ steps.scope.outputs.code }} + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Install task runner + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 + with: + version: 2026.8.2 + install: false + cache: false + - name: Resolve comparison refs + id: refs + env: + EVENT_NAME: ${{ github.event_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} + MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} + BEFORE_SHA: ${{ github.event.before }} + CURRENT_SHA: ${{ github.sha }} + run: mise run --skip-tools github-actions:resolve-comparison-refs + - name: Resolve change scope + id: scope + env: + EVENT_NAME: ${{ github.event_name }} + BASE_REF: ${{ steps.refs.outputs.base }} + HEAD_REF: ${{ steps.refs.outputs.head }} + run: mise run --skip-tools github-actions:changed-scope diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 3bf83919b..9004cc6d4 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -22,7 +22,13 @@ permissions: packages: read jobs: + changes: + name: Changes + uses: ./.github/workflows/changes-reusable.yml + semantic-conventions: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest name: Semantic Conventions steps: @@ -40,6 +46,8 @@ jobs: run: mise run --skip-tools semconv:check csharp-quality: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest name: C# Quality steps: @@ -88,6 +96,8 @@ jobs: src/TrogonEventStore.SemanticConventions/TrogonEventStore.SemanticConventions.csproj vulnerability-scan: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest name: Scan for Vulnerabilities steps: @@ -112,6 +122,8 @@ jobs: run: mise run --skip-tools github-actions:vulnerability-scan protolock: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest name: Protolock Status steps: @@ -123,6 +135,8 @@ jobs: ./protolock.sh status --uptodate release-metadata: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest name: Release Metadata steps: @@ -143,6 +157,8 @@ jobs: run: mise run --skip-tools github-actions:publish-container-test archive-storage-contract: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest name: Archive Storage Contract env: @@ -214,6 +230,8 @@ jobs: run: mise run --skip-tools github-actions:archive-storage-contract -- cluster-restore docker-compose: + needs: changes + if: ${{ needs.changes.outputs.code == 'true' }} runs-on: ubuntu-latest name: Docker Compose Smoke Test steps: @@ -241,6 +259,7 @@ jobs: name: Common CI if: ${{ always() }} needs: + - changes - semantic-conventions - csharp-quality - vulnerability-scan @@ -254,4 +273,9 @@ jobs: - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - run: jq -e 'all(.[]; .result == "success")' <<< "$REQUIRED_JOBS" + EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} + run: | + jq -e --arg expected "$EXPECTED_RESULT" ' + .changes.result == "success" and + (del(.changes) | all(.[]; .result == $expected)) + ' <<< "$REQUIRED_JOBS" From c676c5c9b3aacd5e30158c6b80ce8b835f492bbd Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 18:19:28 -0400 Subject: [PATCH 2/8] refactor(ci): nest the workflow tasks under a github namespace A flat github-actions prefix reads as one word and leaves no room for sibling github tooling that is not tied to Actions. Signed-off-by: Yordis Prieto --- .../actions}/archive-storage-contract | 2 +- .../actions}/changed-scope | 0 .../actions}/client-compatibility | 2 +- .../actions}/container | 2 +- .../actions}/docker-compose-smoke | 2 +- .../actions}/dotnet-build | 2 +- .../actions}/nuget-authenticate | 0 .../actions}/publish-container | 0 .../actions}/publish-container-test | 0 .../actions}/resolve-comparison-refs | 0 .../actions}/vulnerability-scan | 2 +- .../workflows/build-container-reusable.yml | 12 ++++----- .../workflows/build-container-ubuntu-lts.yml | 6 ++--- .github/workflows/build-reusable.yml | 10 +++---- .github/workflows/changes-reusable.yml | 4 +-- .github/workflows/common.yml | 26 +++++++++---------- .github/workflows/tag-docker-images.yml | 4 +-- 17 files changed, 37 insertions(+), 37 deletions(-) rename .config/mise/tasks/{github-actions => github/actions}/archive-storage-contract (96%) rename .config/mise/tasks/{github-actions => github/actions}/changed-scope (100%) rename .config/mise/tasks/{github-actions => github/actions}/client-compatibility (99%) rename .config/mise/tasks/{github-actions => github/actions}/container (92%) rename .config/mise/tasks/{github-actions => github/actions}/docker-compose-smoke (95%) rename .config/mise/tasks/{github-actions => github/actions}/dotnet-build (96%) rename .config/mise/tasks/{github-actions => github/actions}/nuget-authenticate (100%) rename .config/mise/tasks/{github-actions => github/actions}/publish-container (100%) rename .config/mise/tasks/{github-actions => github/actions}/publish-container-test (100%) rename .config/mise/tasks/{github-actions => github/actions}/resolve-comparison-refs (100%) rename .config/mise/tasks/{github-actions => github/actions}/vulnerability-scan (85%) diff --git a/.config/mise/tasks/github-actions/archive-storage-contract b/.config/mise/tasks/github/actions/archive-storage-contract similarity index 96% rename from .config/mise/tasks/github-actions/archive-storage-contract rename to .config/mise/tasks/github/actions/archive-storage-contract index 0a72f9b9f..bd0317556 100755 --- a/.config/mise/tasks/github-actions/archive-storage-contract +++ b/.config/mise/tasks/github/actions/archive-storage-contract @@ -3,7 +3,7 @@ set -eu -root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../.." && pwd) +root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../../.." && pwd) xunit_project="$root/src/EventStore.Core.XUnit.Tests/EventStore.Core.XUnit.Tests.csproj" core_project="$root/src/EventStore.Core.Tests/EventStore.Core.Tests.csproj" phase=${1:?archive storage phase is required} diff --git a/.config/mise/tasks/github-actions/changed-scope b/.config/mise/tasks/github/actions/changed-scope similarity index 100% rename from .config/mise/tasks/github-actions/changed-scope rename to .config/mise/tasks/github/actions/changed-scope diff --git a/.config/mise/tasks/github-actions/client-compatibility b/.config/mise/tasks/github/actions/client-compatibility similarity index 99% rename from .config/mise/tasks/github-actions/client-compatibility rename to .config/mise/tasks/github/actions/client-compatibility index 055fa1ab5..8d5354fb5 100755 --- a/.config/mise/tasks/github-actions/client-compatibility +++ b/.config/mise/tasks/github/actions/client-compatibility @@ -3,7 +3,7 @@ set -eu -root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../.." && pwd) +root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../../.." && pwd) phase=${1:?client compatibility phase is required} config="$root/.github/compatibility/clients.json" diff --git a/.config/mise/tasks/github-actions/container b/.config/mise/tasks/github/actions/container similarity index 92% rename from .config/mise/tasks/github-actions/container rename to .config/mise/tasks/github/actions/container index 130652418..fc51c0838 100755 --- a/.config/mise/tasks/github-actions/container +++ b/.config/mise/tasks/github/actions/container @@ -3,7 +3,7 @@ set -eu -root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../.." && pwd) +root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../../.." && pwd) phase=${1:?container phase is required} cd "$root" diff --git a/.config/mise/tasks/github-actions/docker-compose-smoke b/.config/mise/tasks/github/actions/docker-compose-smoke similarity index 95% rename from .config/mise/tasks/github-actions/docker-compose-smoke rename to .config/mise/tasks/github/actions/docker-compose-smoke index 3be4a5a58..1442513e6 100755 --- a/.config/mise/tasks/github-actions/docker-compose-smoke +++ b/.config/mise/tasks/github/actions/docker-compose-smoke @@ -3,7 +3,7 @@ set -eu -root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../.." && pwd) +root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../../.." && pwd) fingerprints_before=$(mktemp) fingerprints_after=$(mktemp) compose_started=0 diff --git a/.config/mise/tasks/github-actions/dotnet-build b/.config/mise/tasks/github/actions/dotnet-build similarity index 96% rename from .config/mise/tasks/github-actions/dotnet-build rename to .config/mise/tasks/github/actions/dotnet-build index 451f7e181..8e74820ff 100755 --- a/.config/mise/tasks/github-actions/dotnet-build +++ b/.config/mise/tasks/github/actions/dotnet-build @@ -3,7 +3,7 @@ set -euo pipefail -root=$(cd -- "$(dirname -- "$0")/../../../.." && pwd) +root=$(cd -- "$(dirname -- "$0")/../../../../.." && pwd) phase=${1:?dotnet build phase is required} case "$phase" in diff --git a/.config/mise/tasks/github-actions/nuget-authenticate b/.config/mise/tasks/github/actions/nuget-authenticate similarity index 100% rename from .config/mise/tasks/github-actions/nuget-authenticate rename to .config/mise/tasks/github/actions/nuget-authenticate diff --git a/.config/mise/tasks/github-actions/publish-container b/.config/mise/tasks/github/actions/publish-container similarity index 100% rename from .config/mise/tasks/github-actions/publish-container rename to .config/mise/tasks/github/actions/publish-container diff --git a/.config/mise/tasks/github-actions/publish-container-test b/.config/mise/tasks/github/actions/publish-container-test similarity index 100% rename from .config/mise/tasks/github-actions/publish-container-test rename to .config/mise/tasks/github/actions/publish-container-test diff --git a/.config/mise/tasks/github-actions/resolve-comparison-refs b/.config/mise/tasks/github/actions/resolve-comparison-refs similarity index 100% rename from .config/mise/tasks/github-actions/resolve-comparison-refs rename to .config/mise/tasks/github/actions/resolve-comparison-refs diff --git a/.config/mise/tasks/github-actions/vulnerability-scan b/.config/mise/tasks/github/actions/vulnerability-scan similarity index 85% rename from .config/mise/tasks/github-actions/vulnerability-scan rename to .config/mise/tasks/github/actions/vulnerability-scan index 7750e2ce2..277c30d92 100755 --- a/.config/mise/tasks/github-actions/vulnerability-scan +++ b/.config/mise/tasks/github/actions/vulnerability-scan @@ -3,7 +3,7 @@ set -eu -root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../.." && pwd) +root=$(CDPATH='' cd -- "$(dirname -- "$0")/../../../../.." && pwd) results=$(mktemp) trap 'rm -f "$results"' EXIT HUP INT TERM diff --git a/.github/workflows/build-container-reusable.yml b/.github/workflows/build-container-reusable.yml index 7171983b4..f8e17df8a 100644 --- a/.github/workflows/build-container-reusable.yml +++ b/.github/workflows/build-container-reusable.yml @@ -82,7 +82,7 @@ jobs: nuget_auth_token=${{ secrets.GITHUB_TOKEN }} - name: Verify Build if: ${{ inputs.mode != 'test-only' }} - run: mise run --skip-tools github-actions:container -- verify + run: mise run --skip-tools github:actions:container -- verify - name: Build Test Container uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 if: ${{ inputs.mode != 'test-only' }} @@ -100,10 +100,10 @@ jobs: nuget_auth_token=${{ secrets.GITHUB_TOKEN }} - name: Export Test Container if: ${{ inputs.mode == 'build-only' }} - run: mise run --skip-tools github-actions:container -- export-test + run: mise run --skip-tools github:actions:container -- export-test - name: Export Container if: ${{ inputs.mode == 'build-only' }} - run: mise run --skip-tools github-actions:container -- export-runtime + run: mise run --skip-tools github:actions:container -- export-runtime - name: Publish Test Container uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: ${{ inputs.mode == 'build-only' }} @@ -127,15 +127,15 @@ jobs: name: ${{ inputs.test-image-artifact-name }} - name: Load Test Container if: ${{ inputs.mode == 'test-only' }} - run: mise run --skip-tools github-actions:container -- load-test + run: mise run --skip-tools github:actions:container -- load-test - name: Run Tests if: ${{ inputs.mode != 'build-only' }} env: TEST_GROUP: ${{ inputs.test-group-name }} - run: mise run --skip-tools github-actions:container -- test + run: mise run --skip-tools github:actions:container -- test - name: Normalize Test Result Permissions if: ${{ failure() && steps.mise.outcome == 'success' }} - run: mise run --skip-tools github-actions:container -- normalize-results + run: mise run --skip-tools github:actions:container -- normalize-results - name: Publish Test Results uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: failure() diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index c0171e152..585830e98 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -97,7 +97,7 @@ jobs: rustup default stable - name: Resolve client revisions id: clients - run: mise run --skip-tools github-actions:client-compatibility -- refs + run: mise run --skip-tools github:actions:client-compatibility -- refs - name: Checkout C# client uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -117,9 +117,9 @@ jobs: with: name: eventstore-image-noble-linux-x64 - name: Load server image - run: mise run --skip-tools github-actions:container -- load-runtime + run: mise run --skip-tools github:actions:container -- load-runtime - name: Run client compatibility gate - run: mise run --skip-tools github-actions:client-compatibility -- run + run: mise run --skip-tools github:actions:client-compatibility -- run - name: Publish compatibility evidence uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() diff --git a/.github/workflows/build-reusable.yml b/.github/workflows/build-reusable.yml index b9ee8a51a..3fdd85d3d 100644 --- a/.github/workflows/build-reusable.yml +++ b/.github/workflows/build-reusable.yml @@ -42,26 +42,26 @@ jobs: env: NUGET_GITHUB_ACTOR: ${{ github.actor }} NUGET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: mise run --skip-tools github-actions:nuget-authenticate + run: mise run --skip-tools github:actions:nuget-authenticate - name: Compile env: BUILD_CONFIGURATION: ${{ matrix.configuration }} NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TARGET_ARCH: ${{ inputs.arch }} - run: mise run --skip-tools github-actions:dotnet-build -- compile + run: mise run --skip-tools github:actions:dotnet-build -- compile - name: Verify Build env: BUILD_CONFIGURATION: ${{ matrix.configuration }} TARGET_ARCH: ${{ inputs.arch }} - run: mise run --skip-tools github-actions:dotnet-build -- verify + run: mise run --skip-tools github:actions:dotnet-build -- verify - name: Run Tests env: BUILD_CONFIGURATION: ${{ matrix.configuration }} TARGET_ARCH: ${{ inputs.arch }} - run: mise run --skip-tools github-actions:dotnet-build -- test + run: mise run --skip-tools github:actions:dotnet-build -- test - name: Collect Test Results if: ${{ always() && steps.mise.outcome == 'success' }} - run: mise run --skip-tools github-actions:dotnet-build -- collect-results + run: mise run --skip-tools github:actions:dotnet-build -- collect-results - name: Publish Test Results (HTML) uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() diff --git a/.github/workflows/changes-reusable.yml b/.github/workflows/changes-reusable.yml index 44dbae5af..ee958997e 100644 --- a/.github/workflows/changes-reusable.yml +++ b/.github/workflows/changes-reusable.yml @@ -38,11 +38,11 @@ jobs: MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} BEFORE_SHA: ${{ github.event.before }} CURRENT_SHA: ${{ github.sha }} - run: mise run --skip-tools github-actions:resolve-comparison-refs + run: mise run --skip-tools github:actions:resolve-comparison-refs - name: Resolve change scope id: scope env: EVENT_NAME: ${{ github.event_name }} BASE_REF: ${{ steps.refs.outputs.base }} HEAD_REF: ${{ steps.refs.outputs.head }} - run: mise run --skip-tools github-actions:changed-scope + run: mise run --skip-tools github:actions:changed-scope diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 9004cc6d4..35628b70d 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -69,7 +69,7 @@ jobs: env: NUGET_GITHUB_ACTOR: ${{ github.actor }} NUGET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: mise run --skip-tools github-actions:nuget-authenticate + run: mise run --skip-tools github:actions:nuget-authenticate - name: Resolve comparison refs id: refs env: @@ -80,7 +80,7 @@ jobs: MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} BEFORE_SHA: ${{ github.event.before }} CURRENT_SHA: ${{ github.sha }} - run: mise run --skip-tools github-actions:resolve-comparison-refs + run: mise run --skip-tools github:actions:resolve-comparison-refs - name: Restore run: | dotnet restore src/EventStore.sln @@ -117,9 +117,9 @@ jobs: env: NUGET_GITHUB_ACTOR: ${{ github.actor }} NUGET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: mise run --skip-tools github-actions:nuget-authenticate + run: mise run --skip-tools github:actions:nuget-authenticate - name: Scan for Vulnerabilities - run: mise run --skip-tools github-actions:vulnerability-scan + run: mise run --skip-tools github:actions:vulnerability-scan protolock: needs: changes @@ -154,7 +154,7 @@ jobs: - name: Verify release metadata run: mise run --skip-tools release-metadata:check - name: Verify container publication tags - run: mise run --skip-tools github-actions:publish-container-test + run: mise run --skip-tools github:actions:publish-container-test archive-storage-contract: needs: changes @@ -199,35 +199,35 @@ jobs: env: NUGET_GITHUB_ACTOR: ${{ github.actor }} NUGET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: mise run --skip-tools github-actions:nuget-authenticate + run: mise run --skip-tools github:actions:nuget-authenticate - name: Run archive storage contract tests - run: mise run --skip-tools github-actions:archive-storage-contract -- contract + run: mise run --skip-tools github:actions:archive-storage-contract -- contract - name: Seed archive restart recovery data id: seed_archive_recovery env: EVENTSTORE_S3_RECOVERY_PHASE: seed - run: mise run --skip-tools github-actions:archive-storage-contract -- recovery + run: mise run --skip-tools github:actions:archive-storage-contract -- recovery - name: Stop RustFS without removing its data env: RUSTFS_CONTAINER_ID: ${{ job.services.rustfs.id }} - run: mise run --skip-tools github-actions:archive-storage-contract -- stop-storage + run: mise run --skip-tools github:actions:archive-storage-contract -- stop-storage - name: Assert archive storage is unavailable env: EVENTSTORE_S3_RECOVERY_PHASE: unavailable - run: mise run --skip-tools github-actions:archive-storage-contract -- recovery + run: mise run --skip-tools github:actions:archive-storage-contract -- recovery - name: Restart RustFS and verify archive recovery if: ${{ always() && steps.seed_archive_recovery.outcome == 'success' }} env: EVENTSTORE_S3_RECOVERY_PHASE: verify-cleanup RUSTFS_CONTAINER_ID: ${{ job.services.rustfs.id }} - run: mise run --skip-tools github-actions:archive-storage-contract -- restart-and-verify + run: mise run --skip-tools github:actions:archive-storage-contract -- restart-and-verify - name: Run archive cluster restore gate - run: mise run --skip-tools github-actions:archive-storage-contract -- cluster-restore + run: mise run --skip-tools github:actions:archive-storage-contract -- cluster-restore docker-compose: needs: changes @@ -253,7 +253,7 @@ jobs: env: DOCKER_BUILDKIT: 1 NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: mise run --skip-tools github-actions:docker-compose-smoke + run: mise run --skip-tools github:actions:docker-compose-smoke required: name: Common CI diff --git a/.github/workflows/tag-docker-images.yml b/.github/workflows/tag-docker-images.yml index 63c9bbb52..0f04f4750 100644 --- a/.github/workflows/tag-docker-images.yml +++ b/.github/workflows/tag-docker-images.yml @@ -27,7 +27,7 @@ jobs: with: name: ${{ inputs.image-artifact-name }} - name: Load Container - run: mise run --skip-tools github-actions:container -- load-runtime + run: mise run --skip-tools github:actions:container -- load-runtime - name: Login to GitHub Container Registry uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: @@ -37,4 +37,4 @@ jobs: - name: Publish Container env: IMAGE: ghcr.io/trogonstack/trogoneventstore - run: mise run --skip-tools github-actions:publish-container + run: mise run --skip-tools github:actions:publish-container From ee38a366276fa869ec34e29dbbee339cff3c24fa Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 18:29:56 -0400 Subject: [PATCH 3/8] ci: detect the change scope with a maintained filter action A bespoke diff script is ours to keep correct, and the case it has to get right is the one where guessing wrong skips the verification a change needed. Signed-off-by: Yordis Prieto --- .../mise/tasks/github/actions/changed-scope | 57 ------------------- .../workflows/build-container-ubuntu-lts.yml | 3 + .github/workflows/build-ubuntu-lts-arm64.yml | 3 + .github/workflows/build-ubuntu-lts-x64.yml | 3 + .github/workflows/changes-reusable.yml | 43 +++++++------- .github/workflows/common.yml | 3 + 6 files changed, 36 insertions(+), 76 deletions(-) delete mode 100755 .config/mise/tasks/github/actions/changed-scope diff --git a/.config/mise/tasks/github/actions/changed-scope b/.config/mise/tasks/github/actions/changed-scope deleted file mode 100755 index 555155e8f..000000000 --- a/.config/mise/tasks/github/actions/changed-scope +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Write whether the change set reaches past the documentation site to GITHUB_OUTPUT" - -set -euo pipefail - -: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" -: "${EVENT_NAME:?EVENT_NAME is required}" - -# Kept in step with the paths release-please excludes from the server release. -documentation_paths=('docs/') - -emit() { - printf 'code=%s\n' "$1" >>"$GITHUB_OUTPUT" - echo "code=$1" - exit 0 -} - -if [ "$EVENT_NAME" != "pull_request" ] && [ "$EVENT_NAME" != "merge_group" ]; then - echo "Event '$EVENT_NAME' always exercises the full pipeline." - emit true -fi - -: "${BASE_REF:?BASE_REF is required}" -head_ref="${HEAD_REF:-HEAD}" - -if [[ "$BASE_REF" =~ ^0+$ ]]; then - echo "The base ref is unresolved." - emit true -fi - -comparison_base="$BASE_REF" -if merge_base="$(git merge-base "$BASE_REF" "$head_ref" 2>/dev/null)"; then - comparison_base="$merge_base" -fi - -if ! changed_files="$(git diff --name-only --diff-filter=ACMRD "$comparison_base" "$head_ref")"; then - echo "The change set could not be resolved." - emit true -fi - -if [ -z "$changed_files" ]; then - echo "The change set is empty." - emit true -fi - -while IFS= read -r file; do - for prefix in "${documentation_paths[@]}"; do - if [[ "$file" == "$prefix"* ]]; then - continue 2 - fi - done - echo "Change outside the documentation site: $file" - emit true -done <<<"$changed_files" - -echo "Documentation-only change set." -emit false diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index 585830e98..bef68a5e3 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -19,6 +19,9 @@ concurrency: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml build-images: diff --git a/.github/workflows/build-ubuntu-lts-arm64.yml b/.github/workflows/build-ubuntu-lts-arm64.yml index 44c0e1158..6a79d3d37 100644 --- a/.github/workflows/build-ubuntu-lts-arm64.yml +++ b/.github/workflows/build-ubuntu-lts-arm64.yml @@ -19,6 +19,9 @@ concurrency: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/build-ubuntu-lts-x64.yml b/.github/workflows/build-ubuntu-lts-x64.yml index dba47b25b..d0ce3510b 100644 --- a/.github/workflows/build-ubuntu-lts-x64.yml +++ b/.github/workflows/build-ubuntu-lts-x64.yml @@ -15,6 +15,9 @@ on: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/changes-reusable.yml b/.github/workflows/changes-reusable.yml index ee958997e..c8b05f57c 100644 --- a/.github/workflows/changes-reusable.yml +++ b/.github/workflows/changes-reusable.yml @@ -9,6 +9,7 @@ on: permissions: contents: read + pull-requests: read jobs: detect: @@ -17,32 +18,36 @@ jobs: outputs: code: ${{ steps.scope.outputs.code }} steps: + # A pull request resolves through the API, so only a merge group needs the + # working tree the filter diffs against. - name: Checkout + if: ${{ github.event_name == 'merge_group' }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false - - name: Install task runner - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 + - name: Filter paths + id: filter + if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 with: - version: 2026.8.2 - install: false - cache: false - - name: Resolve comparison refs - id: refs - env: - EVENT_NAME: ${{ github.event_name }} - BASE_SHA: ${{ github.event.pull_request.base.sha }} - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} - MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} - BEFORE_SHA: ${{ github.event.before }} - CURRENT_SHA: ${{ github.sha }} - run: mise run --skip-tools github:actions:resolve-comparison-refs + # A file counts when it matches a pattern and no negated one, so the + # filter reads as "anything that is not the documentation site". + # Negated patterns alone would never match a thing. + predicate-quantifier: some-with-excludes + filters: | + code: + - '**' + - '!docs/**' - name: Resolve change scope id: scope env: EVENT_NAME: ${{ github.event_name }} - BASE_REF: ${{ steps.refs.outputs.base }} - HEAD_REF: ${{ steps.refs.outputs.head }} - run: mise run --skip-tools github:actions:changed-scope + FILTERED_CODE: ${{ steps.filter.outputs.code }} + run: | + case "$EVENT_NAME" in + pull_request | merge_group) code=$FILTERED_CODE ;; + *) code=true ;; + esac + printf 'code=%s\n' "$code" >>"$GITHUB_OUTPUT" + echo "code=$code" diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 35628b70d..edc74d257 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -24,6 +24,9 @@ permissions: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml semantic-conventions: From 8d91311b9c25a218e91c5edc51dd59b036f3b866 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 19:39:58 -0400 Subject: [PATCH 4/8] Revert "ci: detect the change scope with a maintained filter action" This reverts commit ee38a366276fa869ec34e29dbbee339cff3c24fa. Signed-off-by: Yordis Prieto --- .../mise/tasks/github/actions/changed-scope | 57 +++++++++++++++++++ .../workflows/build-container-ubuntu-lts.yml | 3 - .github/workflows/build-ubuntu-lts-arm64.yml | 3 - .github/workflows/build-ubuntu-lts-x64.yml | 3 - .github/workflows/changes-reusable.yml | 43 +++++++------- .github/workflows/common.yml | 3 - 6 files changed, 76 insertions(+), 36 deletions(-) create mode 100755 .config/mise/tasks/github/actions/changed-scope diff --git a/.config/mise/tasks/github/actions/changed-scope b/.config/mise/tasks/github/actions/changed-scope new file mode 100755 index 000000000..555155e8f --- /dev/null +++ b/.config/mise/tasks/github/actions/changed-scope @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +#MISE description="Write whether the change set reaches past the documentation site to GITHUB_OUTPUT" + +set -euo pipefail + +: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" +: "${EVENT_NAME:?EVENT_NAME is required}" + +# Kept in step with the paths release-please excludes from the server release. +documentation_paths=('docs/') + +emit() { + printf 'code=%s\n' "$1" >>"$GITHUB_OUTPUT" + echo "code=$1" + exit 0 +} + +if [ "$EVENT_NAME" != "pull_request" ] && [ "$EVENT_NAME" != "merge_group" ]; then + echo "Event '$EVENT_NAME' always exercises the full pipeline." + emit true +fi + +: "${BASE_REF:?BASE_REF is required}" +head_ref="${HEAD_REF:-HEAD}" + +if [[ "$BASE_REF" =~ ^0+$ ]]; then + echo "The base ref is unresolved." + emit true +fi + +comparison_base="$BASE_REF" +if merge_base="$(git merge-base "$BASE_REF" "$head_ref" 2>/dev/null)"; then + comparison_base="$merge_base" +fi + +if ! changed_files="$(git diff --name-only --diff-filter=ACMRD "$comparison_base" "$head_ref")"; then + echo "The change set could not be resolved." + emit true +fi + +if [ -z "$changed_files" ]; then + echo "The change set is empty." + emit true +fi + +while IFS= read -r file; do + for prefix in "${documentation_paths[@]}"; do + if [[ "$file" == "$prefix"* ]]; then + continue 2 + fi + done + echo "Change outside the documentation site: $file" + emit true +done <<<"$changed_files" + +echo "Documentation-only change set." +emit false diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index bef68a5e3..585830e98 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -19,9 +19,6 @@ concurrency: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml build-images: diff --git a/.github/workflows/build-ubuntu-lts-arm64.yml b/.github/workflows/build-ubuntu-lts-arm64.yml index 6a79d3d37..44c0e1158 100644 --- a/.github/workflows/build-ubuntu-lts-arm64.yml +++ b/.github/workflows/build-ubuntu-lts-arm64.yml @@ -19,9 +19,6 @@ concurrency: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/build-ubuntu-lts-x64.yml b/.github/workflows/build-ubuntu-lts-x64.yml index d0ce3510b..dba47b25b 100644 --- a/.github/workflows/build-ubuntu-lts-x64.yml +++ b/.github/workflows/build-ubuntu-lts-x64.yml @@ -15,9 +15,6 @@ on: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/changes-reusable.yml b/.github/workflows/changes-reusable.yml index c8b05f57c..ee958997e 100644 --- a/.github/workflows/changes-reusable.yml +++ b/.github/workflows/changes-reusable.yml @@ -9,7 +9,6 @@ on: permissions: contents: read - pull-requests: read jobs: detect: @@ -18,36 +17,32 @@ jobs: outputs: code: ${{ steps.scope.outputs.code }} steps: - # A pull request resolves through the API, so only a merge group needs the - # working tree the filter diffs against. - name: Checkout - if: ${{ github.event_name == 'merge_group' }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false - - name: Filter paths - id: filter - if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 + - name: Install task runner + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: - # A file counts when it matches a pattern and no negated one, so the - # filter reads as "anything that is not the documentation site". - # Negated patterns alone would never match a thing. - predicate-quantifier: some-with-excludes - filters: | - code: - - '**' - - '!docs/**' + version: 2026.8.2 + install: false + cache: false + - name: Resolve comparison refs + id: refs + env: + EVENT_NAME: ${{ github.event_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} + MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} + BEFORE_SHA: ${{ github.event.before }} + CURRENT_SHA: ${{ github.sha }} + run: mise run --skip-tools github:actions:resolve-comparison-refs - name: Resolve change scope id: scope env: EVENT_NAME: ${{ github.event_name }} - FILTERED_CODE: ${{ steps.filter.outputs.code }} - run: | - case "$EVENT_NAME" in - pull_request | merge_group) code=$FILTERED_CODE ;; - *) code=true ;; - esac - printf 'code=%s\n' "$code" >>"$GITHUB_OUTPUT" - echo "code=$code" + BASE_REF: ${{ steps.refs.outputs.base }} + HEAD_REF: ${{ steps.refs.outputs.head }} + run: mise run --skip-tools github:actions:changed-scope diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index edc74d257..35628b70d 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -24,9 +24,6 @@ permissions: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml semantic-conventions: From a98383d76b310bb3d06dfd58ea769291e92ab5c9 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 19:51:22 -0400 Subject: [PATCH 5/8] Reapply "ci: detect the change scope with a maintained filter action" This reverts commit 8d91311b9c25a218e91c5edc51dd59b036f3b866. Signed-off-by: Yordis Prieto --- .../mise/tasks/github/actions/changed-scope | 57 ------------------- .../workflows/build-container-ubuntu-lts.yml | 3 + .github/workflows/build-ubuntu-lts-arm64.yml | 3 + .github/workflows/build-ubuntu-lts-x64.yml | 3 + .github/workflows/changes-reusable.yml | 43 +++++++------- .github/workflows/common.yml | 3 + 6 files changed, 36 insertions(+), 76 deletions(-) delete mode 100755 .config/mise/tasks/github/actions/changed-scope diff --git a/.config/mise/tasks/github/actions/changed-scope b/.config/mise/tasks/github/actions/changed-scope deleted file mode 100755 index 555155e8f..000000000 --- a/.config/mise/tasks/github/actions/changed-scope +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Write whether the change set reaches past the documentation site to GITHUB_OUTPUT" - -set -euo pipefail - -: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" -: "${EVENT_NAME:?EVENT_NAME is required}" - -# Kept in step with the paths release-please excludes from the server release. -documentation_paths=('docs/') - -emit() { - printf 'code=%s\n' "$1" >>"$GITHUB_OUTPUT" - echo "code=$1" - exit 0 -} - -if [ "$EVENT_NAME" != "pull_request" ] && [ "$EVENT_NAME" != "merge_group" ]; then - echo "Event '$EVENT_NAME' always exercises the full pipeline." - emit true -fi - -: "${BASE_REF:?BASE_REF is required}" -head_ref="${HEAD_REF:-HEAD}" - -if [[ "$BASE_REF" =~ ^0+$ ]]; then - echo "The base ref is unresolved." - emit true -fi - -comparison_base="$BASE_REF" -if merge_base="$(git merge-base "$BASE_REF" "$head_ref" 2>/dev/null)"; then - comparison_base="$merge_base" -fi - -if ! changed_files="$(git diff --name-only --diff-filter=ACMRD "$comparison_base" "$head_ref")"; then - echo "The change set could not be resolved." - emit true -fi - -if [ -z "$changed_files" ]; then - echo "The change set is empty." - emit true -fi - -while IFS= read -r file; do - for prefix in "${documentation_paths[@]}"; do - if [[ "$file" == "$prefix"* ]]; then - continue 2 - fi - done - echo "Change outside the documentation site: $file" - emit true -done <<<"$changed_files" - -echo "Documentation-only change set." -emit false diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index 585830e98..bef68a5e3 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -19,6 +19,9 @@ concurrency: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml build-images: diff --git a/.github/workflows/build-ubuntu-lts-arm64.yml b/.github/workflows/build-ubuntu-lts-arm64.yml index 44c0e1158..6a79d3d37 100644 --- a/.github/workflows/build-ubuntu-lts-arm64.yml +++ b/.github/workflows/build-ubuntu-lts-arm64.yml @@ -19,6 +19,9 @@ concurrency: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/build-ubuntu-lts-x64.yml b/.github/workflows/build-ubuntu-lts-x64.yml index dba47b25b..d0ce3510b 100644 --- a/.github/workflows/build-ubuntu-lts-x64.yml +++ b/.github/workflows/build-ubuntu-lts-x64.yml @@ -15,6 +15,9 @@ on: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/changes-reusable.yml b/.github/workflows/changes-reusable.yml index ee958997e..c8b05f57c 100644 --- a/.github/workflows/changes-reusable.yml +++ b/.github/workflows/changes-reusable.yml @@ -9,6 +9,7 @@ on: permissions: contents: read + pull-requests: read jobs: detect: @@ -17,32 +18,36 @@ jobs: outputs: code: ${{ steps.scope.outputs.code }} steps: + # A pull request resolves through the API, so only a merge group needs the + # working tree the filter diffs against. - name: Checkout + if: ${{ github.event_name == 'merge_group' }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false - - name: Install task runner - uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 + - name: Filter paths + id: filter + if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 with: - version: 2026.8.2 - install: false - cache: false - - name: Resolve comparison refs - id: refs - env: - EVENT_NAME: ${{ github.event_name }} - BASE_SHA: ${{ github.event.pull_request.base.sha }} - HEAD_SHA: ${{ github.event.pull_request.head.sha }} - MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} - MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} - BEFORE_SHA: ${{ github.event.before }} - CURRENT_SHA: ${{ github.sha }} - run: mise run --skip-tools github:actions:resolve-comparison-refs + # A file counts when it matches a pattern and no negated one, so the + # filter reads as "anything that is not the documentation site". + # Negated patterns alone would never match a thing. + predicate-quantifier: some-with-excludes + filters: | + code: + - '**' + - '!docs/**' - name: Resolve change scope id: scope env: EVENT_NAME: ${{ github.event_name }} - BASE_REF: ${{ steps.refs.outputs.base }} - HEAD_REF: ${{ steps.refs.outputs.head }} - run: mise run --skip-tools github:actions:changed-scope + FILTERED_CODE: ${{ steps.filter.outputs.code }} + run: | + case "$EVENT_NAME" in + pull_request | merge_group) code=$FILTERED_CODE ;; + *) code=true ;; + esac + printf 'code=%s\n' "$code" >>"$GITHUB_OUTPUT" + echo "code=$code" diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 35628b70d..edc74d257 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -24,6 +24,9 @@ permissions: jobs: changes: name: Changes + permissions: + contents: read + pull-requests: read uses: ./.github/workflows/changes-reusable.yml semantic-conventions: From 630933032484e9c5e230ba59d1f623a7febd348f Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 19:52:09 -0400 Subject: [PATCH 6/8] Revert "Reapply "ci: detect the change scope with a maintained filter action"" This reverts commit a98383d76b310bb3d06dfd58ea769291e92ab5c9. Signed-off-by: Yordis Prieto --- .../mise/tasks/github/actions/changed-scope | 57 +++++++++++++++++++ .../workflows/build-container-ubuntu-lts.yml | 3 - .github/workflows/build-ubuntu-lts-arm64.yml | 3 - .github/workflows/build-ubuntu-lts-x64.yml | 3 - .github/workflows/changes-reusable.yml | 43 +++++++------- .github/workflows/common.yml | 3 - 6 files changed, 76 insertions(+), 36 deletions(-) create mode 100755 .config/mise/tasks/github/actions/changed-scope diff --git a/.config/mise/tasks/github/actions/changed-scope b/.config/mise/tasks/github/actions/changed-scope new file mode 100755 index 000000000..555155e8f --- /dev/null +++ b/.config/mise/tasks/github/actions/changed-scope @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +#MISE description="Write whether the change set reaches past the documentation site to GITHUB_OUTPUT" + +set -euo pipefail + +: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" +: "${EVENT_NAME:?EVENT_NAME is required}" + +# Kept in step with the paths release-please excludes from the server release. +documentation_paths=('docs/') + +emit() { + printf 'code=%s\n' "$1" >>"$GITHUB_OUTPUT" + echo "code=$1" + exit 0 +} + +if [ "$EVENT_NAME" != "pull_request" ] && [ "$EVENT_NAME" != "merge_group" ]; then + echo "Event '$EVENT_NAME' always exercises the full pipeline." + emit true +fi + +: "${BASE_REF:?BASE_REF is required}" +head_ref="${HEAD_REF:-HEAD}" + +if [[ "$BASE_REF" =~ ^0+$ ]]; then + echo "The base ref is unresolved." + emit true +fi + +comparison_base="$BASE_REF" +if merge_base="$(git merge-base "$BASE_REF" "$head_ref" 2>/dev/null)"; then + comparison_base="$merge_base" +fi + +if ! changed_files="$(git diff --name-only --diff-filter=ACMRD "$comparison_base" "$head_ref")"; then + echo "The change set could not be resolved." + emit true +fi + +if [ -z "$changed_files" ]; then + echo "The change set is empty." + emit true +fi + +while IFS= read -r file; do + for prefix in "${documentation_paths[@]}"; do + if [[ "$file" == "$prefix"* ]]; then + continue 2 + fi + done + echo "Change outside the documentation site: $file" + emit true +done <<<"$changed_files" + +echo "Documentation-only change set." +emit false diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index bef68a5e3..585830e98 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -19,9 +19,6 @@ concurrency: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml build-images: diff --git a/.github/workflows/build-ubuntu-lts-arm64.yml b/.github/workflows/build-ubuntu-lts-arm64.yml index 6a79d3d37..44c0e1158 100644 --- a/.github/workflows/build-ubuntu-lts-arm64.yml +++ b/.github/workflows/build-ubuntu-lts-arm64.yml @@ -19,9 +19,6 @@ concurrency: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/build-ubuntu-lts-x64.yml b/.github/workflows/build-ubuntu-lts-x64.yml index d0ce3510b..dba47b25b 100644 --- a/.github/workflows/build-ubuntu-lts-x64.yml +++ b/.github/workflows/build-ubuntu-lts-x64.yml @@ -15,9 +15,6 @@ on: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml build: diff --git a/.github/workflows/changes-reusable.yml b/.github/workflows/changes-reusable.yml index c8b05f57c..ee958997e 100644 --- a/.github/workflows/changes-reusable.yml +++ b/.github/workflows/changes-reusable.yml @@ -9,7 +9,6 @@ on: permissions: contents: read - pull-requests: read jobs: detect: @@ -18,36 +17,32 @@ jobs: outputs: code: ${{ steps.scope.outputs.code }} steps: - # A pull request resolves through the API, so only a merge group needs the - # working tree the filter diffs against. - name: Checkout - if: ${{ github.event_name == 'merge_group' }} uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false - - name: Filter paths - id: filter - if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 + - name: Install task runner + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: - # A file counts when it matches a pattern and no negated one, so the - # filter reads as "anything that is not the documentation site". - # Negated patterns alone would never match a thing. - predicate-quantifier: some-with-excludes - filters: | - code: - - '**' - - '!docs/**' + version: 2026.8.2 + install: false + cache: false + - name: Resolve comparison refs + id: refs + env: + EVENT_NAME: ${{ github.event_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }} + MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }} + BEFORE_SHA: ${{ github.event.before }} + CURRENT_SHA: ${{ github.sha }} + run: mise run --skip-tools github:actions:resolve-comparison-refs - name: Resolve change scope id: scope env: EVENT_NAME: ${{ github.event_name }} - FILTERED_CODE: ${{ steps.filter.outputs.code }} - run: | - case "$EVENT_NAME" in - pull_request | merge_group) code=$FILTERED_CODE ;; - *) code=true ;; - esac - printf 'code=%s\n' "$code" >>"$GITHUB_OUTPUT" - echo "code=$code" + BASE_REF: ${{ steps.refs.outputs.base }} + HEAD_REF: ${{ steps.refs.outputs.head }} + run: mise run --skip-tools github:actions:changed-scope diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index edc74d257..35628b70d 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -24,9 +24,6 @@ permissions: jobs: changes: name: Changes - permissions: - contents: read - pull-requests: read uses: ./.github/workflows/changes-reusable.yml semantic-conventions: From b2e3b49753fd817265899d267c7a24da48600e17 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 21:19:26 -0400 Subject: [PATCH 7/8] fix(ci): keep the change scope honest about every kind of edit A type change, or a file moved between the documentation site and the rest of the tree, could hide code work behind a documentation-only verdict. Signed-off-by: Yordis Prieto --- .config/mise/tasks/github/actions/changed-scope | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/mise/tasks/github/actions/changed-scope b/.config/mise/tasks/github/actions/changed-scope index 555155e8f..d1d944b20 100755 --- a/.config/mise/tasks/github/actions/changed-scope +++ b/.config/mise/tasks/github/actions/changed-scope @@ -33,7 +33,7 @@ if merge_base="$(git merge-base "$BASE_REF" "$head_ref" 2>/dev/null)"; then comparison_base="$merge_base" fi -if ! changed_files="$(git diff --name-only --diff-filter=ACMRD "$comparison_base" "$head_ref")"; then +if ! changed_files="$(git diff --name-only --no-renames "$comparison_base" "$head_ref")"; then echo "The change set could not be resolved." emit true fi From 269889214c264b0ac4b3f685934943356a2c512f Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Tue, 15 Sep 2026 21:19:26 -0400 Subject: [PATCH 8/8] refactor(ci): move the aggregate gate into the task runner The check belongs with the rest of the workflow logic, where it can be read and exercised outside GitHub. Signed-off-by: Yordis Prieto --- .../github/actions/require-successful-jobs | 29 +++++++++++++++++++ .../workflows/build-container-ubuntu-lts.yml | 20 ++++++++----- .github/workflows/build-ubuntu-lts-arm64.yml | 20 ++++++++----- .github/workflows/build-ubuntu-lts-x64.yml | 20 ++++++++----- .github/workflows/common.yml | 20 ++++++++----- 5 files changed, 81 insertions(+), 28 deletions(-) create mode 100755 .config/mise/tasks/github/actions/require-successful-jobs diff --git a/.config/mise/tasks/github/actions/require-successful-jobs b/.config/mise/tasks/github/actions/require-successful-jobs new file mode 100755 index 000000000..e83fa7f8e --- /dev/null +++ b/.config/mise/tasks/github/actions/require-successful-jobs @@ -0,0 +1,29 @@ +#!/bin/sh +#MISE description="Fail unless every gated job reached the result the change scope calls for" + +set -eu + +: "${REQUIRED_JOBS:?REQUIRED_JOBS is required}" + +expected=skipped +if [ "$(printf '%s' "$REQUIRED_JOBS" | jq -r '.changes.outputs.code')" = "true" ]; then + expected=success +fi + +deviations="$(printf '%s' "$REQUIRED_JOBS" | jq -r --arg expected "$expected" ' + to_entries + | map(select( + (.key == "changes" and .value.result != "success") + or (.key != "changes" and .value.result != $expected) + )) + | .[] + | "\(.key): \(.value.result)" +')" + +if [ -n "$deviations" ]; then + echo "Jobs did not reach '$expected':" >&2 + echo "$deviations" >&2 + exit 1 +fi + +echo "Every gated job reached '$expected'." diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index 585830e98..b87f61c34 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -153,14 +153,20 @@ jobs: - test - client-compatibility runs-on: ubuntu-latest - permissions: {} + permissions: + contents: read steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install task runner + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 + with: + version: 2026.8.2 + install: false + cache: false - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} - run: | - jq -e --arg expected "$EXPECTED_RESULT" ' - .changes.result == "success" and - (del(.changes) | all(.[]; .result == $expected)) - ' <<< "$REQUIRED_JOBS" + run: mise run --skip-tools github:actions:require-successful-jobs diff --git a/.github/workflows/build-ubuntu-lts-arm64.yml b/.github/workflows/build-ubuntu-lts-arm64.yml index 44c0e1158..7dc5ec5cd 100644 --- a/.github/workflows/build-ubuntu-lts-arm64.yml +++ b/.github/workflows/build-ubuntu-lts-arm64.yml @@ -38,14 +38,20 @@ jobs: - changes - build runs-on: ubuntu-latest - permissions: {} + permissions: + contents: read steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install task runner + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 + with: + version: 2026.8.2 + install: false + cache: false - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} - run: | - jq -e --arg expected "$EXPECTED_RESULT" ' - .changes.result == "success" and - (del(.changes) | all(.[]; .result == $expected)) - ' <<< "$REQUIRED_JOBS" + run: mise run --skip-tools github:actions:require-successful-jobs diff --git a/.github/workflows/build-ubuntu-lts-x64.yml b/.github/workflows/build-ubuntu-lts-x64.yml index dba47b25b..4f9f1b9e1 100644 --- a/.github/workflows/build-ubuntu-lts-x64.yml +++ b/.github/workflows/build-ubuntu-lts-x64.yml @@ -34,14 +34,20 @@ jobs: - changes - build runs-on: ubuntu-latest - permissions: {} + permissions: + contents: read steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install task runner + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 + with: + version: 2026.8.2 + install: false + cache: false - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} - run: | - jq -e --arg expected "$EXPECTED_RESULT" ' - .changes.result == "success" and - (del(.changes) | all(.[]; .result == $expected)) - ' <<< "$REQUIRED_JOBS" + run: mise run --skip-tools github:actions:require-successful-jobs diff --git a/.github/workflows/common.yml b/.github/workflows/common.yml index 35628b70d..092a9a5a8 100644 --- a/.github/workflows/common.yml +++ b/.github/workflows/common.yml @@ -268,14 +268,20 @@ jobs: - archive-storage-contract - docker-compose runs-on: ubuntu-latest - permissions: {} + permissions: + contents: read steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install task runner + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 + with: + version: 2026.8.2 + install: false + cache: false - name: Require successful jobs env: REQUIRED_JOBS: ${{ toJSON(needs) }} - EXPECTED_RESULT: ${{ needs.changes.outputs.code == 'true' && 'success' || 'skipped' }} - run: | - jq -e --arg expected "$EXPECTED_RESULT" ' - .changes.result == "success" and - (del(.changes) | all(.[]; .result == $expected)) - ' <<< "$REQUIRED_JOBS" + run: mise run --skip-tools github:actions:require-successful-jobs