@@ -95,18 +95,19 @@ stay unpublished. There are three channels:
9595- ** Nightly** (` npm install -g agent-device@nightly ` ): every few hours the workflow checks ` main ` .
9696 It publishes ` X.Y.Z-nightly.<YYYYMMDD>.<run> ` under the ` nightly ` dist-tag when ` main ` 's HEAD has
9797 changed since the last nightly, its CI passed, and no nightly shipped that UTC day. It then tags
98- the commit ` v<nightly version> ` . To publish one now, run ** Release** with ` channel: nightly ` .
99- - ** Stable** (` latest ` ): run ** Release** with ` channel: stable ` . It promotes the latest nightly's
100- commit, by default as the ` X.Y.Z ` that nightly previewed. Set ` version ` to release it under
101- another number. The run tags that commit ` vX.Y.Z ` and starts the stable run on the tag. The stable
102- run waits for approval in the ` release ` environment, then publishes the packages, drafts the GitHub
103- release with generated notes, attaches the iOS runner and Android helper assets, publishes the
104- release and the MCP Registry entry, and commits ` main ` 's next ` -dev ` version. Pushing a ` vX.Y.Z `
105- tag yourself starts the same stable run for that commit.
98+ the commit ` nightly/v<nightly version> ` . To publish one now, run ** Release** on ` main ` .
99+ - ** Stable** (` latest ` ): a repository admin publishes a GitHub release with a new ` vX.Y.Z ` tag
100+ targeting ` main ` . Use the version ` main ` builds towards (its ` X.Y.Z-dev ` ), or a later one. Write the
101+ notes or generate them in the release form. The tag starts a ** Release** run, which publishes
102+ that commit once its CI has passed and the run is approved in the ` release ` environment. The run
103+ publishes the packages, attaches the iOS runner and Android helper assets to the release, publishes
104+ the MCP Registry entry, and commits ` main ` 's next ` -dev ` version. If you reject the run, delete the
105+ release and its tag. Pushing the tag instead (` git push origin main:refs/tags/vX.Y.Z ` ) starts the
106+ same run, which then drafts the release itself.
106107- ** Dry run** : a pull request that changes release tooling builds and verifies every package with a
107108 nightly version, and publishes nothing.
108109
109- To retry a failed run, re-run its failed jobs. The publisher skips package versions that are already
110+ To retry a failed run, re-run its failed jobs, or run ** Release ** on the release tag . The publisher skips package versions that are already
110111on npm, so a partial publish completes on the next attempt. To preview the packages locally, run
111112` pnpm release:prepare ` . It builds, verifies, and packs every public package into ` .tmp/release ` .
112113
@@ -131,6 +132,42 @@ npm trust github @agent-device/<name> --file release.yml --repository callstack/
131132npm access set mfa=publish @agent-device/< name>
132133```
133134
135+ ### One-time repository setup
136+
137+ npm trusts every run of ` release.yml ` that reaches the ` npm-publish ` environment, so GitHub
138+ settings, not the workflow, decide who can publish. The plan job refuses to publish until the first
139+ three are in place. Apply them before granting npm trust:
140+
141+ 1 . ** Environment ` npm-publish ` ** (Settings → Environments): deployment branches and tags limited to
142+ the branch ` main ` and the tag pattern ` v* ` . No reviewers and no secrets, so nightlies run
143+ unattended.
144+ 2 . ** Environment ` release ` ** : required reviewers. Turn on "Prevent self-review" only when another
145+ maintainer can approve, because the admin who creates the tag cannot approve their own run.
146+ 3 . ** Tag ruleset** (Settings → Rules → Rulesets → New tag ruleset): active, targeting
147+ ` refs/tags/v* ` , restricting creations, updates, and deletions, with only the Repository admin
148+ role allowed to bypass. Nightly tags live under ` nightly/ ` and stay outside it.
149+ 4 . ** Actions** (Settings → Actions → General): workflow permissions set to read repository contents,
150+ and "Allow GitHub Actions to create and approve pull requests" turned off.
151+ 5 . ** Releases** (Settings → General): leave release immutability off, because the run attaches its
152+ assets after the release is published.
153+
154+ Confirm the result, then grant npm trust to each public package and, once a nightly has published,
155+ refuse token-based publishing:
156+
157+ ``` bash
158+ gh api repos/callstack/agent-device/environments/npm-publish/deployment-branch-policies \
159+ --jq ' .branch_policies[] | "\(.type) \(.name)"' # branch main, tag v*
160+ gh api repos/callstack/agent-device/environments/release \
161+ --jq ' .protection_rules[] | select(.type == "required_reviewers")'
162+ gh api repos/callstack/agent-device/rulesets --jq ' .[] | select(.target == "tag") | .name'
163+ npm trust github < package> --file release.yml --repository callstack/agent-device \
164+ --environment npm-publish --allow-publish --yes
165+ npm access set mfa=publish < package>
166+ ```
167+
168+ Anyone who can push to ` main ` can still change what the next nightly publishes; ` main ` does not
169+ require pull requests.
170+
134171### The version on main never equals a published version
135172
136173` main ` carries ` X.Y.Z-dev ` , naming the release it is building towards. Nightlies preview it as
0 commit comments