MCPcopy Create free account
hub / github.com/dmno-dev/bumpy / applyReleasePlan

Function applyReleasePlan

packages/bumpy/src/core/apply-release-plan.ts:17–86  ·  view source on GitHub ↗
(
  releasePlan: ReleasePlan,
  packages: Map<string, WorkspacePackage>,
  rootDir: string,
  config: BumpyConfig,
)

Source from the content-addressed store, hash-verified

15
16/** Apply the release plan: bump versions, update changelogs, delete bump files */
17export async function applyReleasePlan(
18 releasePlan: ReleasePlan,
19 packages: Map<string, WorkspacePackage>,
20 rootDir: string,
21 config: BumpyConfig,
22): Promise<void> {
23 const releaseMap = new Map(releasePlan.releases.map((r) => [r.name, r]));
24 const formatter = config.changelog !== false ? await loadFormatter(config.changelog, rootDir) : null;
25
26 // 1. Update package.json versions and internal dependency ranges (preserving formatting)
27 for (const release of releasePlan.releases) {
28 const pkg = packages.get(release.name)!;
29 const pkgJsonPath = resolve(pkg.dir, 'package.json');
30 const pkgJson = await readJson<Record<string, unknown>>(pkgJsonPath);
31
32 // Bump the version (in-place string replacement)
33 await updateJsonFields(pkgJsonPath, { version: release.newVersion });
34
35 // Update internal dependency ranges (in-place string replacement)
36 for (const depField of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'] as const) {
37 const deps = pkgJson[depField] as Record<string, string> | undefined;
38 if (!deps) continue;
39 for (const [depName, range] of Object.entries(deps)) {
40 const depRelease = releaseMap.get(depName);
41 if (!depRelease) continue;
42 const newRange = updateRange(range, depRelease.newVersion);
43 await updateJsonNestedField(pkgJsonPath, depField, depName, newRange);
44 }
45 }
46 }
47
48 // 2. Update changelogs
49 if (formatter) {
50 for (const release of releasePlan.releases) {
51 const pkg = packages.get(release.name)!;
52 const changelogPath = resolve(pkg.dir, 'CHANGELOG.md');
53
54 const entry = await generateChangelogEntry(release, releasePlan.bumpFiles, formatter);
55 let existingContent = '';
56 if (await exists(changelogPath)) {
57 existingContent = await readText(changelogPath);
58 }
59 const newContent = prependToChangelog(existingContent, entry);
60 await writeText(changelogPath, newContent);
61 }
62 }
63
64 // 3. Delete all bump files (including empty ones that aren't in the release plan).
65 // Channel subdirs (`.bumpy/<channel>/`) hold bump files that shipped as prereleases;
66 // a stable release consumes those too (promotion), then removes the empty dirs.
67 const bumpyDir = getBumpyDir(rootDir);
68 const allBumpFiles = await listFiles(bumpyDir, '.md');
69 for (const file of allBumpFiles) {
70 if (file === 'README.md') continue;
71 await removeFile(resolve(bumpyDir, file));
72 }
73 const { rmdir } = await import('node:fs/promises');
74 for (const channel of Object.keys(config.channels || {})) {

Callers 2

versionCommandFunction · 0.90

Calls 12

loadFormatterFunction · 0.90
updateJsonFieldsFunction · 0.90
updateJsonNestedFieldFunction · 0.90
generateChangelogEntryFunction · 0.90
existsFunction · 0.90
readTextFunction · 0.90
prependToChangelogFunction · 0.90
writeTextFunction · 0.90
getBumpyDirFunction · 0.90
listFilesFunction · 0.90
removeFileFunction · 0.90
updateRangeFunction · 0.85

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…