@williamthorsen/release-kit 18.2.0 → 18.3.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/.meta/changelog.json +90 -0
- package/.readyup/kits/default.js +2 -2
- package/.readyup/manifest.json +13 -13
- package/CHANGELOG.md +26 -0
- package/README.md +11 -12
- package/dist/esm/assertCleanWorkingTree.d.ts +2 -1
- package/dist/esm/assertCleanWorkingTree.js +4 -8
- package/dist/esm/assertReleasesOnNpm.js +2 -9
- package/dist/esm/assertTarballsComplete.js +2 -9
- package/dist/esm/buildChangelogEntries.d.ts +5 -3
- package/dist/esm/buildChangelogEntries.js +9 -4
- package/dist/esm/buildChangelogItem.d.ts +1 -1
- package/dist/esm/buildDependencyGraph.js +2 -16
- package/dist/esm/buildReleasedResult.d.ts +15 -3
- package/dist/esm/buildReleasedResult.js +23 -2
- package/dist/esm/changelogJsonFile.d.ts +3 -2
- package/dist/esm/changelogJsonFile.js +14 -17
- package/dist/esm/changelogJsonUtils.d.ts +22 -2
- package/dist/esm/changelogJsonUtils.js +19 -10
- package/dist/esm/commitCommand.d.ts +2 -1
- package/dist/esm/commitCommand.js +8 -11
- package/dist/esm/configSchema.d.ts +3 -1
- package/dist/esm/configSchema.js +1 -0
- package/dist/esm/createGithubRelease.d.ts +13 -10
- package/dist/esm/createGithubRelease.js +9 -13
- package/dist/esm/createGithubReleaseCommand.d.ts +1 -1
- package/dist/esm/createGithubReleaseCommand.js +25 -3
- package/dist/esm/createTags.d.ts +2 -1
- package/dist/esm/createTags.js +12 -12
- package/dist/esm/decideDirectReleases.d.ts +2 -1
- package/dist/esm/decideDirectReleases.js +5 -5
- package/dist/esm/decideRelease.d.ts +1 -1
- package/dist/esm/defaults.d.ts +4 -2
- package/dist/esm/defaults.js +1 -0
- package/dist/esm/deriveDirectoryConfig.js +5 -4
- package/dist/esm/deriveWorkspaceConfig.js +2 -9
- package/dist/esm/detectUndeclaredTagPrefixes.d.ts +2 -1
- package/dist/esm/detectUndeclaredTagPrefixes.js +4 -13
- package/dist/esm/determineBumpType.d.ts +2 -1
- package/dist/esm/determineBumpType.js +5 -0
- package/dist/esm/enumerateReleaseWindows.d.ts +3 -11
- package/dist/esm/enumerateReleaseWindows.js +17 -60
- package/dist/esm/findUnroutedEntryScopes.d.ts +1 -1
- package/dist/esm/git.d.ts +61 -0
- package/dist/esm/git.js +101 -0
- package/dist/esm/init/checks.d.ts +2 -1
- package/dist/esm/init/checks.js +5 -10
- package/dist/esm/init/eligibility.d.ts +12 -0
- package/dist/esm/init/eligibility.js +50 -0
- package/dist/esm/init/initCommand.d.ts +0 -2
- package/dist/esm/init/initCommand.js +4 -45
- package/dist/esm/init/templates.js +2 -2
- package/dist/esm/init/updateTemplatesCommand.js +4 -31
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +29 -7
- package/dist/esm/injectReleaseNotesIntoReadme.js +13 -21
- package/dist/esm/loadConfig.js +3 -16
- package/dist/esm/mergeConfig.d.ts +2 -1
- package/dist/esm/packFromCleanCheckout.js +3 -9
- package/dist/esm/planVersionBump.js +2 -16
- package/dist/esm/prepareCommand.js +2 -1
- package/dist/esm/prepareComposite.js +1 -1
- package/dist/esm/prepareReleaseTargets.d.ts +2 -1
- package/dist/esm/prepareReleaseTargets.js +3 -2
- package/dist/esm/prepareRunContext.d.ts +2 -0
- package/dist/esm/prepareTarget.d.ts +6 -4
- package/dist/esm/prepareTarget.js +9 -3
- package/dist/esm/previewTagPrefixes.d.ts +2 -1
- package/dist/esm/previewTagPrefixes.js +16 -21
- package/dist/esm/publishCommand.d.ts +1 -1
- package/dist/esm/publishCommand.js +27 -2
- package/dist/esm/pushRelease.d.ts +2 -1
- package/dist/esm/pushRelease.js +11 -11
- package/dist/esm/readJsonFile.d.ts +2 -0
- package/dist/esm/readJsonFile.js +17 -0
- package/dist/esm/reportPrepare.js +68 -114
- package/dist/esm/resolveReleaseTags.d.ts +2 -1
- package/dist/esm/resolveReleaseTags.js +4 -12
- package/dist/esm/types.d.ts +8 -2
- package/docs/configuration.md +12 -3
- package/docs/labels.md +2 -0
- package/docs/releasing.md +3 -2
- package/docs/work-types.md +2 -0
- package/package.json +3 -3
- package/presets/labels/common.yaml +14 -0
package/dist/esm/loadConfig.js
CHANGED
|
@@ -6,31 +6,18 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
6
6
|
}
|
|
7
7
|
return path;
|
|
8
8
|
};
|
|
9
|
-
import { existsSync
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { pathToFileURL } from 'node:url';
|
|
12
12
|
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
13
|
-
import {
|
|
13
|
+
import { readJsonFile } from "./readJsonFile.js";
|
|
14
14
|
export const ROOT_PACKAGE_JSON_PATH = 'package.json';
|
|
15
15
|
export function readRootPackageVersion() {
|
|
16
16
|
const absolutePath = path.resolve(process.cwd(), ROOT_PACKAGE_JSON_PATH);
|
|
17
17
|
if (!existsSync(absolutePath)) {
|
|
18
18
|
return { doesExist: false, version: undefined };
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
contents = readFileSync(absolutePath, 'utf8');
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
throw chainUserError(`Failed to read root ${ROOT_PACKAGE_JSON_PATH}`, error);
|
|
26
|
-
}
|
|
27
|
-
let parsedJson;
|
|
28
|
-
try {
|
|
29
|
-
parsedJson = JSON.parse(contents);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
throw chainUserError(`Failed to parse root ${ROOT_PACKAGE_JSON_PATH}`, error);
|
|
33
|
-
}
|
|
20
|
+
const parsedJson = readJsonFile(ROOT_PACKAGE_JSON_PATH);
|
|
34
21
|
if (!isRecord(parsedJson)) {
|
|
35
22
|
return { doesExist: true, version: undefined };
|
|
36
23
|
}
|
|
@@ -9,7 +9,8 @@ import type { MonorepoReleaseConfig, ReleaseSettings, WorkTypeConfig } from './t
|
|
|
9
9
|
* directories that `releases` declares resolve through `deriveDirectoryConfig` into `directories`, which must not
|
|
10
10
|
* share a version file with any other target.
|
|
11
11
|
* - `workTypes`: Shallow merge; consumer entries override or add to defaults by key.
|
|
12
|
-
* - `versionPatterns`: Consumer value replaces defaults entirely
|
|
12
|
+
* - `versionPatterns`: Consumer value replaces defaults entirely, except that an omitted `none` takes its default in
|
|
13
|
+
* `determineBumpType`.
|
|
13
14
|
* - Pass-through fields (see `applyOptionalPassthroughFields`): Consumer value wins.
|
|
14
15
|
* - `project`: Present iff the config declares a composite. Resolves `tagPrefix` to `DEFAULT_PROJECT_TAG_PREFIX` and
|
|
15
16
|
* `paths` to the union of its members' `paths` when omitted. Requires `rootPackage` to be passed and to contain a
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { mkdirSync, mkdtempSync, readdirSync,
|
|
2
|
+
import { mkdirSync, mkdtempSync, readdirSync, rmSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
6
6
|
import { resolveExecutable } from "./detectPackageManager.js";
|
|
7
|
-
import {
|
|
7
|
+
import { readJsonFile } from "./readJsonFile.js";
|
|
8
8
|
export function packFromCleanCheckout(workspacePaths, packageManager) {
|
|
9
9
|
const repoRoot = runStep('find the repository root', 'git', ['rev-parse', '--show-toplevel'], process.cwd()).trim();
|
|
10
10
|
const headSha = runStep('resolve HEAD', 'git', ['rev-parse', 'HEAD'], repoRoot).trim();
|
|
@@ -53,13 +53,7 @@ const PACKAGE_MANAGER_COMMANDS = {
|
|
|
53
53
|
};
|
|
54
54
|
function declaresPrepublishOnly(packageDir) {
|
|
55
55
|
const manifestPath = join(packageDir, 'package.json');
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
throw chainUserError(`Failed to parse ${manifestPath}`, error);
|
|
62
|
-
}
|
|
56
|
+
const manifest = readJsonFile(manifestPath);
|
|
63
57
|
const scripts = isRecord(manifest) ? manifest['scripts'] : undefined;
|
|
64
58
|
return isRecord(scripts) && typeof scripts['prepublishOnly'] === 'string';
|
|
65
59
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
1
|
import { UserError } from '@williamthorsen/nmr-core';
|
|
3
2
|
import { bumpVersion } from "./bumpVersion.js";
|
|
4
3
|
import { isForwardVersion } from "./compareVersions.js";
|
|
5
|
-
import {
|
|
4
|
+
import { readJsonFile } from "./readJsonFile.js";
|
|
6
5
|
function isPackageJson(value) {
|
|
7
6
|
return typeof value === 'object' && value !== null && 'version' in value && typeof value.version === 'string';
|
|
8
7
|
}
|
|
@@ -46,20 +45,7 @@ function renderVersionWrites(packageFiles, firstFile, firstPackageJson, newVersi
|
|
|
46
45
|
});
|
|
47
46
|
}
|
|
48
47
|
function readPackageJson(filePath) {
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
content = readFileSync(filePath, 'utf8');
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
throw chainUserError(`Failed to read ${filePath}`, error);
|
|
55
|
-
}
|
|
56
|
-
let parsedJson;
|
|
57
|
-
try {
|
|
58
|
-
parsedJson = JSON.parse(content);
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
throw chainUserError(`Failed to parse JSON in ${filePath}`, error);
|
|
62
|
-
}
|
|
48
|
+
const parsedJson = readJsonFile(filePath);
|
|
63
49
|
if (!isPackageJson(parsedJson)) {
|
|
64
50
|
throw new UserError(`No valid 'version' field found in ${filePath}`);
|
|
65
51
|
}
|
|
@@ -7,6 +7,7 @@ import { assertReleasesOnNpm } from "./assertReleasesOnNpm.js";
|
|
|
7
7
|
import { assertTarballsComplete } from "./assertTarballsComplete.js";
|
|
8
8
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
9
9
|
import { dim } from "./format.js";
|
|
10
|
+
import { realGit } from "./git.js";
|
|
10
11
|
import { RELEASE_GLYPHS } from "./glyphs.js";
|
|
11
12
|
import { ConfigProblemError, loadValidatedConfig, reportConfigWarnings } from "./loadValidatedConfig.js";
|
|
12
13
|
import { CANONICAL_SEMVER_PATTERN, parseSetVersions } from "./prepareArgs.js";
|
|
@@ -55,7 +56,7 @@ export async function prepareCommand({ bumpOverride, configPath, isDryRun, onlyN
|
|
|
55
56
|
...(setVersion !== undefined && { versionOverridesById: resolveSetVersions(setVersion, config.targets) }),
|
|
56
57
|
};
|
|
57
58
|
runAndReport(() => {
|
|
58
|
-
const plan = prepareReleaseTargets(config, targetOptions);
|
|
59
|
+
const plan = prepareReleaseTargets(config, targetOptions, realGit);
|
|
59
60
|
const releasedPackagePaths = findReleasedPackagePaths(plan, config.targets);
|
|
60
61
|
assertReleasesOnNpm(releasedPackagePaths);
|
|
61
62
|
assertTarballsComplete(releasedPackagePaths, userConfig?.devExportConditions ?? []);
|
|
@@ -14,7 +14,7 @@ export function describeCompositeSkip(compositeTarget, onlyNames) {
|
|
|
14
14
|
return `Release '${compositeTarget.id}' skipped: --only does not name it. Add ${compositeTarget.id} to --only to include it.`;
|
|
15
15
|
}
|
|
16
16
|
export function prepareComposite(compositeTarget, context, options, collectedReleases, warnings) {
|
|
17
|
-
const { history, untaggedBaseline } = readTargetHistory(compositeTarget, context.settings);
|
|
17
|
+
const { history, untaggedBaseline } = readTargetHistory(compositeTarget, context.settings, context.git);
|
|
18
18
|
assertTaggedBaseline([untaggedBaseline]);
|
|
19
19
|
const decision = decideTargetRelease(compositeTarget, history, {
|
|
20
20
|
shouldForce: options.shouldForce === true,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Git } from './git.js';
|
|
1
2
|
import { type TargetPrepareSettings } from './prepareTarget.js';
|
|
2
3
|
import type { ReleasePlan } from './releasePlan.js';
|
|
3
4
|
import type { ReleaseTarget, ReleaseType } from './types.js';
|
|
@@ -44,4 +45,4 @@ export interface TargetPrepareOptions {
|
|
|
44
45
|
* 5. Prepares each composite after every package target, unless `onlyTargetIds` narrowed the run without naming it.
|
|
45
46
|
* 6. Renders the format command over the modified files.
|
|
46
47
|
*/
|
|
47
|
-
export declare function prepareReleaseTargets(config: TargetPrepareConfig, options: TargetPrepareOptions): ReleasePlan;
|
|
48
|
+
export declare function prepareReleaseTargets(config: TargetPrepareConfig, options: TargetPrepareOptions, git: Git): ReleasePlan;
|
|
@@ -15,7 +15,7 @@ import { propagateBumps } from "./propagateBumps.js";
|
|
|
15
15
|
import { isSinglePackageTargets } from "./resolveReleaseTargets.js";
|
|
16
16
|
import { sortTopologically } from "./topologicalSort.js";
|
|
17
17
|
const PREVIEWS_REQUIRE_JSON_WARNING = '--with-release-notes requires changelogJson.enabled; skipping preview generation';
|
|
18
|
-
export function prepareReleaseTargets(config, options) {
|
|
18
|
+
export function prepareReleaseTargets(config, options, git) {
|
|
19
19
|
const { settings } = config;
|
|
20
20
|
const { onlyTargetIds, shouldIncludeReleaseNotes } = options;
|
|
21
21
|
const isSinglePackage = isSinglePackageTargets(config.targets);
|
|
@@ -33,13 +33,14 @@ export function prepareReleaseTargets(config, options) {
|
|
|
33
33
|
const workTypes = resolveWorkTypes(settings.workTypes);
|
|
34
34
|
const context = {
|
|
35
35
|
settings,
|
|
36
|
+
git,
|
|
36
37
|
overrideContext: createOverrideContext(plannedTargets.flatMap(listOverrideDirectory)),
|
|
37
38
|
sectionOrder: deriveSectionOrder(workTypes),
|
|
38
39
|
dependenciesHeader: deriveDependenciesHeader(workTypes),
|
|
39
40
|
shouldPlanPreviews: shouldIncludeReleaseNotes === true && settings.changelogJson.enabled,
|
|
40
41
|
today: new Date().toISOString().slice(0, 10),
|
|
41
42
|
};
|
|
42
|
-
const directReleases = decideDirectReleases(plannedTargets, settings, options, startIds);
|
|
43
|
+
const directReleases = decideDirectReleases(plannedTargets, settings, options, startIds, git);
|
|
43
44
|
if (!isSinglePackage) {
|
|
44
45
|
reportUnroutedEntryScopes(plannedTargets, directReleases, settings, memberTargets);
|
|
45
46
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReleaseHistory } from './buildChangelogEntries.js';
|
|
2
2
|
import type { OverrideContext } from './changelogOverrides.js';
|
|
3
|
+
import type { Git } from './git.js';
|
|
3
4
|
import { type TargetPrepareSettings } from './prepareTarget.js';
|
|
4
5
|
import type { PlannedWrite } from './releasePlan.js';
|
|
5
6
|
import type { DirectoryTarget, PackageTarget, ReleaseTarget } from './types.js';
|
|
@@ -28,6 +29,7 @@ export declare function readHistory(readings: ReadonlyMap<string, ReleaseHistory
|
|
|
28
29
|
/** What every target's planning reads from the run. */
|
|
29
30
|
export interface RunContext {
|
|
30
31
|
settings: TargetPrepareSettings;
|
|
32
|
+
git: Git;
|
|
31
33
|
overrideContext: OverrideContext;
|
|
32
34
|
sectionOrder: string[];
|
|
33
35
|
dependenciesHeader: string;
|
|
@@ -3,6 +3,7 @@ import { type ReleaseHistory } from './buildChangelogEntries.js';
|
|
|
3
3
|
import { type TargetRelease } from './buildReleasedResult.js';
|
|
4
4
|
import { type OverrideContext } from './changelogOverrides.js';
|
|
5
5
|
import { type GenerateChangelogOptions } from './generateChangelogs.js';
|
|
6
|
+
import type { Git } from './git.js';
|
|
6
7
|
import type { PlannedWrite } from './releasePlan.js';
|
|
7
8
|
import type { DirectoryTarget, ReleasedTargetResult, ReleaseSettings, ReleaseTarget, ReleaseType, TargetPrepareResult } from './types.js';
|
|
8
9
|
/** The shared settings that the prepare step reads, which do not include the release-notes settings. */
|
|
@@ -68,7 +69,7 @@ export interface PlannedTargetRelease {
|
|
|
68
69
|
warnings: string[];
|
|
69
70
|
}
|
|
70
71
|
/** Reads a target's release windows under its tag prefixes and commit window, and finds its untagged baseline. */
|
|
71
|
-
export declare function readTargetHistory(target: ReleaseTarget, settings: TargetPrepareSettings): TargetReading;
|
|
72
|
+
export declare function readTargetHistory(target: ReleaseTarget, settings: TargetPrepareSettings, git: Git): TargetReading;
|
|
72
73
|
/**
|
|
73
74
|
* Returns the bounds of a target's history read: its current and legacy tag prefixes, its commit window, and, for a
|
|
74
75
|
* workspace or a directory other than the repo root, the scope to which change-record entries route, which is its name.
|
|
@@ -87,9 +88,10 @@ export declare function decideTargetRelease(target: ReleaseTarget, history: Rele
|
|
|
87
88
|
* Plans a target's release: the version bump, the changelogs, and the release-notes previews, and builds its result.
|
|
88
89
|
*
|
|
89
90
|
* The changelog entries come from the target's release windows, with the synthetic "Forced version bump." entry for a
|
|
90
|
-
* window that doesn't yield any item, or, for a release that propagation alone set, from
|
|
91
|
-
* the
|
|
92
|
-
* `CHANGELOG.md`
|
|
91
|
+
* window that doesn't yield any item, or, for a release that propagation alone set, from the window's entries with the
|
|
92
|
+
* dependencies added to the new version's entry, or one synthetic entry listing them when the window doesn't yield any
|
|
93
|
+
* item. Overrides apply before `changelog.json` and `CHANGELOG.md` render from the merged set, and `CHANGELOG.md`
|
|
94
|
+
* keeps the existing sections whose versions the merged set lacks.
|
|
93
95
|
*/
|
|
94
96
|
export declare function planTargetRelease(args: PlanTargetReleaseArgs): PlannedTargetRelease;
|
|
95
97
|
/** Builds the result of a target that skips, from the window that its read produced. */
|
|
@@ -9,9 +9,9 @@ import { planChangelogFiles } from "./planChangelogFiles.js";
|
|
|
9
9
|
import { planReleaseNotesPreviews } from "./planReleaseNotesPreviews.js";
|
|
10
10
|
import { planVersionBump, planVersionSet } from "./planVersionBump.js";
|
|
11
11
|
import { PROJECT_TARGET_ID } from "./resolveReleaseTargets.js";
|
|
12
|
-
export function readTargetHistory(target, settings) {
|
|
12
|
+
export function readTargetHistory(target, settings, git) {
|
|
13
13
|
const options = describeHistoryWindow(target);
|
|
14
|
-
const history = readReleaseHistory(settings, options);
|
|
14
|
+
const history = readReleaseHistory(settings, options, git);
|
|
15
15
|
const untaggedBaseline = findUntaggedBaseline({
|
|
16
16
|
label: describeBaselineLabel(target),
|
|
17
17
|
packageFiles: target.versionFiles,
|
|
@@ -60,7 +60,13 @@ export function planTargetRelease(args) {
|
|
|
60
60
|
const tag = `${target.tagPrefix}${bumpPlan.newVersion}`;
|
|
61
61
|
const writes = [...bumpPlan.writes];
|
|
62
62
|
const entries = decision === undefined
|
|
63
|
-
? buildPropagationEntries(target, release,
|
|
63
|
+
? buildPropagationEntries(target, release, {
|
|
64
|
+
history,
|
|
65
|
+
tag,
|
|
66
|
+
newVersion: bumpPlan.newVersion,
|
|
67
|
+
today,
|
|
68
|
+
dependenciesHeader: args.dependenciesHeader,
|
|
69
|
+
})
|
|
64
70
|
: convertToReleaseEntries(history, tag, today);
|
|
65
71
|
const overrideScope = describeOverrideScope(target);
|
|
66
72
|
const appliedOverrides = applyWorkspaceOverrides(entries, overrideScope, overrideContext);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReleaseKitConfig } from './configSchema.js';
|
|
2
2
|
import { type UndeclaredTagPrefix } from './detectUndeclaredTagPrefixes.js';
|
|
3
|
+
import { type Git } from './git.js';
|
|
3
4
|
/** One workspace's or directory target's preview row in the tag-prefix preview. */
|
|
4
5
|
export interface TagPrefixPreviewRow {
|
|
5
6
|
/** Directory path relative to the repo root (e.g., `packages/core`). */
|
|
@@ -56,4 +57,4 @@ export interface TagPrefixPreview {
|
|
|
56
57
|
* tags, and collisions across the successfully derived prefixes. A declared composite's tag prefix counts as known, so
|
|
57
58
|
* that its tags are not reported as undeclared.
|
|
58
59
|
*/
|
|
59
|
-
export declare function previewTagPrefixes(config?: ReleaseKitConfig): TagPrefixPreview;
|
|
60
|
+
export declare function previewTagPrefixes(config?: ReleaseKitConfig, git?: Git): TagPrefixPreview;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
1
|
import { basename } from 'node:path';
|
|
3
|
-
import {
|
|
2
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
4
3
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
4
|
import { DEFAULT_PROJECT_TAG_PREFIX } from "./defaults.js";
|
|
6
5
|
import { deriveDirectoryConfig } from "./deriveDirectoryConfig.js";
|
|
7
6
|
import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
|
|
8
7
|
import { detectUndeclaredTagPrefixes } from "./detectUndeclaredTagPrefixes.js";
|
|
9
8
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
9
|
+
import { realGit } from "./git.js";
|
|
10
10
|
import { matchesTagPrefix } from "./matchesTagPrefix.js";
|
|
11
11
|
import { normalizeWorkspacePath } from "./resolveReleaseDeclaration.js";
|
|
12
|
-
export function previewTagPrefixes(config) {
|
|
12
|
+
export function previewTagPrefixes(config, git = realGit) {
|
|
13
13
|
const workspace = discoverWorkspaces();
|
|
14
14
|
if (workspace.kind === 'empty') {
|
|
15
15
|
throw new UserError(`No workspace package to preview. ${describeEmptyWorkspace(workspace)}`);
|
|
@@ -17,13 +17,13 @@ export function previewTagPrefixes(config) {
|
|
|
17
17
|
const workspacePaths = workspace.kind === 'packages' ? workspace.packageDirs : [];
|
|
18
18
|
const overridesByPath = buildOverrideMap(config, workspacePaths);
|
|
19
19
|
const workspaces = [
|
|
20
|
-
...Array.from(workspacePaths, (workspacePath) => buildPreviewRow(workspacePath, null, () => deriveWorkspaceConfig(workspacePath), overridesByPath)),
|
|
21
|
-
...buildDirectoryRows(config, workspacePaths, overridesByPath),
|
|
20
|
+
...Array.from(workspacePaths, (workspacePath) => buildPreviewRow(workspacePath, null, () => deriveWorkspaceConfig(workspacePath), overridesByPath, git)),
|
|
21
|
+
...buildDirectoryRows(config, workspacePaths, overridesByPath, git),
|
|
22
22
|
];
|
|
23
|
-
const retiredPackages = buildRetiredPreviewEntries(config?.retiredPackages ?? []);
|
|
23
|
+
const retiredPackages = buildRetiredPreviewEntries(config?.retiredPackages ?? [], git);
|
|
24
24
|
const collisions = detectCollisions(workspaces);
|
|
25
25
|
const knownPrefixes = collectKnownPrefixes(workspaces, retiredPackages, findCompositeTagPrefix(config));
|
|
26
|
-
const undeclaredCandidates = detectUndeclaredTagPrefixes(knownPrefixes);
|
|
26
|
+
const undeclaredCandidates = detectUndeclaredTagPrefixes(knownPrefixes, git);
|
|
27
27
|
return { workspaces, collisions, undeclaredCandidates, retiredPackages };
|
|
28
28
|
}
|
|
29
29
|
function buildOverrideMap(config, workspacePaths) {
|
|
@@ -46,7 +46,7 @@ function buildOverrideMap(config, workspacePaths) {
|
|
|
46
46
|
}
|
|
47
47
|
return map;
|
|
48
48
|
}
|
|
49
|
-
function buildDirectoryRows(config, workspacePaths, overridesByPath) {
|
|
49
|
+
function buildDirectoryRows(config, workspacePaths, overridesByPath, git) {
|
|
50
50
|
const discoveredPaths = new Set(workspacePaths);
|
|
51
51
|
return Object.entries(config?.releases ?? {}).flatMap(([name, entry]) => {
|
|
52
52
|
if (entry === false || !('path' in entry))
|
|
@@ -59,10 +59,10 @@ function buildDirectoryRows(config, workspacePaths, overridesByPath) {
|
|
|
59
59
|
path: directoryPath,
|
|
60
60
|
...(entry.tagPrefix !== undefined && { tagPrefix: entry.tagPrefix }),
|
|
61
61
|
};
|
|
62
|
-
return [buildPreviewRow(directoryPath, name, () => deriveDirectoryConfig(declaredDirectory), overridesByPath)];
|
|
62
|
+
return [buildPreviewRow(directoryPath, name, () => deriveDirectoryConfig(declaredDirectory), overridesByPath, git)];
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
|
-
function buildPreviewRow(workspacePath, knownName, deriveTarget, overridesByPath) {
|
|
65
|
+
function buildPreviewRow(workspacePath, knownName, deriveTarget, overridesByPath, git) {
|
|
66
66
|
const dir = basename(workspacePath);
|
|
67
67
|
let name = knownName;
|
|
68
68
|
let derivedPrefix = null;
|
|
@@ -75,12 +75,12 @@ function buildPreviewRow(workspacePath, knownName, deriveTarget, overridesByPath
|
|
|
75
75
|
catch (error) {
|
|
76
76
|
derivationError = describeError(error);
|
|
77
77
|
}
|
|
78
|
-
const derivedTagCount = derivedPrefix === null ? 0 : countTagsMatching(derivedPrefix);
|
|
78
|
+
const derivedTagCount = derivedPrefix === null ? 0 : countTagsMatching(derivedPrefix, git);
|
|
79
79
|
const declaredIdentities = overridesByPath.get(workspacePath) ?? [];
|
|
80
80
|
const legacyEntries = declaredIdentities.map((identity) => ({
|
|
81
81
|
name: identity.name,
|
|
82
82
|
prefix: identity.tagPrefix,
|
|
83
|
-
tagCount: countTagsMatching(identity.tagPrefix),
|
|
83
|
+
tagCount: countTagsMatching(identity.tagPrefix, git),
|
|
84
84
|
}));
|
|
85
85
|
return {
|
|
86
86
|
workspacePath,
|
|
@@ -92,14 +92,9 @@ function buildPreviewRow(workspacePath, knownName, deriveTarget, overridesByPath
|
|
|
92
92
|
legacyEntries,
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
|
-
function countTagsMatching(prefix) {
|
|
95
|
+
function countTagsMatching(prefix, git) {
|
|
96
96
|
try {
|
|
97
|
-
|
|
98
|
-
encoding: 'utf8',
|
|
99
|
-
maxBuffer: GIT_OUTPUT_LIMIT_BYTES,
|
|
100
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
101
|
-
});
|
|
102
|
-
return output.split('\n').filter((line) => matchesTagPrefix(line.trim(), [prefix])).length;
|
|
97
|
+
return git.listTags(`${prefix}*`).filter((tag) => matchesTagPrefix(tag, [prefix])).length;
|
|
103
98
|
}
|
|
104
99
|
catch {
|
|
105
100
|
return 0;
|
|
@@ -142,11 +137,11 @@ function collectKnownPrefixes(rows, retiredPackages, compositeTagPrefix) {
|
|
|
142
137
|
knownPrefixes.add(compositeTagPrefix);
|
|
143
138
|
return [...knownPrefixes];
|
|
144
139
|
}
|
|
145
|
-
function buildRetiredPreviewEntries(retiredPackages) {
|
|
140
|
+
function buildRetiredPreviewEntries(retiredPackages, git) {
|
|
146
141
|
return retiredPackages.map((retiredPackage) => ({
|
|
147
142
|
name: retiredPackage.name,
|
|
148
143
|
tagPrefix: retiredPackage.tagPrefix,
|
|
149
|
-
tagCount: countTagsMatching(retiredPackage.tagPrefix),
|
|
144
|
+
tagCount: countTagsMatching(retiredPackage.tagPrefix, git),
|
|
150
145
|
...(retiredPackage.successor !== undefined && { successor: retiredPackage.successor }),
|
|
151
146
|
}));
|
|
152
147
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { formatStatusLine } from '@williamthorsen/nmr-core';
|
|
3
4
|
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
4
5
|
import { assertTarballsComplete } from "./assertTarballsComplete.js";
|
|
6
|
+
import { describeChangelogFileProblem } from "./changelogJsonUtils.js";
|
|
5
7
|
import { deriveReleaseNotesConfig } from "./deriveReleaseNotesConfig.js";
|
|
6
8
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
7
9
|
import { formatDirectoryPublishSkip } from "./formatDirectoryPublishSkip.js";
|
|
8
10
|
import { formatPrivateSkip } from "./formatPrivateSkip.js";
|
|
9
11
|
import { formatProjectPublishSkip } from "./formatProjectPublishSkip.js";
|
|
10
|
-
import { injectReleaseNotesIntoReadme, resolveReadmePath } from "./injectReleaseNotesIntoReadme.js";
|
|
12
|
+
import { injectReleaseNotesIntoReadme, resolveReadmePath, } from "./injectReleaseNotesIntoReadme.js";
|
|
11
13
|
import { loadUsableConfig } from "./loadValidatedConfig.js";
|
|
12
14
|
import { publishPackage } from "./publish.js";
|
|
13
15
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
@@ -41,7 +43,15 @@ export async function publishCommand({ configPath, isDryRun, shouldGenerateProve
|
|
|
41
43
|
if (shouldInject) {
|
|
42
44
|
readmePath = resolveReadmePath(resolvedTag.workspacePath);
|
|
43
45
|
if (readmePath !== undefined) {
|
|
44
|
-
|
|
46
|
+
const changelogJsonPath = join(resolvedTag.workspacePath, changelogJsonOutputPath);
|
|
47
|
+
const injection = injectReleaseNotesIntoReadme(readmePath, changelogJsonPath, resolvedTag.tag, sectionOrder);
|
|
48
|
+
if (injection.status === 'injected') {
|
|
49
|
+
originalReadme = injection.originalReadme;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const message = formatInjectionSkip(resolvedTag, changelogJsonPath, injection.skip);
|
|
53
|
+
console.warn(` ${formatStatusLine(styles.stderr, 'warning', message)}`);
|
|
54
|
+
}
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
try {
|
|
@@ -65,6 +75,18 @@ export async function publishCommand({ configPath, isDryRun, shouldGenerateProve
|
|
|
65
75
|
throw error;
|
|
66
76
|
}
|
|
67
77
|
}
|
|
78
|
+
function describeInjectionSkip(changelogJsonPath, skip) {
|
|
79
|
+
switch (skip.reason) {
|
|
80
|
+
case 'missing-file':
|
|
81
|
+
return describeChangelogFileProblem('missing', changelogJsonPath);
|
|
82
|
+
case 'unreadable-file':
|
|
83
|
+
return describeChangelogFileProblem('unreadable', changelogJsonPath);
|
|
84
|
+
case 'no-entry':
|
|
85
|
+
return `no changelog entry for version ${skip.version}`;
|
|
86
|
+
case 'empty-body':
|
|
87
|
+
return `no user-facing release notes for version ${skip.version}`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
68
90
|
function filterPublishableTags(resolvedTags, isExplicit) {
|
|
69
91
|
const publishableTags = [];
|
|
70
92
|
const unpublishableTags = [];
|
|
@@ -78,6 +100,9 @@ function filterPublishableTags(resolvedTags, isExplicit) {
|
|
|
78
100
|
}
|
|
79
101
|
return publishableTags;
|
|
80
102
|
}
|
|
103
|
+
function formatInjectionSkip(resolvedTag, changelogJsonPath, skip) {
|
|
104
|
+
return `Skipping README injection for ${resolvedTag.tag} (${resolvedTag.workspacePath}): ${describeInjectionSkip(changelogJsonPath, skip)}`;
|
|
105
|
+
}
|
|
81
106
|
function formatPublishSkip(resolvedTag) {
|
|
82
107
|
switch (resolvedTag.kind) {
|
|
83
108
|
case 'directory':
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Git } from './git.js';
|
|
1
2
|
import type { ResolvedTag } from './resolveReleaseTags.js';
|
|
2
3
|
export interface PushReleaseOptions {
|
|
3
4
|
isDryRun?: boolean;
|
|
@@ -14,4 +15,4 @@ export interface PushStep {
|
|
|
14
15
|
*
|
|
15
16
|
* Returns the push steps, which it runs only outside dry-run mode.
|
|
16
17
|
*/
|
|
17
|
-
export declare function pushRelease(resolvedTags: ResolvedTag[], options?: PushReleaseOptions): PushStep[];
|
|
18
|
+
export declare function pushRelease(resolvedTags: ResolvedTag[], options?: PushReleaseOptions, git?: Git): PushStep[];
|
package/dist/esm/pushRelease.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { GIT_OUTPUT_LIMIT_BYTES } from '@williamthorsen/nmr-core';
|
|
1
|
+
import { buildBranchPushArgs, buildTagPushArgs, realGit } from "./git.js";
|
|
3
2
|
import { asUserError } from "./user-error-chaining.js";
|
|
4
|
-
export function pushRelease(resolvedTags, options = {}) {
|
|
3
|
+
export function pushRelease(resolvedTags, options = {}, git = realGit) {
|
|
5
4
|
const { isDryRun = false, tagsOnly = false } = options;
|
|
6
5
|
const steps = [];
|
|
7
6
|
if (!tagsOnly) {
|
|
8
7
|
let branch;
|
|
9
8
|
try {
|
|
10
|
-
branch =
|
|
11
|
-
encoding: 'utf8',
|
|
12
|
-
maxBuffer: GIT_OUTPUT_LIMIT_BYTES,
|
|
13
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
|
-
}).trim();
|
|
9
|
+
branch = git.readCurrentBranch();
|
|
15
10
|
}
|
|
16
11
|
catch (error) {
|
|
17
12
|
throw asUserError(error);
|
|
18
13
|
}
|
|
19
|
-
const command = ['git',
|
|
14
|
+
const command = ['git', ...buildBranchPushArgs(branch)];
|
|
20
15
|
steps.push({ type: 'branch', ref: branch, command });
|
|
21
16
|
}
|
|
22
17
|
for (const { tag } of resolvedTags) {
|
|
23
|
-
const command = ['git',
|
|
18
|
+
const command = ['git', ...buildTagPushArgs(tag)];
|
|
24
19
|
steps.push({ type: 'tag', ref: tag, command });
|
|
25
20
|
}
|
|
26
21
|
if (!isDryRun) {
|
|
27
22
|
for (const step of steps) {
|
|
28
23
|
try {
|
|
29
|
-
|
|
24
|
+
if (step.type === 'branch') {
|
|
25
|
+
git.pushBranch(step.ref);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
git.pushTag(step.ref);
|
|
29
|
+
}
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
32
32
|
throw asUserError(error);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { chainUserError } from "./user-error-chaining.js";
|
|
3
|
+
export function readJsonFile(filePath) {
|
|
4
|
+
let content;
|
|
5
|
+
try {
|
|
6
|
+
content = readFileSync(filePath, 'utf8');
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
throw chainUserError(`Failed to read ${filePath}`, error);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(content);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
throw chainUserError(`Failed to parse JSON in ${filePath}`, error);
|
|
16
|
+
}
|
|
17
|
+
}
|