diff --git a/.github/actions/README.md b/.github/actions/README.md index d0b7779..5315e38 100644 --- a/.github/actions/README.md +++ b/.github/actions/README.md @@ -347,131 +347,9 @@ pr-review: cancel-in-progress: true ``` -## Release actions - -Three composite actions implement the org release flow — trunk-first -candidate, environment-gated settle (or settle-by-PR), publish-once. They are language-agnostic: the only -repo-specific inputs are where the version lives (`version_file` + -`version_pattern`: `plain`, `toml`, `json`), an optional `bump_command` for -whatever else must move with the version (`cargo update --workspace` for a -lockfile; path-dependency versions in a Cargo workspace — it runs with -`OLD_VERSION`/`NEW_VERSION` set, and the calling job installs the toolchain it -needs first), and the changelog path. Builds and -artifact uploads are not part of them: a repo that ships something composes -its publish targets in an `on-release.yml` from the extensions below, fired -by the GitHub Release that `release-publish` creates. - -| Action | Trigger in the consumer | Does | -|---|---|---| -| `release-candidate` `stage: propose` | `workflow_dispatch` on the default branch | bumps `version_file`, runs `bump_command` (lockfile, path-dep versions), drafts this release's changelog entry under `## vX.Y.Z` (dated at settle), syncs the previous release's entry from its tag, opens `chore/release-candidate-vX.Y.Z` PR | -| `release-candidate` `stage: cut` | that PR merging | creates `release-vX.Y.Z` at the merge commit | -| `release-settle` | `workflow_dispatch` with version + tip SHA | guards, warns if the tip lacks a workflow the default branch has, regenerates the entry up to the tip and stamps the date, opens `chore/release-settle-vX.Y.Z` PR onto the release branch (a re-run on a new tip closes the previous settle PR and opens a fresh one; nothing is ever force-pushed) — or, with `settle_mode: direct` and an authorised actor (`settlers`), commits it straight to the branch and publishes at once | -| `release-publish` | the settle PR merging (PR mode only; in direct mode `release-settle` runs it in the same job) | annotated tag at the merge commit (refuses if it exists or the branch drifted), GitHub Release with the entry as notes | - -The changelog has one owner per phase and is never back-merged: the candidate -PR drafts the entry on the default branch and syncs the *previous* release's -final entry from its tag; the settle PR finalises the entry (date, drift) on -the release branch; the tag and the GitHub Release carry the final text. Between -a release and the next candidate the default branch's copy of the latest entry -carries no date — the GitHub Release is authoritative in that window. - -`workflow-templates/release-*.yml` are the reference callers; they show up -under "New workflow → By megaeth-labs" in every org repo. Consumers track the -actions at `@main`, like every other action here: a merge to this repo reaches -all product release flows at once, which is what `actions-test.yml` and the -`main` ruleset are for. - -Requirements in the consumer repo: - -- Org variable `MEGA_MAXWELL_CLIENT_ID` (the Maxwell app's client id) / secret `MEGA_MAXWELL_PK`. PRs - and tag pushes must come from an App token: `GITHUB_TOKEN` does not trigger - downstream workflows. -- The human gate for creating a tag is the settle dispatch: the templates - put `environment: release` on the settle job, so its required reviewers - approve the run before it starts (create the environment with reviewers, - `prevent self-review` off if the dispatcher approves, and a deployment - branch policy of the default branch only), and the action's - `settlers: admin` checks the dispatching actor again. A repo that settles - by PR instead (no `settle_mode`) has the reviewed settle PR as its gate: - the `release-*` branch ruleset below requires one. -- A tag ruleset for `v*` (no creation/deletion/force-push) with the app as a - bypass actor, so `release-publish` is the only tag creator. -- A branch ruleset for `release-*` requiring PRs, with the app as a bypass - actor: nothing but the app's settle commit reaches a release branch - without a reviewed PR. In PR mode, drift after a settle PR is opened is - caught by `release-publish` itself (it refuses if the branch tip at merge - is not the settled SHA), so no "up to date" status check is needed. -- `gh` and `python3` on the runner (any GitHub-hosted image). -- The cut and publish stages only accept PRs opened by the app identity - (`pr_author`, default `mega-maxwell[bot]`); the templates also gate the - jobs' `if:` on it, so a hand-made `chore/release-*` branch merged by a - collaborator never reaches the app-token steps. -- A `concurrency` group on every release workflow, always with - `cancel-in-progress: false` (the templates carry them): one proposal at a - time, one cut at a time, one settle per version, one publish per release - branch, one `on-release` run per tag. Each of them ends in a push, a tag or - an upload that must never be cancelled half-way, and none of them is - atomic with its own guard, so a second run queues rather than overlaps. - The groups on the `pull_request`-triggered jobs (`cut`, `publish`) are - job-level, not workflow-level: every PR closing on that branch starts the - workflow, and GitHub keeps one pending run per group, so a workflow-wide - group would let an unrelated closure evict a queued release run. A - skipped job holds no slot in a job-level group. -- Every workflow the release depends on must be on the tagged commit, not - just on the default branch: `release-publish.yml` runs from the settle - PR's merge into the release branch, and `on-release.yml` from the tag's - tree (that is how `release` events resolve a workflow). A release branch - cut before such a file landed needs it cherry-picked through its own PR - before settlement is dispatched (or, in PR mode, before the settle PR - merges); `release-settle` warns when the settled - commit lacks a workflow the default branch has, because the alternative is - a Release with nothing attached and no failed run. - -Release notes are generated from commit subjects between the previous `v*` -tag and the settled commit, grouped by Conventional Commit type with PR links -from `(#N)` suffixes. The pure text logic lives in -`release-tools/release_tools.py` and is unit-tested by `actions-test.yml`. - -## Release extensions (publish targets) - -The core above ends with a tag and a GitHub Release. Products that ship -something run their targets from `on: release: published`, composed in a -repo-owned workflow stamped from `workflow-templates/on-release.yml`. Every -target is a step with a `dry_run` input, and the template threads one -`workflow_dispatch` flag into all of them, so a whole release can be -rehearsed on an existing tag with nothing published, uploaded or attached. - -| Action | Publishes | Idempotency on re-run | -|---|---|---| -| `release-publish-rust-crates` | an explicit crate list to crates.io at the release version — one `cargo publish -p … -p …` (Cargo ≥ 1.90 orders and waits); polls the index afterwards | crates already at the version are skipped | -| `release-upload-artifact` | one file to Artifact Registry (generic) or a GCS bucket; all destinations are inputs | identical file already there → `exists`; different → fails, never overwrites | -| `release-assets` | files + `SHA256SUMS` on the GitHub Release | `--clobber` | -| `release-verify-version` | nothing — runs a version probe (`my-binary --version`) between build and publish and requires ` ` (or a custom `expected`); a mismatch warns on a dry run and stops a release, a probe that cannot run stops both | n/a | - -Credentials are the caller's: `CARGO_REGISTRY_TOKEN` for crates.io; -`google-github-actions/auth` (service-account key or WIF) before an upload -step. Hold them in a **`publish` environment** whose deployment policy -allows only `v*` tags, and declare `environment: publish` on the publish -jobs (the template does): the secrets are then readable only by a run whose -ref is a release tag — the `release: published` run, or a rehearsal -dispatched on a tag ref — never by a branch build. Give that environment -no required reviewers: the human gate is the settle approval (the `release` -environment), and a second click before the uploads only holds them up. -`gcloud` and `gh` are on GitHub-hosted runners; the extensions are not -meant for the TKE image. - -What every `on-release.yml` carries, and why (the template has all of it): - -- `TAG: ${{ github.ref_name }}` and a `Require a tag ref` first step in each - job — never a tag input. The `publish` environment authorises the run's - ref, so that ref is the only thing the run may build and publish. -- `environment: publish` on every job that reads a publish credential. -- A workflow-level `concurrency` group keyed on `github.ref` with - `cancel-in-progress: false`: uploads must not overlap and must not be - cancelled mid-flight. -- `release-verify-version` between the build and the first publish step for - each binary. Never wrap a probe of the built artifact in - `2>/dev/null || echo …`: that turns a binary that cannot load into a green - step and a published download. -- The file itself on the tagged commit (see the release-branch requirement - above). +## Release pipeline + +The `release-*` actions and the `workflow-templates/release-*.yml` / +`on-release.yml` callers are documented in [RELEASE.md](RELEASE.md): what the +pipeline does and why, one release step by step, installation in a new +repository, options, publish targets, and recovery. diff --git a/.github/actions/RELEASE.md b/.github/actions/RELEASE.md new file mode 100644 index 0000000..ee19297 --- /dev/null +++ b/.github/actions/RELEASE.md @@ -0,0 +1,391 @@ +# The release pipeline + +How MegaETH repositories cut, settle and publish releases with the shared +actions in this directory, how to install the pipeline in a repository, which +options exist, and what to do when something goes wrong. + +The code is next to this file: `release-candidate/`, `release-settle/`, +`release-publish/` (the core), `release-verify-version/`, +`release-publish-rust-crates/`, `release-upload-artifact/`, `release-assets/` +(the publish targets), and `release-tools/` (the text helpers they share, unit +tested by `actions-test.yml`). The reference callers a repository stamps from +are in `workflow-templates/` at the repository root. `README.md` beside this +file documents the Claude CI actions and is not needed for releases. + +Contents: + +1. [What it does, and the choices behind it](#what-it-does-and-the-choices-behind-it) +2. [One release, step by step](#one-release-step-by-step) +3. [Installing it in a repository](#installing-it-in-a-repository) +4. [Options](#options) +5. [Publish targets (`on-release.yml`)](#publish-targets-on-releaseyml) +6. [Operations and recovery](#operations-and-recovery) +7. [Reference](#reference) + +## What it does, and the choices behind it + +A release is three phases, each a workflow in the consuming repository +calling one composite action here at `@main`: + +| Phase | Trigger | Result | +|---|---|---| +| **Candidate** | a maintainer dispatches `release-candidate.yml` with a version | a PR on the default branch that bumps the version and drafts the changelog entry; merging it creates `release-vX.Y.Z` at the merge commit | +| **Settle** | a maintainer dispatches `release-settle.yml` with the version and the release branch's tip | the dated changelog entry is committed to the release branch, the annotated tag `vX.Y.Z` is created there, and the GitHub Release is published with the entry as notes | +| **Publish targets** | the GitHub Release being published | whatever the repository ships: crates to crates.io, binaries to the artifact registry, downloads on the Release page — composed in `on-release.yml` from the publish-target actions | + +The choices that shape it: + +- **Trunk first.** The version bump and the changelog draft land on the + default branch through an ordinary reviewed PR; the release branch is cut + from that merge. Fixes for a release go to the release branch by PR and + must also land on the default branch, because the next candidate is cut + from there. +- **No release-candidate tags.** A tag is created exactly once, at + settlement, on the commit that ships. Tags are immutable: the `v*` tag + ruleset forbids updating or deleting them, and nothing but the app can + create them. +- **One human gate.** The settle dispatch is the release decision. A + `release` GitHub environment on the settle job makes the run wait for its + required reviewers, and the action checks that the dispatching actor is + authorised (`settlers`). Everything after the click is mechanical. +- **The changelog has one owner per phase.** The candidate PR drafts the + entry (`## vX.Y.Z`, no date) from merged PR titles and syncs the previous + release's final entry from its tag; settlement finalises it (date, any + commits added on the release branch) on the release branch; the tag and the + Release carry the final text. Nothing is back-merged: the default branch's + copy of the latest entry has no date until the next candidate PR syncs it. +- **The app is the identity.** Every push, PR, tag and Release is made by + the Maxwell GitHub App (`mega-maxwell[bot]`), from a token minted in the + job. Its PRs trigger CI (a `GITHUB_TOKEN` push would not), and the + rulesets make it the only actor that can create a tag or push to a release + branch without a PR. The `cut` and publish stages refuse PRs the app did not + open. +- **Credentials are scoped to release tags.** Publish credentials live in a + `publish` environment whose deployment policy allows only `v*` tag refs, so + a branch build can never read them. The environment has no reviewers: the + human gate is settlement. +- **No force pushes to release branches, settle branches or tags.** + Re-running settle supersedes what it made before (closes the old PR, drops + the branch, pushes fresh), so it works in repositories whose rulesets ban + force pushes. The one exception is a candidate re-dispatched while its PR + is still open: that PR's branch is updated in place, which is a force push + (see [Operations](#operations-and-recovery)). +- **Generic.** The actions know nothing about MegaETH: the version file and + its format, the changelog path, the branch prefix, the app identity and + every publish destination are inputs. The templates carry this + organisation's defaults. + +## One release, step by step + +The direct-settle flow, which every migrated repository uses. Commands assume +`gh` authenticated as a maintainer and the default branch `main`. + +**1. Candidate.** + +```sh +gh workflow run release-candidate.yml --ref main -f version=1.2.3 +``` + +Within a minute the app opens `chore(release): candidate v1.2.3` from the +branch `chore/release-candidate-1.2.3`: the version file bumped, whatever +`bump_command` maintains (a lockfile, path-dependency versions), the +changelog entry `## v1.2.3` drafted from the merged PR titles since the last +`v*` tag (grouped by Conventional Commit type, with PR links), and the +previous release's entry synced from its tag. Review it like any PR; fix +wording in it if the generated entry needs work. A stale candidate branch +from an earlier attempt is dropped first, unless its PR is still open. + +**2. Cut.** Merging the candidate PR runs the `cut` job, which creates +`release-v1.2.3` at the merge commit. No tag yet. From here, fixes for the +release go to `release-v1.2.3` by PR, and each one must also reach `main`. + +**3. Settle.** + +```sh +gh workflow run release-settle.yml --ref main -f version=1.2.3 \ + -f commit="$(gh api repos/OWNER/REPO/git/ref/heads/release-v1.2.3 -q .object.sha)" +``` + +The run pauses at the `release` environment; a required reviewer approves it +under "Review deployments" on the run page. The dispatcher may be the +reviewer (the environment is created with self-review allowed). After the +click, the action verifies the commit is a full SHA, the release branch +exists, `v1.2.3` does not, the commit is the branch tip, the version file at +that commit says `1.2.3`, and `1.2.3` is newer than the latest tag. It warns +if the tip lacks a workflow the default branch has (see +[Operations](#operations-and-recovery)). It checks the dispatcher against +`settlers`. Then it regenerates the notes up to the tip, writes +`## v1.2.3 (YYYY-MM-DD)` into the changelog, commits that straight onto +`release-v1.2.3` (the app bypasses the branch ruleset), creates the annotated +tag `v1.2.3` at that commit, and publishes the GitHub Release, marked latest, +with the entry as notes. About a minute after approval. + +**4. Publish targets.** The Release's `published` event starts +`on-release.yml`, which builds and publishes whatever the repository ships. +Each target is a job; `dry_run` is off on a real release. See +[Publish targets](#publish-targets-on-releaseyml). + +Settling by PR instead (`settle_mode: pr`, the action's default; no +migrated repository uses it): step 3 opens `chore(release): settle v1.2.3` +onto the release branch with the dated entry, and merging that PR is the +decision; `release-publish.yml` then tags the merge commit and publishes the +Release, refusing if the branch moved since the PR was made (the merge's first +parent must be the settled SHA) or the PR was not opened by the app. + +## Installing it in a repository + +Everything below was done for mega-agents, mega-evm, stateless-validator and +salt; copy from one of them when in doubt. + +**1. The app must cover the repository.** The Maxwell GitHub App has to be +installed on it (organisation settings → GitHub Apps → mega-maxwell → +repository access). Nothing here can check that for you; a missing +installation shows up as the token step failing in the first run. + +**2. Secret and variable access.** The workflows mint the app token from the +organisation secret `MEGA_MAXWELL_PK` and the organisation variable +`MEGA_MAXWELL_CLIENT_ID`. Both must be readable by the repository: for a +public repository the organisation's access policy must include public +repositories explicitly. + +**3. Rulesets.** Two, both with the app and the repository admins as bypass +actors (`bypass_mode: always`): + +- `release branch`, target `branch`, on `refs/heads/release-*`: `deletion`, + `non_fast_forward`, `pull_request` (one approving review, code-owner review, + squash merges), and a `branch_name_pattern` requiring `release-vX.Y.Z`. + This is what makes every change on a release branch a reviewed PR, except + the app's settle commit. +- `release tag`, target `tag`, on `refs/tags/v*`: `creation`, `update`, + `deletion`, `non_fast_forward`, and a `tag_name_pattern` requiring + semantic-version tags. This is what makes the app the only tag creator. + +Copy them from a repository that has them rather than typing them: + +```sh +src=megaeth-labs/mega-agents; dst=OWNER/REPO +for id in $(gh api repos/$src/rulesets -q '.[] | select(.name | test("release")) | .id'); do + gh api repos/$src/rulesets/$id \ + | python3 -c 'import json,sys; r=json.load(sys.stdin); print(json.dumps({k:r[k] for k in ("name","target","enforcement","bypass_actors","conditions","rules")}))' \ + | gh api -X POST repos/$dst/rulesets --input - +done +``` + +`RepositoryRole` actor `5` is the admin role; the `Integration` actor is the +app's id. + +**4. The `release` environment.** Required reviewers: the maintainers who +may approve a release. Self-review allowed, so the dispatcher can approve +their own dispatch (`prevent_self_review: false`). Deployment branch policy: +the default branch only, because that is the ref the settle workflow is +dispatched on. No secrets. + +```sh +uid=$(gh api users/LOGIN -q .id) +gh api -X PUT repos/OWNER/REPO/environments/release --input - </dev/null || echo …`: that turns a binary that cannot load into a green + step and a published download. +- One build feeding every target when several targets ship the same file + (a workflow artifact between jobs), so the Release page and the registry + carry the same bytes. +- `dry_run` threaded from a `workflow_dispatch` input into every target, so + the whole workflow can be rehearsed on a tag ref: + `gh workflow run on-release.yml --ref vX.Y.Z -f dry_run=true`. + +The targets: + +| Action | Publishes | On re-run | On `dry_run` | +|---|---|---|---| +| `release-publish-rust-crates` | an explicit crate list to crates.io at the release version — one `cargo publish -p … -p …` (Cargo ≥ 1.90 orders and waits); polls the index afterwards. Every crate's manifest must already be at the version. Never `--workspace`: list the crates. | crates already at the version are skipped | `cargo publish --dry-run` for every listed crate, nothing uploaded | +| `release-upload-artifact` | one file to Artifact Registry (generic repository) or a GCS bucket; every destination is an input. Authentication is the caller's: run `google-github-actions/auth` before it. | identical file already there → `exists`; different → fails, never overwrites | checksum and destination check only | +| `release-assets` | files plus a generated `SHA256SUMS` on the GitHub Release; needs the job token with `contents: write` | `--clobber` | prints the sums, attaches nothing | +| `release-verify-version` | nothing — runs a version probe (`my-binary --version`) and requires ` ` (`expected` overrides, with `{name}` and `{version}`) | n/a | a mismatch warns; a probe that cannot run fails even here | + +The three migrated repositories with targets: mega-evm publishes four crates +and uploads `mega-evme` to the registry and the Release page; +stateless-validator uploads both of its binaries to the registry and the +Release page; mega-agents and salt ship nothing beyond the Release. + +## Operations and recovery + +**The release branch moved after settling** (a fix landed): run settle +again with the new tip. In direct mode that is the whole story. In PR mode +the old settle PR is closed with a comment, its branch deleted, and a fresh +PR opened: nothing is force-pushed, so this also works under a +"ban force push" ruleset. + +**A workflow the release needs is not on the release branch.** A `release` +event runs `on-release.yml` from the *tag's* tree, and `release-publish.yml` +runs from the release branch, so a workflow added to the default branch after +the branch was cut is silently absent: the Release publishes with nothing +attached and no failed run. Settle warns when the tip lacks a workflow the +default branch has. Fix: cherry-pick the file onto the release branch through +its own PR, then settle. (A tag's tree is final: a target that was missing +or broken at a tag cannot be re-run for that tag; the next release gets it.) + +**A stale candidate branch exists** from an earlier attempt: the candidate +action deletes it before pushing, unless a PR on it is still open, in which +case that PR is updated in place with a force push of its branch. In a +repository whose rulesets ban force pushes, close the open candidate PR +before re-dispatching; the action then drops the branch and opens a fresh +PR. + +**Two runs of the same stage overlap.** They queue, never cancel: the +candidate's `propose` and `cut`, and `release-publish`'s `publish`, carry +job-level concurrency groups (job-level, because those workflows also start +on every PR closing on their branch, and a workflow-wide group would let such +a run evict a queued one); settle has a group per version, `on-release` a +group per tag. + +**The tag already exists** when settling: that version is released. Pick the +next version and start from the candidate. + +**A publish target failed after the tag was created**: fix the cause on +the default branch. If the fix is in the shared action, dispatch +`on-release.yml` again on the tag (`--ref vX.Y.Z -f dry_run=false`); all +targets are idempotent, so the ones that succeeded are no-ops. If the fix is +in the workflow file itself, it cannot reach that tag's tree; the next release +carries it. + +**The PR reviewer fails on a PR that edits `claude.yml`**: by design of +`anthropics/claude-code-action`, which refuses to run from a PR whose +workflow file differs from the default branch's. It is not a required check; +merge, and the reviewer works again from the merged file. + +**The app cannot push, tag, or read the secret**: check, in this order, that +the app is installed on the repository, that the organisation secret and +variable are readable by it (public repositories are opted in separately), +and that the app is a bypass actor on both rulesets. + +## Reference + +What each action does, in one line: + +| Action | Trigger in the consumer | Does | +|---|---|---| +| `release-candidate` `stage: propose` | `workflow_dispatch` on the default branch | bumps `version_file`, runs `bump_command`, drafts this release's changelog entry under `## vX.Y.Z`, syncs the previous release's entry from its tag, opens `chore/release-candidate-X.Y.Z` | +| `release-candidate` `stage: cut` | that PR merging | creates `release-vX.Y.Z` at the merge commit | +| `release-settle` | `workflow_dispatch` with version + tip SHA | guards, warns if the tip lacks a workflow the default branch has, regenerates the entry up to the tip and stamps the date; `direct`: commits it to the release branch and publishes at once; `pr`: opens `chore/release-settle-vX.Y.Z` (a re-run closes the previous settle PR and opens a fresh one) | +| `release-publish` | the settle PR merging, or `release-settle` in direct mode | annotated tag at the commit (refuses if it exists or the branch drifted), GitHub Release with the entry as notes, marked latest | + +Release notes are generated from commit subjects between the previous `v*` +tag and the settled commit, grouped by Conventional Commit type, with PR +links from `(#N)` suffixes; `chore(release):` commits are left out. The text +logic (`normalize`, `is-greater`, `read-file`, `bump-file`, `notes`, +`changelog-insert`, `changelog-extract`, `changelog-copy`) lives in +`release-tools/release_tools.py`, the crate helpers in `crates_tools.py`; +both are unit tested by `actions-test.yml`, which also drives +`release-verify-version` end to end with fake binaries. Consumers track the +actions at `@main`: a merge here reaches every repository at once, and that +workflow is the gate. + +Requirements on the consumer's side, in one list: the app installed; the +organisation secret `MEGA_MAXWELL_PK` and variable `MEGA_MAXWELL_CLIENT_ID` +readable; the two rulesets with the app as bypass actor; the `release` +environment (and `publish`, for targets); `gh` and `python3` on the runner +(any GitHub-hosted image); the three core workflows, with the same +`version_file`/`version_pattern`/`changelog_file` in each; a `CHANGELOG.md`. diff --git a/.github/actions/release-assets/action.yml b/.github/actions/release-assets/action.yml index fbf2787..0da0f9a 100644 --- a/.github/actions/release-assets/action.yml +++ b/.github/actions/release-assets/action.yml @@ -5,6 +5,7 @@ description: >- idempotent. `dry_run` writes and prints `SHA256SUMS` but attaches nothing. Needs a token with `contents: write` on the repository (the job token is enough). + Guide: .github/actions/RELEASE.md in megaeth-labs/.github. inputs: tag: diff --git a/.github/actions/release-candidate/action.yml b/.github/actions/release-candidate/action.yml index 344527c..2c9d1a4 100644 --- a/.github/actions/release-candidate/action.yml +++ b/.github/actions/release-candidate/action.yml @@ -8,6 +8,7 @@ description: >- is created at either stage — tags come from release-publish, once, at settlement. Run as a step in a job the consumer owns; the consumer checks the repository out first (`fetch-depth: 0`, `persist-credentials: false`). + Guide: .github/actions/RELEASE.md in megaeth-labs/.github. inputs: stage: diff --git a/.github/actions/release-publish-rust-crates/action.yml b/.github/actions/release-publish-rust-crates/action.yml index 01696a2..214ccc2 100644 --- a/.github/actions/release-publish-rust-crates/action.yml +++ b/.github/actions/release-publish-rust-crates/action.yml @@ -11,6 +11,7 @@ description: >- has checked out the release tag and installed the toolchain the crates need. Never use `--workspace`: a crate without `publish = false` that was never meant to be published would go out with it. + Guide: .github/actions/RELEASE.md in megaeth-labs/.github. inputs: crates: diff --git a/.github/actions/release-publish/action.yml b/.github/actions/release-publish/action.yml index fb8a58e..6377ad3 100644 --- a/.github/actions/release-publish/action.yml +++ b/.github/actions/release-publish/action.yml @@ -11,6 +11,7 @@ description: >- changelog catches up in the next release candidate PR — nothing is back-merged. The consumer checks the repository out first (`fetch-depth: 0`, `persist-credentials: false`). + Guide: .github/actions/RELEASE.md in megaeth-labs/.github. inputs: token: diff --git a/.github/actions/release-settle/action.yml b/.github/actions/release-settle/action.yml index 4b572d0..9257396 100644 --- a/.github/actions/release-settle/action.yml +++ b/.github/actions/release-settle/action.yml @@ -13,6 +13,7 @@ description: >- immediately on that commit. Run as a step in a job the consumer owns; the consumer checks the repository out first (`fetch-depth: 0`, `persist-credentials: false`). Needs `gh` and `python3` on the runner. + Guide: .github/actions/RELEASE.md in megaeth-labs/.github. inputs: token: diff --git a/.github/actions/release-upload-artifact/action.yml b/.github/actions/release-upload-artifact/action.yml index 03712c4..a140c0c 100644 --- a/.github/actions/release-upload-artifact/action.yml +++ b/.github/actions/release-upload-artifact/action.yml @@ -9,6 +9,7 @@ description: >- Workload Identity Federation) before this step so `gcloud` is authenticated. `dry_run` computes the checksum and checks the destination but uploads nothing. + Guide: .github/actions/RELEASE.md in megaeth-labs/.github. inputs: file: diff --git a/.github/actions/release-verify-version/action.yml b/.github/actions/release-verify-version/action.yml index e5c5f4a..d20e7a6 100644 --- a/.github/actions/release-verify-version/action.yml +++ b/.github/actions/release-verify-version/action.yml @@ -11,6 +11,7 @@ description: >- never swallowed. A mismatch is a warning on a dry run, so a rehearsal reports what a real run would refuse and carries on, and a hard stop otherwise. + Guide: .github/actions/RELEASE.md in megaeth-labs/.github. inputs: command: diff --git a/README.md b/README.md index a46ae92..a992386 100644 --- a/README.md +++ b/README.md @@ -1 +1,17 @@ -# .github \ No newline at end of file +# .github + +Organisation-wide CI for megaeth-labs: composite actions consumed at `@main` +by every repository, and the workflow templates that show up under +"New workflow → By megaeth-labs". + +- [`.github/actions/RELEASE.md`](.github/actions/RELEASE.md) — the release + pipeline: candidate, settle, publish, publish targets; how it works, how to + install it in a repository, options, recovery. +- [`.github/actions/README.md`](.github/actions/README.md) — the Claude CI + actions: PR review, label check, issue triage, interactive. +- [`workflow-templates/`](workflow-templates/) — the reference callers. +- [`profile/`](profile/) — the organisation profile page. + +`actions-test.yml` is the only gate between a change here and every +consumer's CI; it runs the unit tests of the text helpers and drives the +actions that can be exercised without a repository. diff --git a/workflow-templates/on-release.yml b/workflow-templates/on-release.yml index c20131f..ef37b00 100644 --- a/workflow-templates/on-release.yml +++ b/workflow-templates/on-release.yml @@ -29,6 +29,8 @@ name: On Release # Each target is its own job so a hiccup in one never blocks another. Delete # the jobs this repository does not need; keep the shared actions at @main. # Every destination below is a placeholder — there are no defaults. +# +# Guide: https://github.com/megaeth-labs/.github/blob/main/.github/actions/RELEASE.md on: release: diff --git a/workflow-templates/release-candidate.yml b/workflow-templates/release-candidate.yml index f048a6d..ed541e0 100644 --- a/workflow-templates/release-candidate.yml +++ b/workflow-templates/release-candidate.yml @@ -5,6 +5,8 @@ name: Release Candidate # opens a candidate PR. When that # PR merges, the `cut` job creates `release-vX.Y.Z` at the merge commit. No # tag is created here — see release-settle / release-publish. +# +# Guide: https://github.com/megaeth-labs/.github/blob/main/.github/actions/RELEASE.md on: workflow_dispatch: diff --git a/workflow-templates/release-publish.yml b/workflow-templates/release-publish.yml index 4eef8eb..c71b3e1 100644 --- a/workflow-templates/release-publish.yml +++ b/workflow-templates/release-publish.yml @@ -13,6 +13,8 @@ name: Release Publish # release-settle.yml (the template's default) the settle job publishes # itself and nothing here ever fires: the job gate above rejects any settle # PR the app did not open. +# +# Guide: https://github.com/megaeth-labs/.github/blob/main/.github/actions/RELEASE.md on: pull_request: diff --git a/workflow-templates/release-settle.yml b/workflow-templates/release-settle.yml index 3707c94..9e4c61f 100644 --- a/workflow-templates/release-settle.yml +++ b/workflow-templates/release-settle.yml @@ -16,6 +16,8 @@ name: Release Settle # To settle by PR instead — a reviewed `chore/release-settle-vX.Y.Z` PR onto # the release branch, tagged by release-publish.yml when it merges — remove # `environment:`, `settle_mode` and `settlers` below. +# +# Guide: https://github.com/megaeth-labs/.github/blob/main/.github/actions/RELEASE.md on: workflow_dispatch: