@wpelevator/wp-release 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,7 +97,7 @@ flowchart TD
97
97
  J -->|Yes| K["Workflow opens or updates the release pull request"]
98
98
  K --> L["Review and merge the release pull request"]
99
99
  L --> M["changeset-tag creates tags for packages changed by that release"]
100
- M --> N["GitHub Releases are created from changelog entries"]
100
+ M --> N["A workflow step creates GitHub Releases from the tag notes"]
101
101
  N --> O["Tag-triggered publish and split workflows run"]
102
102
  J -->|No| P["No package versions or release tags are created"]
103
103
  ```
@@ -186,13 +186,27 @@ To release packages separately, for example with a release pull request for each
186
186
 
187
187
  The `changeset-version` result lists each release with its `package`, `bump`, `currentVersion`, `nextVersion` and `changelog` entries, a Markdown list like the one added to `CHANGELOG.md`. Use it to name and describe the release pull request in a repository script, or call `versionChangesets()` from the library and format the entries of each release with `formatChangelogEntries( release.changesets )`.
188
188
 
189
- After merging the release PR, `changeset-tag --since <merge-commit>^1 --push` pushes tags only for packages changed by that release. It pushes one tag at a time because GitHub doesn't trigger workflows for a push of more than three tags. Add `--github-release` to create missing GitHub Releases; it requires `--push`. For initial adoption, preview all missing tags with `changeset-tag --dry-run` (without `--since`), then push if needed. To release each package with a separate workflow instead, `changeset-tag --since <merge-commit>^1 --untagged --github-output` lists the releases that don't have a tag yet, without creating any. Tag pushes need Git write credentials; GitHub Releases need authenticated `gh`.
189
+ After merging the release PR, `changeset-tag --since <merge-commit>^1 --push` pushes tags only for packages changed by that release. It pushes one tag at a time because GitHub doesn't trigger workflows for a push of more than three tags. wp-release doesn't create GitHub Releases: with `--json` or `--github-output`, each entry of `tags` has the `tag`, a `title` like `plugins/alpha@1.2.0`, the release `notes` and a `prerelease` flag, for a workflow step like [`softprops/action-gh-release`](https://github.com/softprops/action-gh-release) to create the release (see the example below). For initial adoption, preview all missing tags with `changeset-tag --dry-run` (without `--since`), then push if needed. To release each package with a separate workflow instead, `changeset-tag --since <merge-commit>^1 --untagged --github-output` lists the releases that don't have a tag yet, without creating any. Tag pushes need Git write credentials.
190
190
 
191
- To release a single package, for example from a manual workflow, pass `--package <name>`, repeatable, and with one package `--version <version>` to fail unless the package is at that version. Packages versioned by git tags have no version file, so for them `--version` is the version to tag. Before a tag is created, every `version.to` target must match the version, so a plugin `Version` header that disagrees with its readme `Stable tag` blocks the tag. GitHub Releases of versions with a pre-release part, like `1.0.0-rc.1`, are marked as pre-releases.
191
+ To release a single package, for example from a manual workflow, pass `--package <name>`, repeatable, and with one package `--version <version>` to fail unless the package is at that version. Packages versioned by git tags have no version file, so for them `--version` is the version to tag. Before a tag is created, every `version.to` target must match the version, so a plugin `Version` header that disagrees with its readme `Stable tag` blocks the tag. Versions with a pre-release part, like `1.0.0-rc.1`, have `prerelease` set, so their GitHub Releases can be marked as pre-releases:
192
+
193
+ ```yaml
194
+ - name: Tag a release
195
+ id: tag
196
+ run: npx wp-release changeset-tag --package "$PACKAGE" --version "$VERSION" --push --github-output
197
+
198
+ # Creates the GitHub Release, or updates it on a rerun.
199
+ - uses: softprops/action-gh-release@v3
200
+ with:
201
+ tag_name: ${{ fromJSON( steps.tag.outputs.result ).tags[0].tag }}
202
+ name: ${{ fromJSON( steps.tag.outputs.result ).tags[0].title }}
203
+ body: ${{ fromJSON( steps.tag.outputs.result ).tags[0].notes }}
204
+ prerelease: ${{ fromJSON( steps.tag.outputs.result ).tags[0].prerelease }}
205
+ ```
192
206
 
193
207
  The release notes are the version's `CHANGELOG.md` section, or without one the commits that changed the package since its previous tag, the highest version below the released one. Configure `releaseNotes( { package, version, tag, previousTag, changelog, commits, notes } )` to format them, for example to add links; `notes` is the default text and `commits` lists `{ hash, shortHash, subject }`, newest first. `changeset-tag --package <name> --dry-run` prints them without creating the tag.
194
208
 
195
- For local development, run the TypeScript source with `npm run wp-release -- <command>` (for example, `npm run wp-release -- changeset-status --since origin/main`). GitHub workflows use the same CLI for versioning and tagging. `SUBTREE_DEPLOY_TOKEN` lets them update the release PR, push tags, and create GitHub Releases.
209
+ For local development, run the TypeScript source with `npm run wp-release -- <command>` (for example, `npm run wp-release -- changeset-status --since origin/main`). GitHub workflows use the same CLI for versioning and tagging. `SUBTREE_DEPLOY_TOKEN` lets them update the release PR and push tags, and the `Tag Release` workflow passes it to `softprops/action-gh-release` to create the GitHub Releases.
196
210
 
197
211
  ### Naming convention
198
212
 
@@ -493,6 +507,20 @@ const { revision, created, pushed } = await new GitDeploy(
493
507
 
494
508
  `resolveGitDeployOptions()` throws a `ValidationError` without a repository URL or with an invalid branch or tag. `GitDeploy` exposes `createTree()`, `fetchPrevious()`, `createRelease()`, `isOutdated()`, `checkTag()`, `getRefsToPush()` and `push()` separately, like `SvnDeploy`, and takes a git command runner from `createGitRunner()` as a dependency. The result has `outdated` when the branch wasn't moved for older source history, the refs to write in `planned` and the refs that were pushed in `pushed`, which is empty in a dry run. `redactUrls()` hides the credentials in URLs.
495
509
 
510
+ Changeset releases:
511
+
512
+ ```js
513
+ import { ChangesetRelease } from '@wpelevator/wp-release';
514
+
515
+ const release = new ChangesetRelease( { cwd: process.cwd() } );
516
+
517
+ const { missingChangesets } = await release.getStatus( { since: 'origin/main' } );
518
+ const { releases } = await release.version( { dryRun: true } );
519
+ const { tags } = await release.tag( { dryRun: true } );
520
+ ```
521
+
522
+ `ChangesetRelease` runs the `changeset-*` commands: `add()`, `getStatus()`, `version()` and `tag()`, plus `getPlan()` and `getPackages()`. Each call reads the configuration, packages and pending changesets again, so one instance can version and then tag. It takes a git runner from `createGitRunner()` as `git`, so that tests can replace it. The functions `addChangeset()`, `getChangesetStatus()`, `versionChangesets()`, `tagChangesets()`, `getChangesetReleasePlan()` and `getChangesetPackages()` do the same with the default runners, in one call.
523
+
496
524
  ### Logging
497
525
 
498
526
  `Logger` filters messages by level and passes them to a writer, like the transports of other Node.js loggers. The writer decides how messages look:
@@ -598,6 +626,35 @@ npm run lint --workspace @wpelevator/wp-release
598
626
 
599
627
  The `wpelevator-source` export condition points at `src/index.ts`. The tests and the `wp-release-deploy-svn` action build use it to work with the source instead of a `dist/` build. It's not set by other tools, so the published package always resolves to `dist/`.
600
628
 
629
+ ### Architecture
630
+
631
+ The package uses ES modules as its unit of encapsulation, like most Node.js CLI tools: a module exports a few functions and keeps its helpers private, so there's no class to wrap them in. A class is used only when it holds state across steps or binds dependencies, so that callers and tests can replace them:
632
+
633
+ - **Workflows with steps** are classes, so that their steps share state and dependencies on the instance: `SvnDeploy` shares the temporary working copy across `prepare()`, `checkout()`, `sync()` and `commit()`, `GitDeploy` binds the options and the git runner to its steps, and `ChangesetRelease` binds the repository and the runners to `add()`, `version()` and `tag()`.
634
+ - **External commands** run through injectable runners: `createSvnRunner()` and `createGitRunner()` return functions that spawn the binary without a shell. The classes take a runner as an option and create the default one when it's missing, so tests pass a fake runner, or one that wraps the real runner and records some commands, instead of putting fake binaries on the `PATH`. The `Svn` and `ReleaseRepository` classes wrap a runner with the commands of their domain.
635
+ - **Domain logic** is plain functions that take their dependencies as arguments, like `discoverPackages( repository, config )`, so that each one can be tested on its own.
636
+ - **Commands** in `src/commands/` parse the CLI options and call the library, and `cli.ts` handles the global options, logging and exit codes. Errors carry their exit code, like `ValidationError` with `2`.
637
+ - Constructors only store their options. Work starts in a method call.
638
+
639
+ The changeset release modules, from the entry point down:
640
+
641
+ | Module | Responsibility |
642
+ |---|---|
643
+ | `changeset-release.ts` | `ChangesetRelease`, which plans releases and runs `add()`, `getStatus()`, `version()` and `tag()`, and the function form of each operation. |
644
+ | `changeset-config.ts` | Loading and validating `wp-release.config.js`, and the version source and target helpers. |
645
+ | `changeset-packages.ts` | Discovering the packages of the configured globs and finding the changed ones. |
646
+ | `changeset-files.ts` | Reading, writing and consuming changeset files. |
647
+ | `changeset-dependents.ts` | The dependents policy: which packages re-release with their sources, and rewriting their dependency specs. |
648
+ | `changesets.ts` | Parsing and formatting changesets, and the release types. |
649
+ | `version-files.ts` | Reading and writing versions in manifests, headers, readmes and PHP constants. |
650
+ | `changelog.ts` | Changelog entries, and the `CHANGELOG.md` and readme changelog sections. |
651
+ | `release-tags.ts` | Choosing the version and previous tag of a release, and building its release notes. |
652
+ | `tag-format.ts` | Formatting tag names from the tag format. |
653
+ | `release-repository.ts` | `ReleaseRepository`, the git commands of a release: tags, commits and changed files. |
654
+ | `github.ts` | Finding the pull request of a commit with the GitHub CLI, for changelog formatters. |
655
+ | `file-journal.ts` | `FileJournal`, which restores every file a failed `changeset-version` run changed. |
656
+ | `versions.ts` | Version checks and SemVer precedence. |
657
+
601
658
  ## Comparison with existing tools
602
659
 
603
660
  | Tool | What it does | How `wp-release` differs |
@@ -0,0 +1,45 @@
1
+ import type { ChangesetConfig } from './changeset-config.ts';
2
+ import type { PackageRelease, VersionedRelease } from './changesets.ts';
3
+ import type { FileJournal } from './file-journal.ts';
4
+ import type { ReleaseRepository } from './release-repository.ts';
5
+ /**
6
+ * Format the changelog entries of each release, with the configured
7
+ * formatter and the commit that added each changeset.
8
+ *
9
+ * @param repository Repository, for the commit of each changeset.
10
+ * @param config Repository release configuration.
11
+ * @param releases Package releases.
12
+ * @param changesetDirectory Directory of the changeset files.
13
+ * @return Releases with their changelog entries.
14
+ */
15
+ export declare function getVersionedReleases(repository: ReleaseRepository, config: ChangesetConfig, releases: PackageRelease[], changesetDirectory: string): Promise<VersionedRelease[]>;
16
+ /**
17
+ * Add the changelog section of a release to the CHANGELOG.md of the package,
18
+ * and with readmeChangelog to the changelog of its plugin readme. A release
19
+ * that already has a section is left as it is.
20
+ *
21
+ * @param directory Package directory.
22
+ * @param release Versioned release.
23
+ * @param date Release date, as YYYY-MM-DD.
24
+ * @param readmeChangelog Whether to update the plugin readme too.
25
+ * @param changelogInsertAfter Marker to insert the section after, if configured.
26
+ * @param journal File journal for rolling back.
27
+ * @throws {ValidationError} If the configured marker isn't found.
28
+ */
29
+ export declare function prependChangelog(directory: string, release: VersionedRelease, date: string, readmeChangelog: boolean, changelogInsertAfter: string | undefined, journal: FileJournal): Promise<void>;
30
+ /**
31
+ * Read the CHANGELOG.md section of a version, without its heading.
32
+ *
33
+ * @param directory Package directory.
34
+ * @param version Released version.
35
+ * @return Section contents, empty without a section.
36
+ */
37
+ export declare function readChangelogSection(directory: string, version: string): Promise<string>;
38
+ /**
39
+ * Read the newest stable version with a CHANGELOG.md section.
40
+ *
41
+ * @param directory Package directory.
42
+ * @param fallback Version to return without a changelog or a version section.
43
+ * @return Latest changelog version.
44
+ */
45
+ export declare function getLatestChangelogVersion(directory: string, fallback: string): Promise<string>;
@@ -0,0 +1,246 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { formatChangelogList } from "./changesets.js";
4
+ import { ValidationError } from "./errors.js";
5
+ import { escapeRegExp } from "./regexp.js";
6
+ /**
7
+ * Format the changelog entries of each release, with the configured
8
+ * formatter and the commit that added each changeset.
9
+ *
10
+ * @param repository Repository, for the commit of each changeset.
11
+ * @param config Repository release configuration.
12
+ * @param releases Package releases.
13
+ * @param changesetDirectory Directory of the changeset files.
14
+ * @return Releases with their changelog entries.
15
+ */
16
+ export async function getVersionedReleases(repository, config, releases, changesetDirectory) {
17
+ const commits = new Map();
18
+ const getCommit = (changeset) => {
19
+ const cached = commits.get(changeset.fileName) ??
20
+ repository.getAddedCommit(path.join(changesetDirectory, changeset.fileName));
21
+ commits.set(changeset.fileName, cached);
22
+ return cached;
23
+ };
24
+ const releasesByName = new Map(releases.map((release) => [release.package.name, release]));
25
+ return Promise.all(releases.map(async (release) => {
26
+ const entries = [];
27
+ // Format one at a time, so that formatters calling an API aren't rate limited.
28
+ for (const changeset of release.changesets) {
29
+ entries.push(await getChangelogEntry(config, release.package, changeset, getCommit));
30
+ }
31
+ for (const sourceName of release.sources ?? []) {
32
+ const source = releasesByName.get(sourceName);
33
+ if (!source) {
34
+ throw new ValidationError(`The dependent package "${release.package.name}" bundles unreleased package "${sourceName}".`);
35
+ }
36
+ entries.push(await getDependentsChangelogEntry(config, release.package, source));
37
+ }
38
+ return { ...release, changelog: formatChangelogList(entries) };
39
+ }));
40
+ }
41
+ /**
42
+ * Format the bundle changelog entry of a dependent release.
43
+ *
44
+ * @param config Repository release configuration.
45
+ * @param pkg Dependent package being re-released.
46
+ * @param source Released source package bundled by the dependent.
47
+ * @return Changelog entry.
48
+ */
49
+ async function getDependentsChangelogEntry(config, pkg, source) {
50
+ if (!config.dependents?.changelog) {
51
+ return `Bundle ${source.package.name.split('/').pop()} ${source.nextVersion}.`;
52
+ }
53
+ const entry = await config.dependents.changelog({
54
+ package: pkg,
55
+ source: {
56
+ package: source.package,
57
+ version: source.nextVersion,
58
+ },
59
+ });
60
+ if ('string' !== typeof entry) {
61
+ throw new ValidationError(`The dependents changelog formatter didn't return a string for package "${pkg.name}".`);
62
+ }
63
+ return entry.trim();
64
+ }
65
+ /**
66
+ * Format the changelog entry of a changeset with the configured formatter.
67
+ *
68
+ * @param config Repository release configuration.
69
+ * @param pkg Released package.
70
+ * @param changeset Changeset of the release.
71
+ * @param getCommit Get the commit that added a changeset.
72
+ * @return Changelog entry, the plain summary without a formatter.
73
+ */
74
+ async function getChangelogEntry(config, pkg, changeset, getCommit) {
75
+ const summary = changeset.summary.trim();
76
+ if (!summary || !config.changelogEntry) {
77
+ return summary;
78
+ }
79
+ const commit = await getCommit(changeset);
80
+ const entry = await config.changelogEntry({
81
+ package: pkg,
82
+ changeset,
83
+ ...(commit ? { commit } : {}),
84
+ });
85
+ if ('string' !== typeof entry) {
86
+ throw new ValidationError(`The changelogEntry formatter didn't return a string for changeset ${changeset.fileName}.`);
87
+ }
88
+ return entry.trim();
89
+ }
90
+ /**
91
+ * Add the changelog section of a release to the CHANGELOG.md of the package,
92
+ * and with readmeChangelog to the changelog of its plugin readme. A release
93
+ * that already has a section is left as it is.
94
+ *
95
+ * @param directory Package directory.
96
+ * @param release Versioned release.
97
+ * @param date Release date, as YYYY-MM-DD.
98
+ * @param readmeChangelog Whether to update the plugin readme too.
99
+ * @param changelogInsertAfter Marker to insert the section after, if configured.
100
+ * @param journal File journal for rolling back.
101
+ * @throws {ValidationError} If the configured marker isn't found.
102
+ */
103
+ export async function prependChangelog(directory, release, date, readmeChangelog, changelogInsertAfter, journal) {
104
+ const summaries = release.changesets
105
+ .map((item) => item.summary.trim())
106
+ .filter(Boolean);
107
+ const section = `## ${release.nextVersion} (${date})\n\n${release.changelog}\n\n`;
108
+ const changelog = path.join(directory, 'CHANGELOG.md');
109
+ let previous = '';
110
+ try {
111
+ previous = await readFile(changelog, 'utf8');
112
+ }
113
+ catch (error) {
114
+ if ('ENOENT' !== error.code) {
115
+ throw error;
116
+ }
117
+ }
118
+ const releaseHeading = new RegExp(`^## ${escapeRegExp(release.nextVersion)} \\(`, 'm');
119
+ if (!releaseHeading.test(previous)) {
120
+ await journal.remember(changelog);
121
+ if (undefined !== changelogInsertAfter) {
122
+ await writeFile(changelog, insertAfterMarker(previous, changelogInsertAfter, section, changelog));
123
+ }
124
+ else {
125
+ const versionHeading = /^##[ \t]+v?\d+(?:\.\d+){1,2}(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?(?:[ \t]+.*)?$/m.exec(previous);
126
+ const heading = /^#[ \t]+[^\r\n]+[ \t]*$/m.exec(previous);
127
+ const insertion = versionHeading ?? heading;
128
+ if (insertion) {
129
+ const before = previous
130
+ .slice(0, insertion.index)
131
+ .replace(/(?:\r?\n)*$/, '');
132
+ const after = previous.slice(insertion.index);
133
+ const prefix = before ? `${before}\n\n` : '';
134
+ await writeFile(changelog, `${prefix}${section}${after}`);
135
+ }
136
+ else {
137
+ await writeFile(changelog, `${section}${previous}`);
138
+ }
139
+ }
140
+ }
141
+ if (readmeChangelog) {
142
+ const readme = ['readme.txt', 'readme.md'].map((name) => path.join(directory, name));
143
+ let readmeUpdated = false;
144
+ let readmeFound = false;
145
+ let readmeMarkerFound = false;
146
+ let readmeAlreadyVersioned = false;
147
+ for (const file of readme) {
148
+ try {
149
+ const contents = await readFile(file, 'utf8');
150
+ readmeFound = true;
151
+ const defaultHeading = undefined === changelogInsertAfter
152
+ ? /^==\s*Changelog\s*==\s*$/im.exec(contents)?.[0]
153
+ : undefined;
154
+ const insertionMarker = changelogInsertAfter ?? defaultHeading;
155
+ readmeMarkerFound ||=
156
+ undefined !== insertionMarker &&
157
+ contents.includes(insertionMarker);
158
+ const alreadyVersioned = new RegExp(`^=\\s*${escapeRegExp(release.nextVersion)}\\s*=$`, 'm').test(contents);
159
+ readmeAlreadyVersioned ||= alreadyVersioned;
160
+ if (insertionMarker &&
161
+ !alreadyVersioned &&
162
+ !readmeUpdated &&
163
+ contents.includes(insertionMarker)) {
164
+ const readmeSection = `\n\n= ${release.nextVersion} =\n\n${summaries
165
+ .map((item) => `* ${item.replaceAll(/\r?\n/g, '\n ')}`)
166
+ .join('\n')}\n\n`;
167
+ await journal.remember(file);
168
+ await writeFile(file, insertAfterMarker(contents, insertionMarker, readmeSection.trimStart(), file));
169
+ readmeUpdated = true;
170
+ }
171
+ }
172
+ catch (error) {
173
+ if ('ENOENT' !== error.code) {
174
+ throw error;
175
+ }
176
+ }
177
+ }
178
+ if (changelogInsertAfter &&
179
+ readmeFound &&
180
+ !readmeMarkerFound &&
181
+ !readmeAlreadyVersioned) {
182
+ throw new ValidationError(`Changelog insertion marker "${changelogInsertAfter}" was not found in a plugin readme in ${directory}.`);
183
+ }
184
+ }
185
+ }
186
+ /**
187
+ * Read the CHANGELOG.md section of a version, without its heading.
188
+ *
189
+ * @param directory Package directory.
190
+ * @param version Released version.
191
+ * @return Section contents, empty without a section.
192
+ */
193
+ export async function readChangelogSection(directory, version) {
194
+ let contents = '';
195
+ try {
196
+ contents = await readFile(path.join(directory, 'CHANGELOG.md'), 'utf8');
197
+ }
198
+ catch (error) {
199
+ if ('ENOENT' !== error.code) {
200
+ throw error;
201
+ }
202
+ }
203
+ const lines = contents.split(/\r?\n/);
204
+ const headingIndex = lines.findIndex((line) => new RegExp(`^##\\s+${escapeRegExp(version)}(?:\\s|$)`).test(line));
205
+ if (-1 === headingIndex) {
206
+ return '';
207
+ }
208
+ const followingHeading = lines.findIndex((line, index) => index > headingIndex && /^##\s/.test(line));
209
+ const end = -1 === followingHeading ? lines.length : followingHeading;
210
+ return lines
211
+ .slice(headingIndex + 1, end)
212
+ .join('\n')
213
+ .trim();
214
+ }
215
+ /**
216
+ * Read the newest stable version with a CHANGELOG.md section.
217
+ *
218
+ * @param directory Package directory.
219
+ * @param fallback Version to return without a changelog or a version section.
220
+ * @return Latest changelog version.
221
+ */
222
+ export async function getLatestChangelogVersion(directory, fallback) {
223
+ try {
224
+ const contents = await readFile(path.join(directory, 'CHANGELOG.md'), 'utf8');
225
+ const match = /^##\s+(\d+\.\d+\.\d+)(?:\s|$)/m.exec(contents);
226
+ return match?.[1] ?? fallback;
227
+ }
228
+ catch (error) {
229
+ if ('ENOENT' === error.code) {
230
+ return fallback;
231
+ }
232
+ throw error;
233
+ }
234
+ }
235
+ function insertAfterMarker(contents, marker, section, file) {
236
+ const index = contents.indexOf(marker);
237
+ if (-1 === index) {
238
+ throw new ValidationError(`Changelog insertion marker "${marker}" was not found in ${file}.`);
239
+ }
240
+ const markerEnd = index + marker.length;
241
+ const after = contents
242
+ .slice(markerEnd)
243
+ .replace(/^(?:[ \t]*\r?\n)+/, '');
244
+ const lineEnding = contents.includes('\r\n') ? '\r\n' : '\n';
245
+ return `${contents.slice(0, markerEnd)}${lineEnding}${lineEnding}${section.replaceAll('\n', lineEnding)}${after}`;
246
+ }
@@ -0,0 +1,132 @@
1
+ import type { ChangesetConfig, DependentsConfig } from './changeset-config.ts';
2
+ import type { ChangedPackages, ConfiguredPackage } from './changeset-packages.ts';
3
+ import type { Changeset, ChangesetPackage, ChangesetReleasePlan, PackageRelease } from './changesets.ts';
4
+ import type { FileJournal } from './file-journal.ts';
5
+ /**
6
+ * A declared dependency of a package on another configured package.
7
+ */
8
+ export interface DependencySpec {
9
+ /** Absolute path of the manifest that declares the dependency. */
10
+ file: string;
11
+ /** Dependency name in the manifest, like the npm package name. */
12
+ name: string;
13
+ /** Declared spec, like ^1.2.0 or workspace:*. */
14
+ spec: string;
15
+ }
16
+ /**
17
+ * Add releases for dependents of packages that release, applying the
18
+ * configured dependents policy until the plan settles.
19
+ *
20
+ * @param releases Planned releases, mutated in place.
21
+ * @param packages Discovered packages.
22
+ * @param config Repository release configuration.
23
+ */
24
+ export declare function applyDependents(releases: PackageRelease[], packages: ConfiguredPackage[], config: ChangesetConfig): Promise<void>;
25
+ /**
26
+ * Limit a release plan to some packages and the changesets that name them.
27
+ *
28
+ * @param plan Release plan of all pending changesets.
29
+ * @param packages Configured packages.
30
+ * @param names Packages to version.
31
+ * @param config Repository release configuration.
32
+ * @param withDependents Whether the selection also releases the dependents of the selected sources.
33
+ * @return Release plan of the selected packages and their dependents.
34
+ * @throws {ValidationError} For unknown packages.
35
+ */
36
+ export declare function selectReleasePlan(plan: ChangesetReleasePlan, packages: ConfiguredPackage[], names: string[], config: ChangesetConfig, withDependents?: boolean): Promise<ChangesetReleasePlan>;
37
+ /**
38
+ * Drop bundle sources that aren't released by the selected plan.
39
+ *
40
+ * @param plan Selected release plan.
41
+ * @return Release plan with dependents bundling only its released sources.
42
+ */
43
+ export declare function expandDependentsPlan(plan: ChangesetReleasePlan): ChangesetReleasePlan;
44
+ /**
45
+ * Remove the derived dependent releases of a plan, keeping only the releases
46
+ * of packages that have their own changesets.
47
+ *
48
+ * @param plan Release plan of all pending changesets.
49
+ * @return Release plan without derived dependent releases.
50
+ */
51
+ export declare function stripDependentsPlan(plan: ChangesetReleasePlan): ChangesetReleasePlan;
52
+ /**
53
+ * Fail when a planned release bundles a source package that has pending
54
+ * changesets released by a later run, so that no dependent ships unreleased
55
+ * code under a changelog that cannot credit it.
56
+ *
57
+ * @param plan Planned releases.
58
+ * @param fullPlan Release plan of all pending changesets.
59
+ * @param packages Discovered packages.
60
+ * @param config Repository release configuration.
61
+ * @throws {ValidationError} If a source of a planned release stays pending.
62
+ */
63
+ export declare function assertDependentsSourcesReleased(plan: ChangesetReleasePlan, fullPlan: ChangesetReleasePlan, packages: ConfiguredPackage[], config: ChangesetConfig): Promise<void>;
64
+ /**
65
+ * Find the changed packages that need no changeset of their own, because the
66
+ * dependents policy releases them with their sources. A dependent releases
67
+ * with its sources, which the release pull request already announces when
68
+ * the changesets of the diff release one of them. Exempting its package
69
+ * manifest pin change keeps the check from demanding an entry for what
70
+ * versioning rewrites anyway; documentation and code changes still need
71
+ * their own changesets.
72
+ *
73
+ * @param changed Changed packages and their files.
74
+ * @param changesetsInDiff Changesets added or changed by the diff.
75
+ * @param packages Discovered packages.
76
+ * @param config Repository release configuration.
77
+ * @return Names of the packages whose only changes are manifests of released dependents.
78
+ */
79
+ export declare function findBundledPinChanges(changed: ChangedPackages, changesetsInDiff: Changeset[], packages: ConfiguredPackage[], config: ChangesetConfig): Promise<Set<string>>;
80
+ /**
81
+ * Check whether a changeset names a package.
82
+ *
83
+ * @param changesets Changesets.
84
+ * @param name Package name.
85
+ * @return Whether one of the changesets names the package.
86
+ */
87
+ export declare function hasChangeset(changesets: Changeset[], name: string): boolean;
88
+ /**
89
+ * Update how a dependent declares its dependency on a released source, with
90
+ * the configured hook or the built-in spec rewrite.
91
+ *
92
+ * @param input Dependent, source, released version, policy and journal.
93
+ * @param input.dependent The released dependent package.
94
+ * @param input.source Released source package that the dependent bundles.
95
+ * @param input.version Released version of the source.
96
+ * @param input.rule Dependents policy with its sections and dependency hooks.
97
+ * @param input.journal File journal for rolling back.
98
+ */
99
+ export declare function updateDependencyDeclaration(input: {
100
+ dependent: ChangesetPackage;
101
+ source: ChangesetPackage;
102
+ version: string;
103
+ rule?: DependentsConfig;
104
+ journal: FileJournal;
105
+ }): Promise<void>;
106
+ /**
107
+ * Update a dependency spec to the released version, keeping its shape, like
108
+ * the caret of ^0.9.0 or the x of 0.9.x.
109
+ *
110
+ * @param spec Declared spec of the source.
111
+ * @param version Released version of the source.
112
+ * @return The updated spec, undefined when nothing has to change, like a wildcard or a workspace spec, or null when the spec can't be rewritten.
113
+ */
114
+ export declare function updatedSpec(spec: string, version: string): string | null | undefined;
115
+ /**
116
+ * Warn about declared pins of configured packages that don't resolve the
117
+ * current version of the package they name.
118
+ *
119
+ * @param config Repository release configuration.
120
+ * @param packages Discovered packages.
121
+ * @return Warnings, one per package pair.
122
+ */
123
+ export declare function collectPinDriftWarnings(config: ChangesetConfig, packages: ConfiguredPackage[]): Promise<string[]>;
124
+ /**
125
+ * Check whether a spec points at the workspace copy of a package, by
126
+ * resolving the current version through its range or its workspace protocol.
127
+ *
128
+ * @param spec Declared spec of the source.
129
+ * @param currentVersion Current version of the source package.
130
+ * @return Whether the spec is a dependency of the source.
131
+ */
132
+ export declare function isDependencyOfSource(spec: string, currentVersion: string): boolean;