From b50ecc62bccc97d0670b52233fe17467ccd1a482 Mon Sep 17 00:00:00 2001 From: across Date: Sun, 27 Sep 2026 04:40:50 +0530 Subject: [PATCH 1/2] ci: add draft tag-triggered release workflow Owner's draft from the phase-zero-trust working tree, committed as written so the follow-up fix is reviewable against it. --- .github/workflows/release.yml | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5168f41 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,63 @@ +name: Release + +on: + push: + tags: ['v*'] + +permissions: + contents: write + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.24' + cache: true + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Build binaries + shell: bash + run: | + set -euo pipefail + mkdir -p dist + ext="" + if [ "$RUNNER_OS" = "Windows" ]; then ext=".exe"; fi + go build -trimpath -o "dist/across${ext}" ./cmd/across + for agent in claude-code codex cursor gemini opencode qwen factory-droid amp goose; do + go build -trimpath -o "dist/across-agent-${agent}${ext}" "./cmd/across-agent-${agent}" + done + sha256sum dist/* > dist/SHA256SUMS + go list -m -json all > dist/go-modules.json + - uses: anchore/sbom-action@v0 + with: + path: . + format: spdx-json + output-file: dist/sbom.spdx.json + - uses: actions/upload-artifact@v4 + with: + name: across-${{ matrix.os }} + path: dist + if-no-files-found: error + + publish: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + pattern: across-* + merge-multiple: true + - uses: softprops/action-gh-release@v2 + with: + files: | + dist/** + generate_release_notes: true From 3daa4d56fe5a1f4fa96a8647fb0f5d805100a44d Mon Sep 17 00:00:00 2001 From: across Date: Sun, 27 Sep 2026 04:40:50 +0530 Subject: [PATCH 2/2] fix(ci): publish per-target release archives with one checksum file The draft release workflow built identically named binaries, SHA256SUMS and SBOMs on three runners and merged them into one directory, so the release would have carried one unlabeled binary of unknown OS and a checksum file that did not match the attachments. It also ran with a workflow-wide contents:write token and mutable action tags, including two third-party actions. This version: - verifies the tag equals VERSION and `across version` before building - runs the test suite, then builds and packages one archive per tested target (across___.tar.gz for linux/amd64 and darwin/arm64); Windows is compile-only in CI and is not released - computes a single SHA256SUMS in the publish job over the archives, the module inventory and one SPDX SBOM, and attests build provenance for exactly those subjects - scopes contents:write/id-token/attestations to the publish job, pins every action to a commit SHA, pins syft, disables the SBOM action's own uploads, and checks out without persisted credentials - creates a draft pre-release so a maintainer reviews it before publishing --- .github/workflows/release.yml | 139 +++++++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 29 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5168f41..0997f39 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,63 +1,144 @@ name: Release +# Runs on a pushed v* tag and creates a DRAFT pre-release. A maintainer +# reviews the attached archives, SHA256SUMS and provenance attestation before +# publishing it. Only targets that CI tests are built; Windows is compile-only +# in CI and is not released. + on: push: tags: ['v*'] permissions: - contents: write + contents: read + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +env: + GO_VERSION: "1.26.6" + GOTOOLCHAIN: local + GOWORK: "off" jobs: build: strategy: - fail-fast: false + fail-fast: true matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + include: + - os: ubuntu-latest + goos: linux + goarch: amd64 + - os: macos-latest + goos: darwin + goarch: arm64 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - go-version: '1.24' - cache: true - - uses: actions/setup-node@v4 + persist-credentials: false + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - node-version: '20' - - name: Build binaries + go-version: ${{ env.GO_VERSION }} + cache: false + - name: Tag matches source version shell: bash run: | set -euo pipefail - mkdir -p dist - ext="" - if [ "$RUNNER_OS" = "Windows" ]; then ext=".exe"; fi - go build -trimpath -o "dist/across${ext}" ./cmd/across + source_version="$(tr -d '\r\n' < VERSION)" + binary_version="$(go run ./cmd/across version)" + if [ "v${source_version}" != "${GITHUB_REF_NAME}" ] || [ "${binary_version}" != "${source_version}" ]; then + echo "::error::tag ${GITHUB_REF_NAME}, VERSION ${source_version} and 'across version' ${binary_version} must agree" + exit 1 + fi + - name: Runner matches target + shell: bash + run: | + set -euo pipefail + actual="$(go env GOOS)/$(go env GOARCH)" + if [ "${actual}" != "${{ matrix.goos }}/${{ matrix.goarch }}" ]; then + echo "::error::runner builds ${actual}, expected ${{ matrix.goos }}/${{ matrix.goarch }}" + exit 1 + fi + - name: Test + run: go test -count=1 ./... + - name: Build and package + shell: bash + env: + CGO_ENABLED: "1" + COPYFILE_DISABLE: "1" + run: | + set -euo pipefail + name="across_${GITHUB_REF_NAME#v}_${{ matrix.goos }}_${{ matrix.goarch }}" + stage="dist/${name}" + mkdir -p "${stage}" + go build -trimpath -o "${stage}/across" ./cmd/across for agent in claude-code codex cursor gemini opencode qwen factory-droid amp goose; do - go build -trimpath -o "dist/across-agent-${agent}${ext}" "./cmd/across-agent-${agent}" + go build -trimpath -o "${stage}/across-agent-${agent}" "./cmd/across-agent-${agent}" done - sha256sum dist/* > dist/SHA256SUMS - go list -m -json all > dist/go-modules.json - - uses: anchore/sbom-action@v0 - with: - path: . - format: spdx-json - output-file: dist/sbom.spdx.json - - uses: actions/upload-artifact@v4 + cp LICENSE README.md CHANGELOG.md "${stage}/" + tar -C dist -czf "dist/${name}.tar.gz" "${name}" + rm -rf "${stage}" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: across-${{ matrix.os }} - path: dist + name: across-${{ matrix.goos }}-${{ matrix.goarch }} + path: dist/*.tar.gz if-no-files-found: error publish: needs: build runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + attestations: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ env.GO_VERSION }} + cache: false + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: dist pattern: across-* merge-multiple: true - - uses: softprops/action-gh-release@v2 + - name: Module inventory + run: go list -m -json all > dist/go-modules.json + - uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2 with: - files: | - dist/** + path: . + format: spdx-json + output-file: dist/sbom.spdx.json + syft-version: v1.51.1 + upload-artifact: false + upload-release-assets: false + - name: Checksums + shell: bash + run: | + set -euo pipefail + cd dist + archives=(across_*.tar.gz) + if [ "${#archives[@]}" -ne 2 ]; then + echo "::error::expected 2 archives, found ${#archives[@]}: ${archives[*]}" + exit 1 + fi + sha256sum across_*.tar.gz go-modules.json sbom.spdx.json > SHA256SUMS + cat SHA256SUMS + - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-checksums: dist/SHA256SUMS + - uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 + with: + draft: true + prerelease: true generate_release_notes: true + fail_on_unmatched_files: true + files: | + dist/across_*.tar.gz + dist/SHA256SUMS + dist/go-modules.json + dist/sbom.spdx.json