Skip to content

Commit cca68bd

Browse files
authored
Add tests, docs, change file; finalize lint/build/test
Agent-Logs-Url: https://github.com/microsoft/beachball/sessions/dd2b3791-2992-450d-9f89-6b00c7ef6c81
1 parent 1152641 commit cca68bd

13 files changed

Lines changed: 565 additions & 39 deletions

File tree

PLAN.md

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# Implementation plan: rename `canary``prerelease`, simplify ChangeType
22

3-
This is a working document tracked alongside the implementation. It will be
4-
updated as discoveries are made or decisions change.
3+
This document tracked the plan as it was implemented.
54

65
## Decisions (open questions resolved)
76

8-
1. `prereleasePrefix` defaults to `"prerelease"` so `beachball prerelease` works
9-
with zero configuration.
7+
1. `prereleasePrefix` defaults to `"prerelease"` (applied in the `prerelease`
8+
command itself rather than in `getDefaultOptions`, to keep `configList`
9+
output minimal). So `beachball prerelease` works with zero configuration.
1010
2. With `identifierBase: false` and the same target version already published,
1111
`beachball prerelease` errors with a message recommending `identifierBase: '0'`.
1212

@@ -19,44 +19,65 @@ updated as discoveries are made or decisions change.
1919
- Shrink `ChangeType` to `'patch' | 'minor' | 'major' | 'none'`.
2020
- Migration: change files / CLI types `premajor|preminor|prepatch` are coerced
2121
to `major|minor|patch` with a warning; `prerelease` is a hard error.
22-
- `publish` strips an existing prerelease component from `package.json#version`
23-
before applying the change file's bump (assumption E).
24-
- New `getPrereleaseVersion(...)` helper; `beachball prerelease` queries
22+
- `bump`/`publish` strip an existing prerelease component from
23+
`package.json#version` before applying the change file's bump (assumption E).
24+
- New `getPrereleaseVersion(...)` pure helper; `beachball prerelease` queries
2525
`listPackageVersions` for the highest existing counter.
2626

2727
## Checklist
2828

29-
- [ ] Explore the codebase to confirm exact file shapes/locations
30-
- [ ] Update `ChangeType` in `src/types/ChangeInfo.ts`
31-
- [ ] Migration logic for old types in `src/changefile/readChangeFiles.ts`
32-
(warn + coerce `pre*`, error on `prerelease`)
33-
- [ ] CLI `--type` validation in `src/changefile/getPackageChangeTypes.ts`
34-
(or wherever `--type` is parsed) — same coercion + error
35-
- [ ] `bumpPackageInfoVersion`: drop `prereleasePrefix`/`identifierBase` params,
29+
- [x] Update `ChangeType` in `src/types/ChangeInfo.ts`
30+
- [x] Migration logic for old types in `src/changefile/readChangeFiles.ts`
31+
(warn + coerce `pre*`, error on `prerelease`); also handles
32+
`dependentChangeType`
33+
- [x] CLI `--type` and `--dependent-change-type` migration in
34+
`src/options/getCliOptions.ts` (same coercion + error)
35+
- [x] `bumpPackageInfoVersion`: drop `prereleasePrefix`/`identifierBase` params,
3636
strip prerelease component before `semver.inc` (handles assumption E)
37-
- [ ] New `src/bump/getPrereleaseVersion.ts` pure helper + unit tests
38-
- [ ] Rename `src/commands/canary.ts``src/commands/prerelease.ts`; rewrite
37+
- [x] New `src/bump/getPrereleaseVersion.ts` pure helper + unit tests
38+
- [x] Rename `src/commands/canary.ts``src/commands/prerelease.ts`; rewrite
3939
to use `listPackageVersions` + `getPrereleaseVersion`
40-
- [ ] `src/cli.ts`: replace `canary` case with `prerelease`
41-
- [ ] `src/types/BeachballOptions.ts`: drop `canaryName`; `command` includes
42-
`prerelease` (and not `canary`)
43-
- [ ] `src/options/getCliOptions.ts`: drop `canaryName`; derive `tag` from
40+
- [x] `src/cli.ts`: replace `canary` case with `prerelease`
41+
- [x] `src/types/BeachballOptions.ts`: drop `canaryName`
42+
- [x] `src/options/getCliOptions.ts`: drop `canaryName`; derive `tag` from
4443
`prereleasePrefix` for the `prerelease` command
45-
- [ ] `src/options/getDefaultOptions.ts`: default `prereleasePrefix: 'prerelease'`,
46-
drop `canaryName` default
47-
- [ ] `src/commands/configGet.ts`: drop `canaryName: true`
48-
- [ ] `src/help.ts`: replace `--canary-name` docs with `prerelease` section;
44+
- [x] `src/options/getDefaultOptions.ts`: drop `canaryName` default (default
45+
for `prereleasePrefix` is applied at command level, not here)
46+
- [x] `src/commands/configGet.ts`: drop `canaryName: true`
47+
- [x] `src/help.ts`: replace `--canary-name` docs with `prerelease` section;
4948
remove `--prerelease-prefix` from `bump`/`publish` sections
50-
- [ ] `src/changefile/getQuestionsForPackage.ts`: drop "Prerelease" choice
51-
- [ ] Changelog rendering: drop `pre*`/`prerelease` `groupNames` entries; prune snapshots
52-
- [ ] Tests: update/remove canary references; add new tests per plan
53-
- [ ] Docs: rename `docs/cli/canary.md``docs/cli/prerelease.md`; update
54-
sidebar; update `bump`/`publish` pages; update `configuration.md`,
55-
`change-types.md`; sentence-case headings; build with `yarn docs:build`
56-
- [ ] Generate Beachball change file (major) via `/beachball-change-files`
57-
- [ ] Run `yarn build`, `yarn test`, `yarn lint`, `yarn format`
49+
- [x] `src/changefile/getQuestionsForPackage.ts`: drop "Prerelease" choice
50+
- [x] Changelog rendering: drop `pre*`/`prerelease` `groupNames` entries;
51+
prune snapshot
52+
- [x] Update existing tests (configList snapshot, getCliOptions, bumpInMemory,
53+
bumpPackageInfoVersion, changeTypes, getQuestionsForPackage,
54+
updateRelatedChangeType, writeChangelog, e2e bump)
55+
- [x] Add new tests: - `__tests__/bump/getPrereleaseVersion.test.ts` - `__functional__/commands/prerelease.test.ts` - migration tests in `__functional__/changefile/readChangeFiles.test.ts` - legacy `--type` / `--dependent-change-type` tests in
56+
`__functional__/options/getCliOptions.test.ts`
57+
- [x] Docs: create `docs/cli/prerelease.md`; add to sidebar in
58+
`docs/.vuepress/config.ts`; remove `--prerelease-prefix` rows from
59+
`docs/cli/bump.md` and `docs/cli/publish.md`; rewrite the
60+
`prereleasePrefix` row in `docs/overview/configuration.md`; add
61+
"Prereleases" section to `docs/concepts/change-types.md`. Built with
62+
`yarn docs:build`.
63+
- [x] Generate Beachball change file (major)
64+
- [x] Run `yarn build`, `yarn test`, `yarn lint`
5865
- [ ] `parallel_validation`
5966

6067
## Notes/discoveries
6168

62-
(to be filled in as work progresses)
69+
- The single repo fixture has only the package `foo`; tests use that name.
70+
- `createCommandContext` is `@deprecated` and triggers `etc/no-deprecated`;
71+
command implementations and their tests use
72+
`// eslint-disable-next-line etc/no-deprecated -- ...` to suppress.
73+
- `cliOptions.command === 'prerelease'` overrides `--tag`, mirroring previous
74+
canary behavior. `--prerelease-prefix` outside of the `prerelease` command
75+
does NOT affect tag.
76+
- `bumpInMemory.test.ts` previously had four `prereleasePrefix`-driven
77+
scenarios; all are removed since `bump` no longer produces prereleases.
78+
Replaced with a focused test for prerelease → release promotion.
79+
- `updateRelatedChangeType` tests previously expected `preminor` as a fallback
80+
when `minor` was disallowed; with pre\* types removed, the algorithm now
81+
falls back to `patch` instead.
82+
- `writeChangelog` "includes pre\* changes" tests are obsolete; replaced with
83+
one test exercising `major`/`minor`/`patch` headers.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"packageName": "beachball",
3+
"type": "major",
4+
"dependentChangeType": "patch",
5+
"comment": "Rename `canary` command to `prerelease`, repurpose `prereleasePrefix` (and remove `canaryName`), and shrink `ChangeType` to `patch | minor | major | none`. Old `pre*` change types in change files are auto-coerced (with a warning); `prerelease` change type is now an error. `bump`/`publish` no longer produce prerelease versions and now strip any prerelease component from the current version before bumping (prerelease → release promotion).",
6+
"email": "198982749+Copilot@users.noreply.github.com"
7+
}

docs/.vuepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export default defineUserConfig({
4242
'/cli/change',
4343
'/cli/check',
4444
'/cli/config',
45+
'/cli/prerelease',
4546
'/cli/publish',
4647
'/cli/sync',
4748
],

docs/cli/bump.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ $ beachball bump
2020

2121
[General options](./options) also apply for this command.
2222

23-
| Option | Description |
24-
| --------------------- | -------------------------------------------------------------------------------- |
25-
| `--keep-change-files` | don't delete the change files from disk after bumping |
26-
| `--prerelease-prefix` | prerelease prefix (e.g. `beta`) for packages that will receive a prerelease bump |
23+
| Option | Description |
24+
| --------------------- | ----------------------------------------------------- |
25+
| `--keep-change-files` | don't delete the change files from disk after bumping |

docs/cli/prerelease.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
tags:
3+
- cli
4+
category: doc
5+
---
6+
7+
# `prerelease`
8+
9+
Publishes a prerelease version (such as a canary, beta, or per-PR release) for the current change set, **without committing changes back to git or deleting change files**.
10+
11+
```bash
12+
$ beachball prerelease
13+
```
14+
15+
This command is useful for sharing prereleases of in-progress work — for example, a per-PR build that consumers can install to test changes before merging.
16+
17+
## How it works
18+
19+
For each package that has a change file (or that becomes modified due to dependency bumps), `beachball prerelease`:
20+
21+
1. Looks up the change type from the change files (just like `bump` and `publish`).
22+
2. Computes the **target release version** by applying the change type to the current `package.json` version, after stripping any existing prerelease component. (For example, `0.2.0-beta.0` with a `minor` change type produces a target of `0.3.0`.)
23+
3. Queries the npm registry for existing published versions that match `<target>-<prereleasePrefix>.<n>`, finds the highest counter `N`, and publishes `<target>-<prereleasePrefix>.<N+1>`.
24+
4. Publishes to npm under the dist-tag matching `prereleasePrefix` (e.g. `beta`).
25+
26+
If no matching prerelease versions have been published yet, the counter starts from [`identifierBase`](#options).
27+
28+
The change files and `package.json` versions are **not** committed back to git. This means subsequent prerelease runs from the same change set will produce incrementing prerelease versions — useful for iterating on a PR without polluting git history.
29+
30+
## Workflow example
31+
32+
| Step | Command | Result |
33+
| ---- | ---------------------------------------------------------------- | ------------------------------------------------------------------- |
34+
| 1 | A developer creates a change file for a `minor` change to `foo`. | `change/foo-….json` exists; `foo` is at version `1.2.3`. |
35+
| 2 | CI runs `beachball prerelease --prerelease-prefix pr30`. | `foo@1.3.0-pr30.0` is published with dist-tag `pr30`. |
36+
| 3 | The developer pushes another commit to the PR; CI re-runs. | `foo@1.3.0-pr30.1` is published. |
37+
| 4 | The PR is merged; main runs `beachball publish`. | `foo@1.3.0` is published with dist-tag `latest`, normal git commit. |
38+
39+
Step 4 above demonstrates the **prerelease-to-release promotion** behavior of `bump`/`publish`: when a package's current `package.json` version contains a prerelease component (such as if `prerelease` ran on the same branch as `publish` would later run), the prerelease component is stripped before applying the bump, so the user gets the intuitive target release version.
40+
41+
## Options
42+
43+
[General options](./options) also apply for this command.
44+
45+
<!-- prettier-ignore -->
46+
| Option | Default | Description |
47+
| ------ | ------- | ----------- |
48+
| `--prerelease-prefix` | `'prerelease'` | Suffix used for the prerelease version, e.g. `"beta"` produces `1.2.3-beta.0`. The same value is used as the npm dist-tag. |
49+
| `--identifier-base` | `'0'` | Starting counter for prereleases when no matching versions are published yet. `'0'` (default) starts at `.0`, `'1'` starts at `.1`, or `false` omits the numeric counter entirely (in which case re-running on the same target version will error). |
50+
51+
## Migrating from `beachball canary`
52+
53+
This command replaces the old `beachball canary` command. The previous `canaryName` option has been removed; use `prereleasePrefix` instead.
54+
55+
In addition, `bump` and `publish` no longer support producing prerelease versions directly. If you previously used `prereleasePrefix` with `bump`/`publish`, switch to `beachball prerelease` for prerelease publishing.

docs/cli/publish.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Publishing automates all the bumping and synchronizing of package versions in th
1919
| `--git-tags`, `--no-git-tags` | | `true` (`--git-tags`) | whether to create git tags for published package versions |
2020
| `--keep-change-files` | | | don't delete the change files from disk after bumping |
2121
| `--message` | `-m` | `'applying package updates'` | custom commit message |
22-
| `--prerelease-prefix` | | | prerelease prefix (e.g. `beta`) for packages that will receive a prerelease bump |
2322
| `--publish`, `--no-publish` | | `true` (`--publish`) | whether to publish to the npm registry |
2423
| `--push`, `--no-push` | | `true` (`--push`) | whether to commit changes and push them back to the git remote |
2524
| `--registry` | `-r` | `'https://registry.npmjs.org'` | npm registry for publishing |

docs/concepts/change-types.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ Packages with a major version of 0 are considered unstable per the semver spec.
3838

3939
Some repos or packages may restrict which change types are allowed using the [`disallowedChangeTypes`](../overview/configuration#options) config option. For example, `major` bumps are often disallowed to ensure coordination of major release efforts. Any disallowed options will be omitted from the interactive prompt, and a change file or `--type` argument that uses a disallowed type will cause an error.
4040

41+
## Prereleases
42+
43+
To publish a prerelease version (such as a canary, beta, or per-PR build), use the [`beachball prerelease`](../cli/prerelease) command. There is no `prerelease` change type — instead, choose one of `patch`, `minor`, `major`, or `none` based on the impact of your changes, and let `beachball prerelease` handle the prerelease versioning.
44+
45+
> **Note:** Older Beachball versions accepted `premajor`, `preminor`, `prepatch`, and `prerelease` as change types. These have been removed; existing change files using `premajor`/`preminor`/`prepatch` are auto-migrated to `major`/`minor`/`patch` (with a deprecation warning), and change files using `prerelease` will produce an error so they can be recreated with the appropriate type.
46+
4147
## Tips for reviewers
4248

4349
Change files show up as part of the PR diff, making it easy to verify the change type during code review. Common things to watch for:

docs/overview/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ For the latest full list of supported options, see `RepoOptions` [in this file](
9393
| `ignorePatterns` | `string[]` | | repo | Ignore changes in files matching these glob patterns ([see notes][6]) |
9494
| `npmReadConcurrency` | number | 5 | repo | Maximum concurrency for fetching package versions from the registry (see `concurrency` for write operations) |
9595
| `package` | `string` | | repo | Specifies which package the command relates to (overrides change detection based on `git diff`) |
96-
| `prereleasePrefix` | `string` | | repo | Prerelease prefix, e.g. `"beta"`. Note that if this is specified, packages with change type major/minor/patch will be bumped as prerelease instead. |
96+
| `prereleasePrefix` | `string` | `'prerelease'` | repo | Suffix used by the [`prerelease`](../cli/prerelease) command for prerelease versions, e.g. `"beta"` produces versions like `1.2.3-beta.0`. |
9797
| `packStyle` | `'sequential' \| 'layer'` | `'sequential'` | repo | With `packToPath`, how to organize the tgz files. `'sequential'` uses numeric prefixes to ensure topological ordering. `'layer'` groups the packages into numbered subfolders based on dependency tree layers. |
9898
| `packToPath` | `string` | | repo | Instead of publishing to npm, pack packages to tgz files under the specified path. |
9999
| `publish` | `boolean` | `true` | repo | Whether to publish to npm registry |

packages/beachball/src/__functional__/changefile/readChangeFiles.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,46 @@ describe('readChangeFiles', () => {
188188
expect(logs.mocks.warn).not.toHaveBeenCalled();
189189
});
190190

191+
it.each<['premajor' | 'preminor' | 'prepatch', 'major' | 'minor' | 'patch']>([
192+
['premajor', 'major'],
193+
['preminor', 'minor'],
194+
['prepatch', 'patch'],
195+
])('coerces legacy change type %s to %s with a warning', (legacyType, replacement) => {
196+
tempRoot = createTestFileStructureType('monorepo');
197+
const { options, packageInfos, scopedPackages } = getOptionsAndPackages();
198+
199+
generateChangeFiles([{ packageName: 'foo', type: legacyType as 'patch' }], options);
200+
const changeSet = readChangeFiles(options, packageInfos, scopedPackages);
201+
202+
expect(changeSet).toHaveLength(1);
203+
expect(changeSet[0].change.type).toBe(replacement);
204+
expect(logs.mocks.warn).toHaveBeenCalledWith(
205+
expect.stringContaining(`legacy change type "${legacyType}", which has been renamed to "${replacement}"`)
206+
);
207+
});
208+
209+
it('errors on legacy change type "prerelease"', () => {
210+
tempRoot = createTestFileStructureType('monorepo');
211+
const { options, packageInfos, scopedPackages } = getOptionsAndPackages();
212+
213+
generateChangeFiles([{ packageName: 'foo', type: 'prerelease' as 'patch' }], options);
214+
215+
expect(() => readChangeFiles(options, packageInfos, scopedPackages)).toThrow(
216+
/change type "prerelease", which is no longer supported/
217+
);
218+
});
219+
220+
it('coerces legacy dependentChangeType "preminor" to "minor" with a warning', () => {
221+
tempRoot = createTestFileStructureType('monorepo');
222+
const { options, packageInfos, scopedPackages } = getOptionsAndPackages();
223+
224+
generateChangeFiles([{ packageName: 'foo', type: 'minor', dependentChangeType: 'preminor' as 'minor' }], options);
225+
const changeSet = readChangeFiles(options, packageInfos, scopedPackages);
226+
227+
expect(changeSet[0].change.dependentChangeType).toBe('minor');
228+
expect(logs.mocks.warn).toHaveBeenCalledWith(expect.stringContaining('legacy dependentChangeType "preminor"'));
229+
});
230+
191231
it('runs transform.changeFiles functions if provided', () => {
192232
const editedComment = 'Edited comment for testing';
193233
tempRoot = createTestFileStructureType('monorepo');

0 commit comments

Comments
 (0)