@pnpm/releasing.versioning 1100.2.0 → 1100.2.1
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/CHANGELOG.md +12 -0
- package/lib/applyReleasePlan.d.ts +35 -0
- package/lib/applyReleasePlan.js +115 -0
- package/lib/assembleReleasePlan.d.ts +99 -0
- package/lib/assembleReleasePlan.js +714 -0
- package/lib/changelog.d.ts +12 -0
- package/lib/changelog.js +84 -0
- package/lib/index.d.ts +6 -0
- package/lib/intents.d.ts +17 -0
- package/lib/intents.js +76 -0
- package/lib/ledger.d.ts +50 -0
- package/lib/ledger.js +146 -0
- package/lib/pendingChangelog.d.ts +26 -0
- package/lib/pendingChangelog.js +84 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @pnpm/releasing.versioning
|
|
2
2
|
|
|
3
|
+
## 1100.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Republished every package: the tarballs published by the v11.13.1 through v11.16.0 releases were missing most of their compiled files due to a packing bug [#13164](https://github.com/pnpm/pnpm/issues/13164).
|
|
8
|
+
|
|
9
|
+
- Updated dependencies:
|
|
10
|
+
- @pnpm/error@1100.1.0
|
|
11
|
+
- @pnpm/types@1101.6.0
|
|
12
|
+
- @pnpm/workspace.project-manifest-reader@1100.0.20
|
|
13
|
+
- @pnpm/workspace.spec-parser@1100.0.1
|
|
14
|
+
|
|
3
15
|
## 1100.2.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { VersioningChangelogStorage, VersioningSettings } from '@pnpm/types';
|
|
2
|
+
import { type ReleasePlan, type WorkspaceProject } from './assembleReleasePlan.js';
|
|
3
|
+
import type { ChangeIntent } from './intents.js';
|
|
4
|
+
/**
|
|
5
|
+
* Registry storage is the default: no CHANGELOG.md is committed; a release's
|
|
6
|
+
* section is parked (see {@link writePendingChangelog}) and packed into the
|
|
7
|
+
* published tarball. `repository` opts back into committed changelogs.
|
|
8
|
+
*/
|
|
9
|
+
export declare function changelogStorage(versioning?: VersioningSettings): VersioningChangelogStorage;
|
|
10
|
+
export interface ApplyReleasePlanOptions {
|
|
11
|
+
workspaceDir: string;
|
|
12
|
+
projects: WorkspaceProject[];
|
|
13
|
+
/**
|
|
14
|
+
* All intent files currently in the workspace, used to decide which files
|
|
15
|
+
* are fully consumed after this run and can be deleted.
|
|
16
|
+
*/
|
|
17
|
+
allIntents: ChangeIntent[];
|
|
18
|
+
versioning?: VersioningSettings;
|
|
19
|
+
/**
|
|
20
|
+
* In `registry` changelog storage, the gate that lets a ledgered release's
|
|
21
|
+
* intents be garbage-collected: it must resolve `true` only once the
|
|
22
|
+
* registry has published `${name}@${version}` and that tarball's
|
|
23
|
+
* CHANGELOG.md already contains `section`. Without it, registry-mode
|
|
24
|
+
* releases keep their intents (the repository is still their only prose).
|
|
25
|
+
* Ignored in `repository` storage, where the committed changelog already
|
|
26
|
+
* holds the prose and the ledger alone gates deletion.
|
|
27
|
+
*/
|
|
28
|
+
verifyPublished?: (name: string, version: string, section: string) => Promise<boolean>;
|
|
29
|
+
}
|
|
30
|
+
export interface AppliedRelease {
|
|
31
|
+
name: string;
|
|
32
|
+
currentVersion: string;
|
|
33
|
+
newVersion: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function applyReleasePlan(plan: ReleasePlan, opts: ApplyReleasePlanOptions): Promise<AppliedRelease[]>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { readProjectManifest } from '@pnpm/workspace.project-manifest-reader';
|
|
3
|
+
import { indexProjectRefs } from './assembleReleasePlan.js';
|
|
4
|
+
import { composeChangelogSection, prependChangelogSection } from './changelog.js';
|
|
5
|
+
import { appendToLedger, buildConsumptionIndex } from './ledger.js';
|
|
6
|
+
import { listPendingChangelogs, readPendingChangelog, removePendingChangelog, writePendingChangelog } from './pendingChangelog.js';
|
|
7
|
+
/**
|
|
8
|
+
* Registry storage is the default: no CHANGELOG.md is committed; a release's
|
|
9
|
+
* section is parked (see {@link writePendingChangelog}) and packed into the
|
|
10
|
+
* published tarball. `repository` opts back into committed changelogs.
|
|
11
|
+
*/
|
|
12
|
+
export function changelogStorage(versioning) {
|
|
13
|
+
return versioning?.changelog?.storage ?? 'registry';
|
|
14
|
+
}
|
|
15
|
+
export async function applyReleasePlan(plan, opts) {
|
|
16
|
+
const storage = changelogStorage(opts.versioning);
|
|
17
|
+
const applied = await Promise.all(plan.releases.map(async (release) => {
|
|
18
|
+
const { manifest, writeProjectManifest } = await readProjectManifest(release.rootDir);
|
|
19
|
+
manifest.version = release.newVersion;
|
|
20
|
+
await writeProjectManifest(manifest);
|
|
21
|
+
return {
|
|
22
|
+
name: release.name,
|
|
23
|
+
currentVersion: release.currentVersion,
|
|
24
|
+
newVersion: release.newVersion,
|
|
25
|
+
};
|
|
26
|
+
}));
|
|
27
|
+
// In `repository` storage the section is committed to CHANGELOG.md now. In
|
|
28
|
+
// `registry` storage nothing is committed to the package; the section is
|
|
29
|
+
// parked until publish, when it is packed into the tarball.
|
|
30
|
+
await Promise.all(plan.releases.map(async (release) => {
|
|
31
|
+
const section = composeChangelogSection(release);
|
|
32
|
+
if (storage === 'repository') {
|
|
33
|
+
await prependChangelogSection(release.rootDir, release.name, section);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
await writePendingChangelog(opts.workspaceDir, release.name, release.newVersion, section);
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
const newEntries = {};
|
|
40
|
+
for (const release of plan.releases) {
|
|
41
|
+
if (release.intents.length === 0)
|
|
42
|
+
continue;
|
|
43
|
+
newEntries[`${release.name}@${release.newVersion}`] = {
|
|
44
|
+
dir: release.dir,
|
|
45
|
+
intents: release.intents.map((intent) => intent.id).sort(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const ledger = await appendToLedger(opts.workspaceDir, newEntries);
|
|
49
|
+
// An intent file is deletable once every project it names has a consumed
|
|
50
|
+
// ledger entry for it, with one exemption: while a project is still on a
|
|
51
|
+
// lane, entries against prerelease versions alone keep the file alive — its
|
|
52
|
+
// prose is still needed to compose the stable changelog section at
|
|
53
|
+
// graduation. Declined (`none`) entries demand no release and never block
|
|
54
|
+
// deletion. References here were already validated by the plan assembly,
|
|
55
|
+
// so an unresolvable one just keeps its file around.
|
|
56
|
+
//
|
|
57
|
+
// In `registry` storage the ledger alone does not authorize deletion: the
|
|
58
|
+
// repository is the only copy of the prose until the release is published,
|
|
59
|
+
// so an entry counts as consumed only once the registry confirms its version
|
|
60
|
+
// carries the composed section.
|
|
61
|
+
const refs = indexProjectRefs(opts.projects, opts.workspaceDir);
|
|
62
|
+
const consumedLedger = storage === 'repository'
|
|
63
|
+
? ledger
|
|
64
|
+
: await confirmPublished(ledger, opts);
|
|
65
|
+
const consumptionOf = buildConsumptionIndex(consumedLedger, refs.nameToDirs);
|
|
66
|
+
const laneDirs = new Set();
|
|
67
|
+
for (const ref of Object.keys(opts.versioning?.lanes ?? {})) {
|
|
68
|
+
for (const dir of refs.refToDirs(ref)) {
|
|
69
|
+
laneDirs.add(dir);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const deletable = opts.allIntents.filter((intent) => Object.entries(intent.releases).every(([ref, bumpType]) => {
|
|
73
|
+
if (bumpType === 'none')
|
|
74
|
+
return true;
|
|
75
|
+
const dirs = refs.refToDirs(ref);
|
|
76
|
+
if (dirs.length !== 1)
|
|
77
|
+
return false;
|
|
78
|
+
const consumption = consumptionOf(dirs[0]);
|
|
79
|
+
return consumption.allIds.has(intent.id) &&
|
|
80
|
+
!(laneDirs.has(dirs[0]) && consumption.prereleaseOnlyIds.has(intent.id));
|
|
81
|
+
}));
|
|
82
|
+
await Promise.all(deletable.map(async (intent) => fs.rm(intent.filePath)));
|
|
83
|
+
return applied;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Confirms the parked sections whose releases the registry reports published
|
|
87
|
+
* with that section, deletes those section files (their prose now lives in the
|
|
88
|
+
* published tarball), and returns the subset of `ledger` those confirmations
|
|
89
|
+
* cover — the consumed ledger that gates intent deletion. Driven by the parked
|
|
90
|
+
* files rather than the ledger, so a dependency-propagated release (which has a
|
|
91
|
+
* section but no consumed intents, hence no ledger entry) still gets its
|
|
92
|
+
* section collected. Every parked file belongs to an as-yet-unpublished
|
|
93
|
+
* release, so the confirmation cost is bounded by the release backlog, not by
|
|
94
|
+
* history.
|
|
95
|
+
*/
|
|
96
|
+
async function confirmPublished(ledger, opts) {
|
|
97
|
+
const { verifyPublished } = opts;
|
|
98
|
+
const confirmed = Object.create(null);
|
|
99
|
+
if (verifyPublished == null)
|
|
100
|
+
return confirmed;
|
|
101
|
+
const pending = await listPendingChangelogs(opts.workspaceDir);
|
|
102
|
+
await Promise.all(pending.map(async ({ name, version }) => {
|
|
103
|
+
const section = await readPendingChangelog(opts.workspaceDir, name, version);
|
|
104
|
+
if (section == null)
|
|
105
|
+
return;
|
|
106
|
+
if (!(await verifyPublished(name, version, section)))
|
|
107
|
+
return;
|
|
108
|
+
const key = `${name}@${version}`;
|
|
109
|
+
if (ledger[key] != null)
|
|
110
|
+
confirmed[key] = ledger[key];
|
|
111
|
+
await removePendingChangelog(opts.workspaceDir, name, version);
|
|
112
|
+
}));
|
|
113
|
+
return confirmed;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=applyReleasePlan.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ProjectManifest, VersioningSettings } from '@pnpm/types';
|
|
2
|
+
import type { ChangeIntent, ReleaseBumpType } from './intents.js';
|
|
3
|
+
import { type Ledger } from './ledger.js';
|
|
4
|
+
export interface WorkspaceProject {
|
|
5
|
+
rootDir: string;
|
|
6
|
+
manifest: ProjectManifest;
|
|
7
|
+
}
|
|
8
|
+
export type ReleaseCause = 'intent' | 'dependencies' | 'fixed' | 'epic';
|
|
9
|
+
export interface DependencyUpdate {
|
|
10
|
+
name: string;
|
|
11
|
+
newVersion: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PlannedRelease {
|
|
14
|
+
name: string;
|
|
15
|
+
/** Workspace-relative project directory — the engine's unit of identity. */
|
|
16
|
+
dir: string;
|
|
17
|
+
rootDir: string;
|
|
18
|
+
currentVersion: string;
|
|
19
|
+
newVersion: string;
|
|
20
|
+
bumpType: ReleaseBumpType;
|
|
21
|
+
/**
|
|
22
|
+
* The intent files this release consumes for this package: the pending ones,
|
|
23
|
+
* plus — when the release graduates the package off a lane — the
|
|
24
|
+
* ones the ledger recorded against the lane's prerelease versions.
|
|
25
|
+
*/
|
|
26
|
+
intents: ChangeIntent[];
|
|
27
|
+
dependencyUpdates: DependencyUpdate[];
|
|
28
|
+
causes: ReleaseCause[];
|
|
29
|
+
}
|
|
30
|
+
export interface ReleasePlan {
|
|
31
|
+
releases: PlannedRelease[];
|
|
32
|
+
}
|
|
33
|
+
export interface AssembleReleasePlanOptions {
|
|
34
|
+
workspaceDir: string;
|
|
35
|
+
projects: WorkspaceProject[];
|
|
36
|
+
intents: ChangeIntent[];
|
|
37
|
+
ledger: Ledger;
|
|
38
|
+
versioning?: VersioningSettings;
|
|
39
|
+
/**
|
|
40
|
+
* Workspace-relative directories of the projects selected with --filter.
|
|
41
|
+
* The plan is narrowed to the selected packages' portion of the pending
|
|
42
|
+
* work, expanded with their fixed-group companions and range-invalidated
|
|
43
|
+
* dependents.
|
|
44
|
+
*/
|
|
45
|
+
filter?: Set<string>;
|
|
46
|
+
/**
|
|
47
|
+
* When set, every planned release gets the version `0.0.0-<suffix>` instead
|
|
48
|
+
* of the computed one, matching snapshot releases.
|
|
49
|
+
*/
|
|
50
|
+
snapshotSuffix?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Enforce that every internal production dependency uses the `workspace:`
|
|
53
|
+
* protocol — a prerequisite for actually releasing. The release path
|
|
54
|
+
* (`pnpm version -r`) sets this; read-only callers (`pnpm change status`)
|
|
55
|
+
* leave it off so a diagnostic never fails on an unmigrated dependency.
|
|
56
|
+
*/
|
|
57
|
+
enforceWorkspaceProtocol?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Directories whose current manifest version the registry does not have.
|
|
60
|
+
* Their first release publishes that version verbatim, so the pending change
|
|
61
|
+
* intents bump it only from the next release. Resolved by the command layer's
|
|
62
|
+
* registry probe. Fixed-group sharing and epic band re-basing still override
|
|
63
|
+
* it, since a package cannot opt out of those workspace-wide version rules.
|
|
64
|
+
*/
|
|
65
|
+
unpublishedDirs?: Set<string>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether a package reference is a workspace-relative directory path rather
|
|
69
|
+
* than a package name — the additive extension to the changesets format,
|
|
70
|
+
* needed only when workspace projects share a published name.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isDirRef(ref: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Resolves package references — bare names, or `./`-prefixed
|
|
75
|
+
* workspace-relative directories — against the workspace. Names are aliases:
|
|
76
|
+
* one that matches several projects cannot identify any of them and callers
|
|
77
|
+
* must treat it as an error, never a silent pick.
|
|
78
|
+
*/
|
|
79
|
+
export interface ProjectRefIndex {
|
|
80
|
+
/** The directories a reference resolves to: `[]` unknown, 2+ ambiguous. */
|
|
81
|
+
refToDirs: (ref: string) => string[];
|
|
82
|
+
nameToDirs: (name: string) => string[];
|
|
83
|
+
}
|
|
84
|
+
export declare function indexProjectRefs(projects: ReadonlyArray<{
|
|
85
|
+
rootDir: string;
|
|
86
|
+
manifest: {
|
|
87
|
+
name?: string;
|
|
88
|
+
};
|
|
89
|
+
}>, workspaceDir: string): ProjectRefIndex;
|
|
90
|
+
/** The workspace-relative directory of a project, in canonical spelling. */
|
|
91
|
+
export declare function toProjectDir(workspaceDir: string, rootDir: string): string;
|
|
92
|
+
export declare function assembleReleasePlan(opts: AssembleReleasePlanOptions): ReleasePlan;
|
|
93
|
+
/**
|
|
94
|
+
* The range that pnpm materializes for a workspace: spec at pack time, given
|
|
95
|
+
* the dependency's version at the dependent's previous release. Dependent
|
|
96
|
+
* propagation republishes the dependent whenever the dependency's new version
|
|
97
|
+
* falls outside this range.
|
|
98
|
+
*/
|
|
99
|
+
export declare function materializeWorkspaceRange(spec: string, depCurrentVersion: string): string | null;
|