feat: one-step soothfast action with bot landing - #164
Merged
Merged
Conversation
Contributor
soothfast gate |
Verdenroz
force-pushed
the
feat/soothfast-action
branch
from
September 6, 2026 00:53
0086556 to
9914445
Compare
Contributor
soothfast gate |
Verdenroz
force-pushed
the
feat/soothfast-action
branch
from
September 6, 2026 00:55
9914445 to
9fd1434
Compare
6 tasks
Verdenroz
force-pushed
the
feat/soothfast-action
branch
from
September 6, 2026 01:34
9fd1434 to
4b03441
Compare
Verdenroz
force-pushed
the
feat/soothfast-action
branch
from
September 6, 2026 01:35
4b03441 to
908049d
Compare
Verdenroz
force-pushed
the
feat/soothfast-action
branch
from
September 6, 2026 02:01
908049d to
5923edb
Compare
Verdenroz
force-pushed
the
feat/soothfast-action
branch
2 times, most recently
from
September 6, 2026 02:04
c849536 to
7d4b2a1
Compare
Verdenroz
force-pushed
the
feat/soothfast-action
branch
from
September 6, 2026 02:10
7d4b2a1 to
4cd40e1
Compare
- The root action grows from installing the CLI to being the whole adopter surface: gate and comment on pull requests, regenerate and land as soothfast-bot on the default branch, one `uses:` line. - The same scripts back this repo's own workflows, so the adopter path is exercised on every merge here. - A no-op regeneration never mints a token. Closes #158 Closes #14
Verdenroz
force-pushed
the
feat/soothfast-action
branch
from
September 6, 2026 02:12
4cd40e1 to
68dbfda
Compare
Verdenroz
added a commit
that referenced
this pull request
Sep 6, 2026
## What changed Three writes were still attributed to github-actions after #164: the gate comment on pull requests, the gh-pages push in `deploy-docs`, and `gh release create`. All three now carry the soothfast-bot identity. Pull request jobs never receive a token for it: the broker posts the gate comment itself. Release creation moves to its own job because a job carries one environment and `publish` already needs the `release` approval gate. - Added `POST /comment` to the broker. A `pull_request` run from the repository itself, in the `soothfast-bot` environment, sends `{pull_request, marker, body}` over its OIDC identity; the broker checks the number against the run's own merge ref, mints a `pull_requests: write` token for itself, creates or updates the comment that starts with the marker, and revokes the token. The job holds nothing. `/token` refuses `pull_request` runs outright. - Accepted tag refs for a landing token only when the compare of default branch to the OIDC `sha` reports `behind` or `identical`. The commit that ran is what is judged, never the tag name, so moving a tag after the run starts changes nothing. Revoked on any other status. - `action/oidc.sh` holds the broker URL and OIDC fetch shared by `bot-token.sh` and `comment.sh`. `comment.sh` tries the broker first and falls back to the caller's token; `gate.sh` runs the gate before any comment and passes no token into it. - `soothfast-gate.yml` runs in the `soothfast-bot` environment with `id-token: write`, checks out this repository's `action/` at `github.job_workflow_sha` so a cross-repo caller gets matching scripts, installs the CLI through that checkout instead of a pinned self-reference, and posts its comment through the broker with the same fallback. - `deploy-docs` pushes gh-pages with a landing token and revokes it. `release.yml` gains a `release` job (report, changelog extraction, `gh release create` as soothfast-bot) between `publish` and `binaries`; `publish` drops the rustdoc pin it no longer needs. - Gate output quoted in the comment is text the pull request's own binaries wrote, now under a write-access author. It stays inside a code fence and any fence sequence in it is neutralised first, so no line of it can become top-level markdown (an @mention or a ChatOps command) posted as soothfast-bot. The broker validates a `/comment` body before it authenticates, so a malformed request costs no mint. - `docs/ci.md` and `CLAUDE.md` describe the flow. The App's permission list is unchanged. Part of #158. ## Why Harvey's requirement is that soothfast-bot is the only identity soothfast writes with. A pull request runs unmerged code, so it must never hold a write credential; having the broker post the comment keeps the identity without handing the job anything. A tag push was excluded because a tag is not the default branch; requiring the tagged commit to already be on it makes the release equivalent to a `workflow_dispatch` on master. The same `environment` opt-in gates every request. ## How was this tested - `npm test`: 64 passed, 0 failed. New coverage: `/comment` creates or updates by marker and ignores other bots' comments, refuses a different pull request number, validates the body and size, revokes its token on every path; `/token` refuses `pull_request`; a tag is judged by `sha` (the fake asserts the compared head); `pull_request_target`, `issue_comment`, and head refs stay refused. - `tsc --noEmit`: clean. `shellcheck -x` on all eight scripts: clean. `zizmor` 1.29.0 on every workflow and `action.yml`: no findings. - `bot-token.sh` fails loudly with no OIDC token; `comment.sh` falls back to the job token when the broker is unreachable (the case CI is in until the Worker is deployed). - Not run end to end. The first exercise is this PR's own gate matrix once the broker is live (the comment should appear as soothfast-bot); the release path runs at the next tag. - `make check` and `make gate` were not run for this layer. It changes no Rust. Manual, before the first run: this repository's `soothfast-bot` environment must have no deployment branch policy, since the gate now uses it on pull requests and the release on tags. ## Checklist - [ ] `make check` passes (fmt, clippy `-D warnings`, `cargo test --workspace`) - [ ] `make gate BASE=master` passes, or any intentional cost change is explained above - [x] Tests added/updated for new behavior (not required for docs/CI-only changes) - [ ] Public API items have `///` doc comments - [x] No new dependency, or its justification is included above (see [Dependency Policy](../CONTRIBUTING.md#dependency-policy)) - [x] Docs (`README.md`, `docs/`, `soothfast:bind`/`soothfast:claim` markers) updated if behavior changed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What changed
The root
action.ymlwas "install the CLI". It is now the whole adopter surface: oneuses: Verdenroz/soothfast@<tag>step in an existing workflow gates pull requests and lands regenerated files as soothfast-bot on the default branch. Everything it runs lives inaction/*.sh, the same scripts this repo's own workflows call, so the adopter path is exercised here on every merge.action.ymlwithprepare,gate,regen,bot-token, andlandsteps, dispatching ongithub.event_nameand the default branch. New inputs:binary,packages(auto-detected fromcargo metadatawhen empty),gate,changelog,spec,baseline,rustdoc-toolchain,token,bot-token,bot-slug,broker. Addedbrandingfor the Marketplace.action/prepare.sh(default-branch lookup, early exit when a run has nothing to do, package detection, unshallow, base-ref fetch, valgrind on Debian-family runners, rustdoc toolchain),action/gate.sh(gate every package, last forty lines of each in one PR comment,failedoutput so the triage upload happens before the step fails),action/comment.sh(creates or updates the comment found by a marker line, sincegithub.token's author is shared with every other action), andaction/regen.sh(baseline, changelog against the latest tag, spec gen,changedoutput).regen.shreports a change, so a no-op push never asks the broker for a token.rustdoc-toolchaindefault to the nightly the release was tested with; the Makefile now reads that pin fromaction.ymlso there is one place to bump.contents: read,pull-requests: write, andid-token: write; nothing in the step needs contents write, the push goes through the bot token.docs/ci.md(setup, the environment's deployment side effects and the two-job alternative, what happens on each event, why the environment, every input, action-ref versus CLI-version coupling, bring-your-own-token, self-hosted runners, the reusable gate workflow). Addedci.mdto the site nav aftergating.md.Closes #158. Closes #14.
Why
Adopting soothfast should cost what codecov costs: one line in CI and one App install. Until now the bot pattern needed the App private key, which only the App owner has, so the changelog and spec flows could not leave this repo. With the broker in place, the missing piece was a single action that does the right thing on both events without a second workflow file. #14 asked for a token broker so the key never leaves one place; that is what
bot/is, so it closes here rather than as a separate service design.How was this tested
shellcheck0.11.0 on every script inaction/: clean.bash -non every script.cargo metadataauto-detection checked against this workspace: it lists the seven self-bench crates.bot/soothfast-updateauthored by soothfast-bot.make checkandmake gatewere not run for this layer. It changes no Rust.Checklist
make checkpasses (fmt, clippy-D warnings,cargo test --workspace)make gate BASE=masterpasses, or any intentional cost change is explained above///doc commentsREADME.md,docs/,soothfast:bind/soothfast:claimmarkers) updated if behavior changed