diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml deleted file mode 100644 index 3a29820..0000000 --- a/.github/workflows/publish-cli.yml +++ /dev/null @@ -1,83 +0,0 @@ -# One-shot bootstrap: the first `termlens-cli` publish (#255). -# -# Every other publish in this repository is crates.io Trusted Publishing — -# release.yml exchanges a job's OIDC token for a short-lived one and no -# secret is stored. That cannot be used here: Trusted Publishing is -# configured per crate, on a crate that already exists, so the first publish -# of a new name has nothing to configure it against. release.yml therefore -# publishes termlens-cli only when crates.io already knows it, and warns -# otherwise. -# -# This workflow closes that gap exactly once, with a token, from a tag. Once -# it has run: link Trusted Publishing for termlens-cli (crates.io → -# termlens-cli → Settings → Trusted Publishing → GitHub, repository -# vyncint/termlens, workflow release.yml, environment release), then revoke -# the token, delete the CARGO_REGISTRY_TOKEN secret and delete this file. -# Every release after that goes through release.yml with no secret at all. -# -# Deliberately not wired to the `release` environment: that environment -# exists so an OIDC token cannot be minted from a branch, and its policy -# admits only `v*` refs. CARGO_REGISTRY_TOKEN is a repository secret, so the -# environment would add no protection here while blocking the dispatch. -name: publish-cli (bootstrap) - -on: - workflow_dispatch: - inputs: - ref: - description: The tag to publish from. Its version must match the manifest. - required: true - default: v0.10.0 - confirm: - description: Type the crate name to confirm this irreversible publish. - required: true - default: "" - -permissions: - contents: read - -concurrency: - group: publish-cli - cancel-in-progress: false - -jobs: - publish: - name: publish termlens-cli - runs-on: ubuntu-latest - steps: - - name: Refuse an unconfirmed dispatch - env: - CONFIRM: ${{ inputs.confirm }} - run: | - if [ "$CONFIRM" != "termlens-cli" ]; then - echo "::error::type 'termlens-cli' in the confirm box; a publish cannot be undone" - exit 1 - fi - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - with: - ref: ${{ inputs.ref }} - persist-credentials: false - - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1 - with: - toolchain: stable - # The same guard release.yml puts in front of every publish: the crate - # that goes out must be the version the tag names, or the registry gets - # a number that means nothing. - - name: Compare the ref against the crate version - env: - REF: ${{ inputs.ref }} - run: | - tag="${REF#v}" - version="$(cargo metadata --no-deps --format-version 1 \ - | jq -r '.packages[] | select(.name == "termlens-cli") | .version')" - echo "ref=${tag} crate=${version}" - if [ "$tag" != "$version" ]; then - echo "::error::${REF} does not match termlens-cli ${version}" - exit 1 - fi - # --locked so the published crate is the tested tree, not whatever the - # index offers today. The verify build resolves `termlens` from - # crates.io rather than the path, which is what a consumer will do. - - run: cargo publish -p termlens-cli --locked - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml index 9bbcf87..b6dabed 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -14,14 +14,3 @@ rules: - release.yml - install.yml - windows.yml - - publish-cli.yml - - # Correct advice that this one workflow cannot take, which is the whole - # reason it exists. Trusted Publishing is configured per crate on a crate - # that already exists, so the *first* publish of a new name has nothing to - # configure it against and must use a token. Every other publish here is - # Trusted Publishing (release.yml), and this file is deleted along with the - # secret once crates.io knows termlens-cli. - use-trusted-publishing: - ignore: - - publish-cli.yml diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 7c3fc7d..6601c2d 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -8,14 +8,12 @@ One page, copy-pasteable. Maintainers only. crate**: crates.io → *crate* → Settings → Trusted Publishing → GitHub, repository `vyncint/termlens`, workflow `release.yml`. - `termlens` — linked 2026-08-09. - - `termlens-cli` — **not linked yet**. The crate was first published on - 2026-09-08 by the one-shot `publish-cli.yml` bootstrap (a new name has - no crate to configure Trusted Publishing against, which is the whole - chicken-and-egg). Until it is linked, `release.yml`'s termlens-cli - step will fail on authentication *after* `termlens` has already gone - out — so link it before the next tag. Then delete the - `CARGO_REGISTRY_TOKEN` secret and `.github/workflows/publish-cli.yml`, - and the repository is back to storing no secret at all. + - `termlens-cli` — linked 2026-09-08, and first exercised by v0.10.1. + The crate's own first publish could not use it (a new name has no + crate to configure Trusted Publishing against) and went out through a + one-shot token workflow, since deleted. + + No token or secret is stored for publishing. - The publish job runs in the **`release` GitHub environment**, which only deploys from `v*` tags — an OIDC publish token can never be minted from a branch. (Optionally set the environment name `release` @@ -73,15 +71,21 @@ Pushing the tag runs `release.yml`, which: ## Bootstrapping a brand-new crate Trusted Publishing is configured per crate, on a crate that already -exists, so the *first* publish of a new name cannot use it. -`.github/workflows/publish-cli.yml` is that one-shot path — dispatch-only, -a typed confirmation, a tag rather than a branch, the same -tag-matches-version guard, `--locked` — run once against -`CARGO_REGISTRY_TOKEN`. It published `termlens-cli` 0.10.0 on 2026-09-08. - -Afterwards, always: link Trusted Publishing for the new crate, revoke the -token, delete the secret, delete the workflow. A stored publish token is a -standing risk that this repository otherwise does not carry. +exists, so the *first* publish of a new name cannot use it. The recipe, +used once for `termlens-cli` on 2026-09-08 (see `publish-cli.yml` in that +commit range for the exact shape): + +1. A dispatch-only workflow with a typed confirmation, checking out the + **tag** rather than a branch, carrying `release.yml`'s + tag-matches-version guard, publishing with `--locked` against a + `CARGO_REGISTRY_TOKEN` repository secret. +2. Link Trusted Publishing for the new crate on crates.io. +3. Revoke the token, delete the secret, delete the workflow. + +Step 3 is not optional and comes *after* a release has proved step 2: a +stored publish token is a standing risk this repository otherwise does not +carry, and deleting the only fallback before the mechanism is exercised is +the wrong order. ## If something fails mid-release