-
Notifications
You must be signed in to change notification settings - Fork 1
feat(release): publish verifiable release artifacts from a version tag #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
2aeb85b
feat(release): publish verifiable release artifacts from a version tag
kanushka a85fb33
fix(release): verify published assets and align the ARM variant with …
kanushka 9e8eb07
fix(release): compare archive names with the checksum file, not counts
kanushka 04b0b3b
docs(release): document the Windows platform token and the unsupporte…
kanushka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| name: Release | ||
|
|
||
| # A version tag is the whole trigger. Nothing publishes from a branch, so an | ||
| # artifact on the release page always corresponds to a tag someone pushed. | ||
| on: | ||
| push: | ||
| tags: | ||
| - 'v*' | ||
|
|
||
| # Read-only by default. Only the job that publishes the release is granted write | ||
| # access to repository contents, and nothing here is granted anything else. | ||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| # A tag may not ship artifacts that fail the proof. This is the same gate a | ||
| # pull request runs and the same script a contributor runs locally, so the | ||
| # release is held to the standard the repository already enforces rather than | ||
| # to a second, weaker one. | ||
| acceptance: | ||
| name: Architecture Proof | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Check out repository | ||
| uses: actions/checkout@v7 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Go | ||
| uses: actions/setup-go@v7 | ||
| with: | ||
| go-version: '1.25.x' | ||
| cache-dependency-path: | | ||
| go.mod | ||
| sdk/go.mod | ||
|
|
||
| - name: Run the architecture-proof acceptance gate | ||
| run: ./scripts/acceptance.sh | ||
|
|
||
| release: | ||
| name: Publish artifacts | ||
| needs: acceptance | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| # Creating the release and uploading its assets. GoReleaser needs nothing | ||
| # further: this release publishes no container image, no Homebrew tap, and | ||
| # no package repository. | ||
| contents: write | ||
| steps: | ||
| - name: Check out repository | ||
| uses: actions/checkout@v7 | ||
| with: | ||
| # GoReleaser reads the tag history to determine the version and to | ||
| # build the changelog, and a shallow clone leaves it with neither. | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Go | ||
| uses: actions/setup-go@v7 | ||
| with: | ||
| go-version: '1.25.x' | ||
| cache-dependency-path: | | ||
| go.mod | ||
| sdk/go.mod | ||
|
|
||
| - name: Build and publish the release | ||
| uses: goreleaser/goreleaser-action@v6 | ||
| with: | ||
| # Pinned in step with the Makefile's own pin, so a contributor running | ||
| # `make release-snapshot` builds with the version that publishes. | ||
| version: v2.17.1 | ||
| args: release --clean | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| # Proves what a user will actually find, by fetching it back from the | ||
| # release page rather than inspecting the build output: the checksum file | ||
| # describes every archive published, the archive named for this tag | ||
| # extracts to a binary reporting this tag's version, and the protocol | ||
| # version it claims is the one the shell's own source defaults to. | ||
| # | ||
| # A release that ships a binary still reporting the development | ||
| # placeholder, or a checksum file that does not cover every archive | ||
| # beside it, fails here rather than reaching a user. | ||
| - name: Verify the published artifacts | ||
| shell: bash | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| tag="${GITHUB_REF_NAME}" | ||
| archive="wso2-cli-${tag}-linux-amd64.tar.gz" | ||
|
|
||
| published="$(mktemp -d)" | ||
| gh release download "${tag}" --dir "${published}" --repo "${GITHUB_REPOSITORY}" | ||
|
|
||
| cd "${published}" | ||
|
|
||
| # What the install scripts will trust has to be what was built. A | ||
| # checksum file rewritten or truncated between build and publish is | ||
| # exactly what this comparison catches. | ||
| if ! diff -u "${GITHUB_WORKSPACE}/dist/checksums.txt" checksums.txt; then | ||
| echo "::error::the published checksums.txt is not the one that was built" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # --ignore-missing exits non-zero when it verifies nothing, so this | ||
| # cannot pass on an empty file. What it cannot see is an archive that | ||
| # is present but unlisted, so the counts are compared too: a target | ||
| # dropped from the checksum file would otherwise ship unverifiable. | ||
| sha256sum --check --ignore-missing checksums.txt | ||
| archives="$(find . -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.zip' \) | wc -l)" | ||
| listed="$(wc -l < checksums.txt)" | ||
| if [[ "${archives}" -ne "${listed}" ]]; then | ||
| echo "::error::${archives} archives published but ${listed} listed in checksums.txt" | ||
| exit 1 | ||
| fi | ||
| echo "${listed} archives published and all verified" | ||
|
|
||
| # The state root is redirected so that reading the module inventory | ||
| # cannot touch the runner's real home directory. | ||
| workdir="$(mktemp -d)" | ||
| state="$(mktemp -d)" | ||
| export WSO2_HOME="${state}" | ||
| tar -xzf "${archive}" -C "${workdir}" | ||
|
|
||
| reported="$("${workdir}/wso2" version)" | ||
| echo "${reported}" | ||
|
|
||
| if ! grep -qF "${tag}" <<<"${reported}"; then | ||
| echo "::error::the released binary reports no version matching ${tag}" | ||
| exit 1 | ||
| fi | ||
| if grep -qF '0.0.0-dev' <<<"${reported}"; then | ||
| echo "::error::the released binary reports the development placeholder" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # The protocol version is injected by .goreleaser.yaml and defaulted in | ||
| # internal/version. A release that disagrees with the shell's own | ||
| # default would ship a binary claiming support it was not built with, | ||
| # so the two are compared rather than assumed to have been kept in step. | ||
| # | ||
| # Both are required to be non-empty: if the version output ever stops | ||
| # carrying a Protocol field under this label, an unguarded comparison | ||
| # would find two empty strings equal and report agreement it never saw. | ||
| released_protocol="$(awk '$1 == "Protocol" { print $2 }' <<<"${reported}")" | ||
| source_protocol="$(cd "${GITHUB_WORKSPACE}" && go run ./cmd/wso2 version | awk '$1 == "Protocol" { print $2 }')" | ||
| if [[ -z "${released_protocol}" || -z "${source_protocol}" ]]; then | ||
| echo "::error::no protocol version was reported, so nothing was compared" | ||
| exit 1 | ||
| fi | ||
| if [[ "${released_protocol}" != "${source_protocol}" ]]; then | ||
| echo "::error::released protocol ${released_protocol} does not match the source default ${source_protocol}" | ||
| exit 1 | ||
| fi | ||
| echo "Protocol ${released_protocol} matches the source default" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # Release configuration for the wso2 shell. | ||
| # | ||
| # One tagged commit produces every artifact a user can install, plus the | ||
| # checksum file the install scripts verify against. The artifact naming | ||
| # convention this file implements is a published contract: the install scripts | ||
| # derive a download URL from a resolved tag alone, with no manifest to fetch. | ||
| # See docs/reference/release-artifacts.md before changing any name template. | ||
| # | ||
| # Reproduce a release locally without publishing anything: | ||
| # | ||
| # make release-snapshot | ||
| version: 2 | ||
|
|
||
| project_name: wso2-cli | ||
|
|
||
| builds: | ||
| # The shell is the only binary this repository releases. Modules are acquired | ||
| # by the shell at runtime and are versioned independently, so they are not | ||
| # artifacts of a shell release. | ||
| - id: wso2 | ||
| main: ./cmd/wso2 | ||
| binary: wso2 | ||
| env: | ||
| - CGO_ENABLED=0 | ||
| flags: | ||
| - -trimpath | ||
| ldflags: | ||
| - -s -w | ||
| # The shell version carries no leading "v": the version package prefixes | ||
| # one for display, and its semantic-version parse reads the bare form. | ||
| - -X github.com/wso2/wso2-cli/internal/version.shellVersion={{ .Version }} | ||
| # Kept in step with the default in internal/version by the release | ||
| # workflow, which runs the built binary and fails the release if the two | ||
| # disagree. Widening the protocol list means changing both. | ||
| - -X github.com/wso2/wso2-cli/internal/version.protocolVersion=1 | ||
| goos: | ||
| - linux | ||
| - darwin | ||
| - windows | ||
| goarch: | ||
| - amd64 | ||
| - arm64 | ||
| - arm | ||
| - "386" | ||
| goarm: | ||
| - "6" | ||
| # The eight surviving pairs are exactly the targets the pull-request | ||
| # cross-build check compiles. A target may not be released without also | ||
| # being compile-checked on every pull request. | ||
| ignore: | ||
| - goos: darwin | ||
| goarch: arm | ||
| - goos: darwin | ||
| goarch: "386" | ||
| - goos: windows | ||
| goarch: arm | ||
| - goos: windows | ||
| goarch: "386" | ||
|
|
||
| archives: | ||
| # wso2-cli-v0.1.0-darwin-arm64.zip, wso2-cli-v0.1.0-linux-amd64.tar.gz, and | ||
| # so on. The tag appears verbatim so that a script that resolved a tag can | ||
| # build this name without transforming it. | ||
| - id: wso2 | ||
| ids: | ||
| - wso2 | ||
| name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}" | ||
| formats: | ||
| - tar.gz | ||
| format_overrides: | ||
| - goos: darwin | ||
| formats: | ||
| - zip | ||
| - goos: windows | ||
| formats: | ||
| - zip | ||
| # The binary sits at the archive root rather than inside a version-named | ||
| # directory, so an installer extracts and moves one known path. | ||
| wrap_in_directory: false | ||
| # Apache-2.0 requires the licence and notice to travel with the binary. | ||
| files: | ||
| - LICENSE | ||
| - NOTICE | ||
|
|
||
| checksum: | ||
| # One file covering every archive, published as a release asset. Both install | ||
| # scripts fetch it and verify the archive they downloaded before extracting. | ||
| name_template: checksums.txt | ||
| algorithm: sha256 | ||
|
|
||
| snapshot: | ||
| version_template: "{{ incpatch .Version }}-snapshot" | ||
|
|
||
| changelog: | ||
| use: github | ||
| sort: asc | ||
| filters: | ||
| exclude: | ||
| - "^docs:" | ||
| - "^test:" | ||
| - "^chore:" | ||
| - Merge pull request | ||
|
|
||
| release: | ||
| # A tag carrying a prerelease identifier publishes as a prerelease, which is | ||
| # what the installers' prerelease channel resolves and what keeps a release | ||
| # candidate from becoming every user's default. | ||
| prerelease: auto | ||
| draft: false | ||
| name_template: "{{ .Tag }}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.