From 3ba02e3324981629a32f2db8ef94db3a4518847a Mon Sep 17 00:00:00 2001 From: Jens Drenhaus Date: Thu, 10 Jul 2025 15:21:07 +0200 Subject: [PATCH] ci: build and test per commit in PRs To ensure a consistent and stable tree, the CI pipeline builds and tests every commit in a PR. For pushes to main only the final commit is considered for performance reasons. The requirement that every commit in the tree builds prepares for easy use of e.g. git bisect when finding bugs. Further, the tests for different architectures are also integrated in the introduced matrix strategy. Signed-off-by: Jens Drenhaus --- .github/workflows/go.yml | 118 ++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 44 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e1c72da5..6c6172c6 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,4 +1,5 @@ -# This workflow will build a golang project +# This workflow builds and tests a golang project for every commit in a PR +# # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go name: Go @@ -15,6 +16,51 @@ permissions: # pull-requests: read jobs: + # This job identifies all commits that need to be tested + # For push events: only tests the latest commit + # For PR events: tests every commit in the PR + commit_list: + name: Get Commits to Test + runs-on: ubuntu-latest + steps: + # Checkout with full history to enable commit listing + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # For push events: only test the latest commit (GITHUB_SHA) + - name: Get commit list (push) + id: get_commit_list_push + if: ${{ github.event_name == 'push' }} + run: | + # Create a single output variable with the latest commit + echo "id0=$GITHUB_SHA" > $GITHUB_OUTPUT + # Add commit URL to the workflow summary for visibility + echo "List of tested commits:" > $GITHUB_STEP_SUMMARY + echo "- https://github.com/${{ github.repository }}/commit/$GITHUB_SHA" >> $GITHUB_STEP_SUMMARY + + # For PR events: get all commits in the PR + - name: Get commit list (PR) + id: get_commit_list_pr + if: ${{ github.event_name == 'pull_request' }} + run: | + # Get all commits between base branch and PR head, in oldest to newest order + git rev-list --reverse refs/remotes/origin/${{ github.base_ref }}..${{ github.event.pull_request.head.sha }} | + awk '{ print "id" NR "=" $1 }' > $GITHUB_OUTPUT + + # If the merge commit differs from the PR head, also test the merge commit + git diff --quiet ${{ github.event.pull_request.head.sha }} ${{ github.sha }} || + echo "id0=$GITHUB_SHA" >> $GITHUB_OUTPUT + + # Create a summary with links to all commits + echo "List of tested commits:" > $GITHUB_STEP_SUMMARY + sed -n 's,^id[0-9]\+=\(.*\),- https://github.com/${{ github.repository }}/commit/\1,p' -- $GITHUB_OUTPUT >> $GITHUB_STEP_SUMMARY + + # Make the list of commits available to downstream jobs + outputs: + commits: ${{ toJSON(steps.*.outputs.*) }} + + # Lint job - runs once per workflow execution, not per commit golangci-lint: name: Lint runs-on: ubuntu-latest @@ -33,55 +79,39 @@ jobs: with: version: v1.64.8 - build_amd64: - name: Build AMD64 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: 'go.mod' - - - run: go version - - - name: Build for amd64 - run: GOARCH=amd64 go build -v ./... - - build_arm64: - name: Build ARM64 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: 'go.mod' - - - run: go version - - - name: Build for arm64 - run: GOARCH=arm64 go build -v ./... - - test: - name: Unit Tests + # Build and test each commit for both architectures + # Uses a matrix strategy to create separate job runs for each combination of commit and architecture + per_commit_build_test: + name: Build & Test ${{ matrix.arch }} runs-on: ubuntu-latest - needs: build_amd64 + needs: commit_list + strategy: + # Don't stop all tests if one commit/arch fails + fail-fast: false + matrix: + # Create matrix entries for each combination of commit and architecture + commit: ${{ fromJSON(needs.commit_list.outputs.commits) }} + arch: [amd64, arm64] steps: - - uses: actions/checkout@v4 + # Checkout the specific commit to test + - uses: actions/checkout@v4 + with: + ref: ${{ matrix.commit }} - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: 'go.mod' + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' - - run: go version + - run: go version - - name: Run Tests - run: go test -v ./... + - name: Build ${{ matrix.commit }} for ${{ matrix.arch }} + run: GOARCH=${{ matrix.arch }} go build -v ./... + + - name: Unit test ${{ matrix.commit }} for ${{ matrix.arch }} + run: go test -v ./... + # License check runs once per workflow execution check-license-lines: name: Check License Lines runs-on: ubuntu-latest