From dbf7727e16ca64ccf1eebedd3afd2f38cd4ed1df Mon Sep 17 00:00:00 2001 From: Joseph Bernstein Date: Sun, 11 Oct 2026 18:01:46 +0000 Subject: [PATCH 1/2] CI: drop merge_group trigger; the fork has no merge queue CI runs on pull requests and pushes to main. The aggregate CI check is the required status for main. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb30a97..3010fe2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,8 +2,6 @@ name: CI on: pull_request: - merge_group: - types: [checks_requested] push: branches: [main] @@ -67,9 +65,9 @@ jobs: GOARCH: amd64 run: go build -trimpath -o vprox . - # The single check to require in branch protection and the merge queue. It - # runs even when a dependency fails or is cancelled, so a broken job turns it - # red instead of leaving the required check pending forever. + # The single required status check for main. It runs even when a dependency + # fails or is cancelled, so a broken job turns it red instead of leaving the + # required check pending forever. ci: name: CI if: always() From c1b117becc82bbc01ab59b185fde39dce43eb46e Mon Sep 17 00:00:00 2001 From: Joseph Bernstein Date: Sun, 11 Oct 2026 18:04:21 +0000 Subject: [PATCH 2/2] CI: publish commit-tagged releases of main After CI passes on a main push, publish the binaries the CI build jobs produced (linux/amd64 and linux/arm64) with SHA256SUMS as a release tagged sha-. Releases are never marked latest, so consumers pin an exact commit. Main runs get per-commit concurrency groups so no merged commit is skipped. Nothing is deployed. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 63 ++++++++++++++++++++++++++++++++++++---- README.md | 10 +++++++ 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3010fe2..92ec76e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,9 +8,11 @@ on: permissions: contents: read +# Pull requests cancel superseded runs; every main commit gets its own group so +# no merged commit's run is cancelled or skipped before it can publish. concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} + group: ci-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true jobs: fmt: @@ -51,19 +53,31 @@ jobs: - run: go test -race -count=1 ./... build: - name: Linux build + name: Linux build (${{ matrix.goarch }}) runs-on: ubuntu-latest + strategy: + matrix: + goarch: [amd64, arm64] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod - - name: Build linux/amd64 + - name: Build env: CGO_ENABLED: "0" GOOS: linux - GOARCH: amd64 - run: go build -trimpath -o vprox . + GOARCH: ${{ matrix.goarch }} + run: | + go build -trimpath -o "vprox-linux-$GOARCH" . + go version -m "vprox-linux-$GOARCH" + # The release publishes these exact binaries; nothing is rebuilt later. + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: vprox-linux-${{ matrix.goarch }} + path: vprox-linux-${{ matrix.goarch }} + if-no-files-found: error + retention-days: 7 # The single required status check for main. It runs even when a dependency # fails or is cancelled, so a broken job turns it red instead of leaving the @@ -82,3 +96,40 @@ jobs: for result in $RESULTS; do [ "$result" = success ] || exit 1 done + + # Publishes each merged main commit, after CI passes, as an immutable release + # tagged sha-. Consumers pin that tag and verify SHA256SUMS; there is + # deliberately no "latest" to follow. Nothing is deployed from here. + publish: + name: Publish release + needs: ci + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + pattern: vprox-linux-* + merge-multiple: true + path: dist + - name: Write checksums + working-directory: dist + run: sha256sum vprox-linux-amd64 vprox-linux-arm64 | tee SHA256SUMS + - name: Create release + env: + GH_TOKEN: ${{ github.token }} + TAG: sha-${{ github.sha }} + run: | + { + echo "vprox built from https://github.com/$GITHUB_REPOSITORY/commit/$GITHUB_SHA" + echo + echo "Verify downloads with \`sha256sum -c SHA256SUMS\`:" + echo + echo '```' + cat dist/SHA256SUMS + echo '```' + } > notes.md + gh release create "$TAG" dist/vprox-linux-amd64 dist/vprox-linux-arm64 dist/SHA256SUMS \ + --repo "$GITHUB_REPOSITORY" --target "$GITHUB_SHA" --title "$TAG" \ + --notes-file notes.md --latest=false diff --git a/README.md b/README.md index 2cd9860..4205699 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,16 @@ CGO_ENABLED=0 go build This produces a static binary in `./vprox`. +### Releases + +CI publishes every commit merged to `main`, once its checks pass, as a GitHub release tagged `sha-`. Each release contains static `vprox-linux-amd64` and `vprox-linux-arm64` binaries, built by CI from exactly that commit, plus a `SHA256SUMS` file. Releases are never marked latest. Pin an exact tag and verify the download before running it: + +```bash +TAG=sha- +gh release download "$TAG" --repo prassoai/vprox --pattern 'vprox-linux-amd64' --pattern SHA256SUMS +sha256sum -c --ignore-missing SHA256SUMS +``` + ### Multiple private IPs On cloud providers like AWS, you can attach [secondary private IP addresses](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/MultipleIP.html) to an interface and associate each of them with a global IPv4 unicast address.