From c69e3451fe0f3954cf72cd50bd467c5a656222cd Mon Sep 17 00:00:00 2001 From: bdchatham Date: Wed, 5 Aug 2026 12:52:26 -0700 Subject: [PATCH] ci: release through version.json and the org's shared workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopts the release tooling the other Go repositories here use: bump version.json, label the pull request `release`, merge, and the tag and GitHub Release follow. Replaces the tag I pushed by hand for v0.1.0, which nothing recorded and nobody could review. version.json is seeded at v0.1.0 to match the existing tag, so installing this does not itself release anything — release-publish sees the tag and no-ops. The next bump is the first one the tooling performs. The part worth knowing is that release-check earns its place on a LIBRARY, which is not what I assumed when I started wiring this. It runs `gorelease` and `gocompat` against the previous tag: the Go project's own API-compatibility checkers, comparing the exported surface to the last release and reporting whether the version increment is semver-honest. For a module whose whole purpose is to be imported, catching a breaking change under a patch bump is worth more than any build artifact. It also only proposes a tag when the pull request carries a `release` label, so an ordinary version edit on a feature branch cannot release by accident. Deliberately NO goreleaser-release job, unlike sei-chain's integration. This module is a library, so the tag IS the release — `go get` resolves a version from the module proxy, which reads it from the tag, and there is no binary to build or attach. The repositories that chain goreleaser after release-publish are shipping a command. The comment in uci-release-publish.yml says where that job would go if this ever does. Both callers pin uci@v0.0.15, the current tag, rather than the mixed v0.0.9 and v0.0.11 an older integration ended up on. release-check is triggered by pull_request_target because it needs a token that can comment on the pull request, which a fork-originated run does not get. That makes the trigger privileged, so it is worth stating why it is safe: nothing in these files checks out or executes pull-request code, and the reusable workflow reads version.json through the API at the head SHA rather than from a checkout. --- .github/workflows/uci-release-check.yml | 31 +++++++++++++++++++++ .github/workflows/uci-release-publish.yml | 34 +++++++++++++++++++++++ README.md | 33 ++++++++++++++++++++++ version.json | 3 ++ 4 files changed, 101 insertions(+) create mode 100644 .github/workflows/uci-release-check.yml create mode 100644 .github/workflows/uci-release-publish.yml create mode 100644 version.json diff --git a/.github/workflows/uci-release-check.yml b/.github/workflows/uci-release-check.yml new file mode 100644 index 0000000..1440169 --- /dev/null +++ b/.github/workflows/uci-release-check.yml @@ -0,0 +1,31 @@ +name: UCI +run-name: UCI / Release Check + +# Validates a version bump on the pull request that proposes it, before it can +# reach main. The org's shared implementation lives in sei-protocol/uci; this file +# only supplies the trigger and the permissions. +# +# pull_request_target rather than pull_request, matching the other repositories +# that use this: the check needs a token that can comment on the pull request, +# which a fork-originated pull_request run does not get. That makes the trigger +# privileged, so nothing here checks out or executes pull-request code — the +# reusable workflow reads version.json through the API at the head SHA rather +# than from a checkout. +on: + pull_request_target: + paths: [ 'version.json' ] + types: [ opened, synchronize, reopened, labeled, unlabeled ] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + release-check: + name: Release + uses: sei-protocol/uci/.github/workflows/release-check.yml@v0.0.15 diff --git a/.github/workflows/uci-release-publish.yml b/.github/workflows/uci-release-publish.yml new file mode 100644 index 0000000..5d44241 --- /dev/null +++ b/.github/workflows/uci-release-publish.yml @@ -0,0 +1,34 @@ +name: UCI +run-name: UCI / Release Publish + +# Tags and releases the version in version.json once it lands on main. +# +# This is a Go LIBRARY, so the tag IS the release: `go get` resolves a version +# from the module proxy, which reads it from the tag. There is no binary to build +# and nothing to attach, which is why this calls release-publish and stops — +# unlike the repositories that follow it with goreleaser-release to build a +# command. If this ever ships an executable, that job goes here. +# +# Note that `paths` filters do not apply to tag pushes, so this also fires when a +# version tag is pushed by hand. That is deliberate and harmless: release-publish +# checks whether the tag exists and no-ops when it does, so the manual path and +# the automatic one converge instead of conflicting. +on: + push: + paths: [ 'version.json' ] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + # Keyed per event and ref, never cancelled. A version.json branch push and a + # follow-up manual tag push share a SHA, and cancelling an in-flight release on + # the later event would leave a tag without its release. + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + name: Release + uses: sei-protocol/uci/.github/workflows/release-publish.yml@v0.0.15 diff --git a/README.md b/README.md index 61c732c..cccb391 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,39 @@ resources, policies, permissions, comments, fork, agent swap) that this package does not call. + +## Releasing + +Releases are driven by `version.json` through the org's shared workflows in +[sei-protocol/uci](https://github.com/sei-protocol/uci), the same ones the other +Go repositories here use. Bump the version, open a pull request, label it +`release`, and merge — the tag and the GitHub Release follow from the merge. + + { "version": "v0.1.0" } + +Two things happen, in this order. + +**On the pull request**, `release-check` validates the bump and — the part that +matters for a library — runs `gorelease` and `gocompat` against the previous +version. Those are the Go project's own API-compatibility checkers: they compare +the exported surface to the last tag and say whether the version increment is +semver-honest. A breaking change under a patch bump is caught before it ships, +which is worth more here than any artifact would be. The check only proposes a +tag when the pull request carries a `release` label, so an ordinary bump in a +feature branch cannot release by accident. + +**On merge to main**, `release-publish` reads `version.json`, creates the tag and +the GitHub Release, and no-ops if the tag already exists. A tag pushed by hand +converges with the automatic path rather than conflicting with it. + +There is deliberately no GoReleaser job. **This module is a library, so the tag +is the release** — `go get` resolves a version from the module proxy, which reads +it from the tag, and there is no binary to build or attach. The repositories here +that do follow release-publish with `goreleaser-release` are shipping a command. +If this ever ships one, that job belongs in +`.github/workflows/uci-release-publish.yml`. + + ## Provenance This module is maintained by sei-protocol and is **not** an official Omnigent diff --git a/version.json b/version.json new file mode 100644 index 0000000..557859c --- /dev/null +++ b/version.json @@ -0,0 +1,3 @@ +{ + "version": "v0.1.0" +}