Skip to content

Commit f0db274

Browse files
committed
WIP
1 parent 460fb80 commit f0db274

3 files changed

Lines changed: 283 additions & 0 deletions

File tree

docs/contributing.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ To use a preview build for a package within a project, you need to override the
186186
187187
## Releasing changes
188188
189+
Before preparing a release, read [Release Branch Protocols](./release-branch-protocols.md)
190+
to understand which packages must be released together. The rules in that document — in
191+
particular the handling of `peerDependencies` across `@metamask/connect-multichain` and
192+
the ecosystem adapters (`@metamask/connect-evm`, `@metamask/connect-solana`) — drive
193+
which packages you should select in step 2 below.
194+
189195
Use the following process to release new packages in this repo:
190196
191197
1. **Start the release tool.**

docs/release-branch-protocols.md

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Release Branch Protocols
2+
3+
This document captures the rules that govern **which packages must be released together**
4+
when a release branch is prepared in `connect-monorepo`. These rules apply on top of the
5+
mechanical workflow described in [Releasing changes](./contributing.md#releasing-changes).
6+
7+
Most of these rules are enforced automatically by
8+
[`@metamask/create-release-branch`](https://github.com/MetaMask/create-release-branch) (see
9+
[Tooling enforcement](#tooling-enforcement)), but understanding them is essential for
10+
review (see [Reviewing Release PRs](./reviewing-release-prs.md)) and for cases where the
11+
tool requires a judgement call from the release author.
12+
13+
## Table of contents
14+
15+
- [Why these rules exist](#why-these-rules-exist)
16+
- [Definitions](#definitions)
17+
- [Core protocol](#core-protocol)
18+
- [Canonical scenarios](#canonical-scenarios)
19+
- [Operational checklist for release authors](#operational-checklist-for-release-authors)
20+
- [Tooling enforcement](#tooling-enforcement)
21+
- [Open gaps and future work](#open-gaps-and-future-work)
22+
23+
## Why these rules exist
24+
25+
Several public packages in this monorepo (`@metamask/connect-evm`,
26+
`@metamask/connect-solana`, ...) declare `@metamask/connect-multichain` as a **peer
27+
dependency**. From [`packages/connect-evm/package.json`](../packages/connect-evm/package.json)
28+
and [`packages/connect-solana/package.json`](../packages/connect-solana/package.json):
29+
30+
```json
31+
"peerDependencies": {
32+
"@metamask/connect-multichain": "workspace:^"
33+
}
34+
```
35+
36+
At publish time, Yarn rewrites `workspace:^` to `^<connect-multichain-version-at-publish>`
37+
in the published manifest. So if `connect-evm@1.4.0` is published while
38+
`connect-multichain` is at `1.0.0`, the published `connect-evm@1.4.0` manifest pins
39+
`@metamask/connect-multichain: ^1.0.0`.
40+
41+
This has two consequences a release must respect:
42+
43+
1. **Co-installability.** When a consumer installs `@metamask/connect-evm` and
44+
`@metamask/connect-solana` together (a supported configuration — see the
45+
[architecture diagram](./architecture.md#package-topology)), `npm` / `yarn` must be
46+
able to satisfy _both_ packages' peer dependency ranges with a _single_ resolved
47+
version of `@metamask/connect-multichain`. If one adapter ships with `^1.0.0` and the
48+
other ships with `^2.0.0`, the install fails (or, worse, silently picks one and warns).
49+
2. **Minimum-version honesty.** A new `connect-evm` published _immediately after_ a
50+
`connect-multichain` minor bump captures the new minor as its peer floor (because
51+
`workspace:^` resolves against the current workspace version). That is the right thing
52+
to do _if_ the new `connect-evm` actually relies on the new `connect-multichain`
53+
feature, and a no-op otherwise.
54+
55+
These two constraints drive the [core protocol](#core-protocol).
56+
57+
## Definitions
58+
59+
- **Ecosystem client** — a public workspace package that has
60+
`@metamask/connect-multichain` as a `peerDependency`. Currently:
61+
`@metamask/connect-evm`, `@metamask/connect-solana`.
62+
- **Relies on** — has source changes (since its previous published tag) that depend on
63+
the new behaviour or API surface of a new `@metamask/connect-multichain` release.
64+
Bumping just to refresh the peer range without consuming any new behaviour does **not**
65+
count as "relies on".
66+
- **Breaking change to a peer-dep'd package** — a `major` bump (per
67+
[SemVer](https://semver.org/)). This is the only kind of bump that invalidates an
68+
existing `^X.Y.Z` peer range on downstream packages.
69+
70+
## Core protocol
71+
72+
For each release branch:
73+
74+
1. **An ecosystem client MUST be released alongside `@metamask/connect-multichain` if and
75+
only if either:**
76+
- **(a)** it relies on the new `@metamask/connect-multichain` behaviour, or
77+
- **(b)** the `@metamask/connect-multichain` release is a `major` bump.
78+
79+
Case (a) is enforced by the changelog/review pass — if the adapter has source-level
80+
changes that depend on the new multichain release, the adapter has its own changelog
81+
entries and is included by `hasChangesSinceLatestRelease`.
82+
83+
Case (b) is enforced by `create-release-branch` (see
84+
[Tooling enforcement](#tooling-enforcement)).
85+
86+
2. **An ecosystem client MUST NOT be released solely because its peer dep was bumped
87+
non-breakingly.** A no-op release that re-tightens the `^1.0.0` floor to `^1.1.0`
88+
without any consumer-visible change is noise in `npm` and in changelogs. Skip it.
89+
90+
3. **All ecosystem clients that peer-depend on a package being released with a `major`
91+
bump MUST be released in the same release branch.** Otherwise, the unreleased
92+
ecosystem client will keep the previous peer range, which by definition no longer
93+
satisfies the new major, and co-installing the released ecosystem client with the
94+
unreleased one becomes impossible.
95+
96+
4. **The `intentionally-skip` directive is a last resort.** `create-release-branch`
97+
accepts `intentionally-skip` as a per-package value in the release spec to bypass a
98+
"missing peer dependent" error. Use it only when _both_ hold:
99+
- The skipped package genuinely does not need to be published (e.g. it has been
100+
deprecated or is unused), **and**
101+
- You have a follow-up plan to keep it consistent (e.g. retire the package, or
102+
publish it on a subsequent release).
103+
104+
Skipping for convenience (e.g. "I'll publish it later") creates the co-installability
105+
failure described above. If in doubt, do not skip.
106+
107+
## Canonical scenarios
108+
109+
The starting state for all three scenarios:
110+
111+
```text
112+
@metamask/connect-evm@1.0.0
113+
peerDependencies: { @metamask/connect-multichain: ^1.0.0 }
114+
115+
@metamask/connect-solana@1.0.0
116+
peerDependencies: { @metamask/connect-multichain: ^1.0.0 }
117+
118+
@metamask/connect-multichain@1.0.0
119+
```
120+
121+
### Scenario 1 — minor bump of the peer dep, nobody consumes it
122+
123+
`@metamask/connect-multichain@1.1.0` is released. No ecosystem client uses the new
124+
behaviour.
125+
126+
**Release set:** `@metamask/connect-multichain@1.1.0` only.
127+
128+
Why:
129+
130+
- `^1.0.0` already satisfies `1.1.0`, so the existing published `connect-evm@1.0.0` and
131+
`connect-solana@1.0.0` remain co-installable with `connect-multichain@1.1.0`.
132+
- Neither adapter relies on the new behaviour, so rule (a) does not apply.
133+
- Per rule 2, do **not** ship a no-op release of either adapter.
134+
135+
### Scenario 2 — minor bump of the peer dep, one adapter consumes it
136+
137+
`@metamask/connect-multichain@1.1.0` is released. `@metamask/connect-evm` is updated to
138+
use the new behaviour.
139+
140+
**Release set:** `@metamask/connect-multichain@1.1.0` **and**
141+
`@metamask/connect-evm@<new>`.
142+
143+
Why:
144+
145+
- `connect-evm` "relies on" the change (rule 1a), so it must ship. Because the published
146+
`connect-evm` resolves `workspace:^` against the new multichain version, the new
147+
`connect-evm` will ship with `peerDependencies.connect-multichain: ^1.1.0` — i.e. it
148+
correctly advertises the new minimum it needs.
149+
- `connect-solana` is unchanged and `^1.0.0` still satisfies `1.1.0`. Co-installing
150+
`connect-evm@<new>` (peer `^1.1.0`) with the still-published
151+
`connect-solana@1.0.0` (peer `^1.0.0`) resolves cleanly to `connect-multichain@1.1.0`.
152+
Per rule 2, do **not** release a no-op `connect-solana`.
153+
154+
### Scenario 3 — major (breaking) bump of the peer dep, one adapter consumes it
155+
156+
`@metamask/connect-multichain@2.0.0` is released. `@metamask/connect-evm` is updated to
157+
adopt the breaking change.
158+
159+
**Release set:** `@metamask/connect-multichain@2.0.0`, `@metamask/connect-evm@<new>`,
160+
**and** `@metamask/connect-solana@<new>`.
161+
162+
Why:
163+
164+
- `connect-evm` is required by rule 1a (relies on the change). The new `connect-evm`
165+
publishes with `peerDependencies.connect-multichain: ^2.0.0`.
166+
- `connect-solana` is required by **rule 3 / rule 1b**: even though it has no source
167+
changes, its currently-published peer range `^1.0.0` is incompatible with `2.0.0`. A
168+
consumer using both adapters together would otherwise get a peer-dependency conflict.
169+
Releasing a `connect-solana` whose only diff is the regenerated `^2.0.0` peer range
170+
restores co-installability.
171+
172+
This is the case `create-release-branch` raises as a hard error if you forget; see
173+
[Tooling enforcement](#tooling-enforcement).
174+
175+
## Operational checklist for release authors
176+
177+
When you run `yarn prepare-release`, work through this checklist before clicking
178+
"Confirm":
179+
180+
1. **Identify the set of public packages being released.** Filter to the ones with a
181+
version bump in the release spec.
182+
2. **For each package being released that other workspace packages list as a
183+
`peerDependency`:**
184+
- Is the bump a `major`? If yes, **every** workspace package that lists it as a peer
185+
dependency must also be in the release set. (`create-release-branch` enforces this;
186+
do not bypass with `intentionally-skip` casually — see rule 4.)
187+
- Is the bump `minor` or `patch`? Then ecosystem clients are included **only if** they
188+
have source changes that rely on the new behaviour. Adapters with no source-level
189+
dependence stay on their current published version.
190+
3. **For each package being released that lists a workspace package as a `dependency` or
191+
`peerDependency`:** the tool prompts you if the dependency itself has unreleased
192+
changes. Decide whether the depending package actually needs the new dependency
193+
version, and include / skip accordingly.
194+
4. **Sanity check co-installability.** After the tool finishes, the released ecosystem
195+
clients should all advertise compatible peer ranges. With `workspace:^`, this is
196+
automatic — but verify by inspecting the diffs to `packages/*/package.json` in the
197+
release branch.
198+
199+
## Tooling enforcement
200+
201+
`yarn prepare-release` (= `yarn create-release-branch -i` +
202+
`yarn bump-playground-versions`) enforces most of the above through
203+
`@metamask/create-release-branch` v4.1.3. Concretely:
204+
205+
- **Auto-population.** Packages with source changes since their last release
206+
(`hasChangesSinceLatestRelease`) are pre-listed in the release spec. The UI also adds
207+
peer dependents of any package the user marks as a `major` bump, so the user can pick
208+
a version for them directly. (File-based flow does not — see
209+
[Open gaps](#open-gaps-and-future-work).)
210+
- **Major-bump peer-dependent enforcement.** Implemented by
211+
`findMissingUnreleasedDependentsForBreakingChanges` (renamed to
212+
`findCandidateDependentsOfTypeForMajorBump` on upstream `main`). If a package is
213+
bumped `major` (either via the `major` directive or an explicit `X+1.0.0` version) and
214+
any workspace package that peer-depends on it is missing from the release spec, the
215+
tool refuses to proceed and prints the missing dependents.
216+
- **Missing-dependency warnings.** Implemented by `findMissingUnreleasedDependencies`.
217+
If a package is being released and one of its workspace `dependencies` or
218+
`peerDependencies` has unreleased changes but is not in the release spec, the tool
219+
raises an error. This is the safeguard for rule 1a.
220+
- **Peer-range regeneration.** Yarn's `workspace:^` protocol is what makes
221+
`peerDependencies.connect-multichain` track the freshly-bumped multichain version at
222+
publish time. The `yarn.config.cjs` constraints (see
223+
`expectUpToDateWorkspacePeerDependencies`) keep the source manifests using
224+
`workspace:^` instead of pinned ranges. Combined, these guarantee that any newly
225+
published ecosystem client correctly captures the current multichain version as its
226+
peer floor.
227+
- **Non-major bumps do not require dependents.** This is intentional — see Scenarios 1
228+
and 2. A `minor` / `patch` bump of `connect-multichain` does **not** force
229+
`connect-evm` or `connect-solana` into the release spec.
230+
231+
The only escape hatch is the per-package `intentionally-skip` directive in the release
232+
spec. See [rule 4](#core-protocol) for when it is appropriate.
233+
234+
## Open gaps and future work
235+
236+
These are known limitations of the current tooling that the release author has to cover
237+
manually. Some are good candidates for upstream contributions to
238+
`@metamask/create-release-branch`.
239+
240+
1. **File-based template does not include "required" peer dependents until you bump
241+
manually.** When you run `yarn create-release-branch` without `-i` (so the YAML
242+
editor flow rather than the UI), the template only lists packages with source
243+
changes. If `connect-multichain` has source changes but `connect-evm` /
244+
`connect-solana` do not, you have to add them to the YAML _and_ bump
245+
`connect-multichain` to `major` before the validator can tell you they were required.
246+
The interactive UI already does the right thing here (`requiredDependents` in
247+
`dist/ui.js`), so the fix would be to mirror that behaviour in
248+
`generateReleaseSpecificationTemplateForMonorepo` once a `major` selection is known.
249+
Practical mitigation: prefer `yarn prepare-release` (the interactive flow).
250+
2. **No automatic enforcement of rule 1a.** "`connect-evm` relies on the change" is a
251+
judgement call. The tool can detect that `connect-multichain` is in the release spec
252+
and that `connect-evm` has source changes (in which case
253+
`findMissingUnreleasedDependencies` raises if you forgot to include
254+
`connect-multichain`), but it cannot tell whether a `connect-evm` _without_ source
255+
changes silently relies on a new multichain feature. This must be caught in code
256+
review of the source diff before opening the release PR.
257+
3. **No automatic enforcement of "no no-op releases".** The tool does not refuse a
258+
release that has no consumer-visible changes. If a `connect-solana` ends up in the
259+
release set whose only diff is a manifest-level peer range refresh that isn't
260+
strictly required (i.e. a minor bump scenario where the old `^X.0.0` still satisfies
261+
the new minor), the validator does not flag it. Review-time vigilance is the current
262+
mitigation, captured in [reviewing-release-prs.md](./reviewing-release-prs.md).
263+
4. **`intentionally-skip` has no policy enforcement.** A user can suppress any of the
264+
above errors by adding `intentionally-skip` per package. There is currently no
265+
automated check that flags a release spec containing `intentionally-skip` for review.
266+
Until there is, treat any `intentionally-skip` entry in a release PR as something
267+
that must be explicitly justified in the PR description.

docs/reviewing-release-prs.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,19 @@ In order to publish a new release of the monorepo, a release PR must be created.
44

55
This document shows you how to do that.
66

7+
> Reviewers should also be familiar with the
8+
> [Release Branch Protocols](./release-branch-protocols.md), which define which
9+
> packages must be released together (especially in the presence of `peerDependencies`
10+
> between workspace packages). Those rules complement the
11+
> reviewer checklist below.
12+
713
## 0. tl;dr — if you've done this sort of thing before
814

915
- [ ] Are there any packages with version bumps that should not be published?
16+
- [ ] Does the release set follow the [Release Branch Protocols](./release-branch-protocols.md)? In particular:
17+
- [ ] If any package being released with a `major` bump is listed as a `peerDependency` of another workspace package, are **all** such workspace dependents also in the release set?
18+
- [ ] If any ecosystem client is in the release set, does it actually rely on changes in its peer dependency (rather than being a no-op release)?
19+
- [ ] If the release spec contains any `intentionally-skip` entries, is the justification explicit in the PR description?
1020
- For all packages being published:
1121
- [ ] Does the new version string sufficiently communicate the impact of changes?
1222
- [ ] Are there any changelog entries that do not need to be listed?

0 commit comments

Comments
 (0)