Skip to content

Commit e957d78

Browse files
committed
docs(release): document releasing from main and the one-time repository setup
1 parent 47789c9 commit e957d78

1 file changed

Lines changed: 46 additions & 9 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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
110111
on 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/
131132
npm 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

Comments
 (0)