@williamthorsen/release-kit 0.2.2 → 1.0.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/CHANGELOG.md +43 -1
- package/README.md +202 -219
- package/dist/esm/.cache +1 -1
- package/dist/esm/bin/release-kit.d.ts +2 -0
- package/dist/esm/bin/release-kit.js +73 -0
- package/dist/esm/component.d.ts +2 -0
- package/dist/esm/component.js +15 -0
- package/dist/esm/defaults.d.ts +3 -2
- package/dist/esm/defaults.js +17 -12
- package/dist/esm/determineBumpType.d.ts +2 -2
- package/dist/esm/determineBumpType.js +11 -13
- package/dist/esm/discoverWorkspaces.d.ts +1 -0
- package/dist/esm/discoverWorkspaces.js +45 -0
- package/dist/esm/getCommitsSinceTarget.js +2 -1
- package/dist/esm/index.d.ts +5 -2
- package/dist/esm/index.js +11 -2
- package/dist/esm/init/checks.d.ts +9 -0
- package/dist/esm/init/checks.js +56 -0
- package/dist/esm/init/detectRepoType.d.ts +2 -0
- package/dist/esm/init/detectRepoType.js +19 -0
- package/dist/esm/init/initCommand.d.ts +5 -0
- package/dist/esm/init/initCommand.js +65 -0
- package/dist/esm/init/parseJsonRecord.d.ts +1 -0
- package/dist/esm/init/parseJsonRecord.js +15 -0
- package/dist/esm/init/prompt.d.ts +5 -0
- package/dist/esm/init/prompt.js +30 -0
- package/dist/esm/init/scaffold.d.ts +9 -0
- package/dist/esm/init/scaffold.js +65 -0
- package/dist/esm/init/templates.d.ts +3 -0
- package/dist/esm/init/templates.js +105 -0
- package/dist/esm/loadConfig.d.ts +5 -0
- package/dist/esm/loadConfig.js +91 -0
- package/dist/esm/parseCommitMessage.d.ts +1 -1
- package/dist/esm/parseCommitMessage.js +4 -4
- package/dist/esm/prepareCommand.d.ts +1 -0
- package/dist/esm/prepareCommand.js +77 -0
- package/dist/esm/releasePrepare.d.ts +1 -1
- package/dist/esm/releasePrepare.js +7 -3
- package/dist/esm/releasePrepareMono.d.ts +1 -1
- package/dist/esm/releasePrepareMono.js +16 -10
- package/dist/esm/runReleasePrepare.d.ts +9 -0
- package/dist/esm/runReleasePrepare.js +112 -0
- package/dist/esm/types.d.ts +22 -4
- package/dist/esm/validateConfig.d.ts +5 -0
- package/dist/esm/validateConfig.js +143 -0
- package/dist/tsconfig.generate-typings.tsbuildinfo +1 -1
- package/package.json +12 -4
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
function component(workspacePath, tagPrefix) {
|
|
3
|
+
const dir = basename(workspacePath);
|
|
4
|
+
const prefix = tagPrefix ?? `${dir}-v`;
|
|
5
|
+
return {
|
|
6
|
+
dir,
|
|
7
|
+
tagPrefix: prefix,
|
|
8
|
+
packageFiles: [`${workspacePath}/package.json`],
|
|
9
|
+
changelogPaths: [workspacePath],
|
|
10
|
+
paths: [`${workspacePath}/**`]
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
component
|
|
15
|
+
};
|
package/dist/esm/defaults.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import type { WorkTypeConfig } from './types.ts';
|
|
2
|
-
export declare const DEFAULT_WORK_TYPES:
|
|
1
|
+
import type { VersionPatterns, WorkTypeConfig } from './types.ts';
|
|
2
|
+
export declare const DEFAULT_WORK_TYPES: Record<string, WorkTypeConfig>;
|
|
3
|
+
export declare const DEFAULT_VERSION_PATTERNS: VersionPatterns;
|
package/dist/esm/defaults.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
const DEFAULT_WORK_TYPES =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
const DEFAULT_WORK_TYPES = {
|
|
2
|
+
fix: { header: "Bug fixes", aliases: ["bugfix"] },
|
|
3
|
+
feat: { header: "Features", aliases: ["feature"] },
|
|
4
|
+
internal: { header: "Internal" },
|
|
5
|
+
refactor: { header: "Refactoring" },
|
|
6
|
+
tests: { header: "Tests", aliases: ["test"] },
|
|
7
|
+
tooling: { header: "Tooling" },
|
|
8
|
+
ci: { header: "CI" },
|
|
9
|
+
deps: { header: "Dependencies", aliases: ["dep"] },
|
|
10
|
+
docs: { header: "Documentation", aliases: ["doc"] },
|
|
11
|
+
fmt: { header: "Formatting" }
|
|
12
|
+
};
|
|
13
|
+
const DEFAULT_VERSION_PATTERNS = {
|
|
14
|
+
major: ["!"],
|
|
15
|
+
minor: ["feat", "feature"]
|
|
16
|
+
};
|
|
13
17
|
export {
|
|
18
|
+
DEFAULT_VERSION_PATTERNS,
|
|
14
19
|
DEFAULT_WORK_TYPES
|
|
15
20
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ParsedCommit, ReleaseType, WorkTypeConfig } from './types.ts';
|
|
2
|
-
export declare function determineBumpType(commits: readonly ParsedCommit[], workTypes:
|
|
1
|
+
import type { ParsedCommit, ReleaseType, VersionPatterns, WorkTypeConfig } from './types.ts';
|
|
2
|
+
export declare function determineBumpType(commits: readonly ParsedCommit[], workTypes: Record<string, WorkTypeConfig>, versionPatterns: VersionPatterns): ReleaseType | undefined;
|
|
@@ -3,24 +3,25 @@ const RELEASE_PRIORITY = {
|
|
|
3
3
|
minor: 2,
|
|
4
4
|
patch: 1
|
|
5
5
|
};
|
|
6
|
-
function determineBumpType(commits, workTypes) {
|
|
7
|
-
const
|
|
8
|
-
for (const config of workTypes) {
|
|
9
|
-
typeToBump[config.type] = config.bump;
|
|
10
|
-
}
|
|
6
|
+
function determineBumpType(commits, workTypes, versionPatterns) {
|
|
7
|
+
const knownTypes = new Set(Object.keys(workTypes));
|
|
11
8
|
let highestPriority = 0;
|
|
12
9
|
let result;
|
|
13
10
|
for (const commit of commits) {
|
|
14
|
-
if (commit.breaking) {
|
|
11
|
+
if (commit.breaking && versionPatterns.major.includes("!")) {
|
|
15
12
|
return "major";
|
|
16
13
|
}
|
|
17
14
|
const commitType = commit.type;
|
|
18
|
-
if (!
|
|
15
|
+
if (!knownTypes.has(commitType)) {
|
|
19
16
|
continue;
|
|
20
17
|
}
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
18
|
+
let bump;
|
|
19
|
+
if (versionPatterns.major.includes(commitType)) {
|
|
20
|
+
bump = "major";
|
|
21
|
+
} else if (versionPatterns.minor.includes(commitType)) {
|
|
22
|
+
bump = "minor";
|
|
23
|
+
} else {
|
|
24
|
+
bump = "patch";
|
|
24
25
|
}
|
|
25
26
|
const priority = RELEASE_PRIORITY[bump];
|
|
26
27
|
if (priority > highestPriority) {
|
|
@@ -30,9 +31,6 @@ function determineBumpType(commits, workTypes) {
|
|
|
30
31
|
}
|
|
31
32
|
return result;
|
|
32
33
|
}
|
|
33
|
-
function isKeyOf(key, obj) {
|
|
34
|
-
return Object.hasOwn(obj, key);
|
|
35
|
-
}
|
|
36
34
|
export {
|
|
37
35
|
determineBumpType
|
|
38
36
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function discoverWorkspaces(): Promise<string[] | undefined>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
import { load } from "js-yaml";
|
|
5
|
+
async function discoverWorkspaces() {
|
|
6
|
+
const workspaceFile = "pnpm-workspace.yaml";
|
|
7
|
+
if (!existsSync(workspaceFile)) {
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
let content;
|
|
11
|
+
try {
|
|
12
|
+
content = readFileSync(workspaceFile, "utf8");
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.warn(`Warning: Failed to read ${workspaceFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
17
|
+
const parsed = load(content);
|
|
18
|
+
if (!isRecord(parsed)) {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
const packagesField = parsed.packages;
|
|
22
|
+
if (!Array.isArray(packagesField)) {
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
25
|
+
const patterns = packagesField.filter((p) => typeof p === "string");
|
|
26
|
+
if (patterns.length === 0) {
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
const directories = [];
|
|
30
|
+
for (const pattern of patterns) {
|
|
31
|
+
const matches = await glob(pattern, { posix: true });
|
|
32
|
+
for (const match of matches) {
|
|
33
|
+
if (existsSync(join(match, "package.json"))) {
|
|
34
|
+
directories.push(match);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return directories.length > 0 ? [...directories].sort() : void 0;
|
|
39
|
+
}
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
discoverWorkspaces
|
|
45
|
+
};
|
|
@@ -20,6 +20,7 @@ function findLatestTag(tagPrefix) {
|
|
|
20
20
|
throw new Error(`Failed to run 'git describe': ${errorMessage(error)}`);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
const RELEASE_COMMIT_PREFIX = "release:";
|
|
23
24
|
function parseLogOutput(logOutput) {
|
|
24
25
|
const commits = [];
|
|
25
26
|
for (const line of logOutput.split("\n")) {
|
|
@@ -28,7 +29,7 @@ function parseLogOutput(logOutput) {
|
|
|
28
29
|
continue;
|
|
29
30
|
}
|
|
30
31
|
const [message, hash] = trimmedLine.split(FIELD_SEPARATOR);
|
|
31
|
-
if (message !== void 0 && hash !== void 0) {
|
|
32
|
+
if (message !== void 0 && hash !== void 0 && !message.startsWith(RELEASE_COMMIT_PREFIX)) {
|
|
32
33
|
commits.push({ message, hash });
|
|
33
34
|
}
|
|
34
35
|
}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export type { GenerateChangelogOptions } from './generateChangelogs.ts';
|
|
2
2
|
export type { ReleasePrepareOptions } from './releasePrepare.ts';
|
|
3
|
-
export type { Commit, ComponentConfig, MonorepoReleaseConfig, ParsedCommit, ReleaseConfig, ReleaseType, WorkTypeConfig, } from './types.ts';
|
|
4
|
-
export { DEFAULT_WORK_TYPES } from './defaults.ts';
|
|
3
|
+
export type { Commit, ComponentConfig, ComponentOverride, MonorepoReleaseConfig, ParsedCommit, ReleaseConfig, ReleaseKitConfig, ReleaseType, VersionPatterns, WorkTypeConfig, } from './types.ts';
|
|
4
|
+
export { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from './defaults.ts';
|
|
5
5
|
export { bumpAllVersions } from './bumpAllVersions.ts';
|
|
6
6
|
export { bumpVersion } from './bumpVersion.ts';
|
|
7
|
+
export { component } from './component.ts';
|
|
7
8
|
export { determineBumpType } from './determineBumpType.ts';
|
|
9
|
+
export { discoverWorkspaces } from './discoverWorkspaces.ts';
|
|
8
10
|
export { generateChangelog, generateChangelogs } from './generateChangelogs.ts';
|
|
9
11
|
export { getCommitsSinceTarget } from './getCommitsSinceTarget.ts';
|
|
10
12
|
export { parseCommitMessage } from './parseCommitMessage.ts';
|
|
11
13
|
export { releasePrepare } from './releasePrepare.ts';
|
|
12
14
|
export { releasePrepareMono } from './releasePrepareMono.ts';
|
|
15
|
+
export { RELEASE_TAGS_FILE, runReleasePrepare, writeReleaseTags } from './runReleasePrepare.ts';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
|
-
import { DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
1
|
+
import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
2
2
|
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
3
3
|
import { bumpVersion } from "./bumpVersion.js";
|
|
4
|
+
import { component } from "./component.js";
|
|
4
5
|
import { determineBumpType } from "./determineBumpType.js";
|
|
6
|
+
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
5
7
|
import { generateChangelog, generateChangelogs } from "./generateChangelogs.js";
|
|
6
8
|
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
7
9
|
import { parseCommitMessage } from "./parseCommitMessage.js";
|
|
8
10
|
import { releasePrepare } from "./releasePrepare.js";
|
|
9
11
|
import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
12
|
+
import { RELEASE_TAGS_FILE, runReleasePrepare, writeReleaseTags } from "./runReleasePrepare.js";
|
|
10
13
|
export {
|
|
14
|
+
DEFAULT_VERSION_PATTERNS,
|
|
11
15
|
DEFAULT_WORK_TYPES,
|
|
16
|
+
RELEASE_TAGS_FILE,
|
|
12
17
|
bumpAllVersions,
|
|
13
18
|
bumpVersion,
|
|
19
|
+
component,
|
|
14
20
|
determineBumpType,
|
|
21
|
+
discoverWorkspaces,
|
|
15
22
|
generateChangelog,
|
|
16
23
|
generateChangelogs,
|
|
17
24
|
getCommitsSinceTarget,
|
|
18
25
|
parseCommitMessage,
|
|
19
26
|
releasePrepare,
|
|
20
|
-
releasePrepareMono
|
|
27
|
+
releasePrepareMono,
|
|
28
|
+
runReleasePrepare,
|
|
29
|
+
writeReleaseTags
|
|
21
30
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface CheckResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
message?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function isGitRepo(): CheckResult;
|
|
6
|
+
export declare function hasPackageJson(): CheckResult;
|
|
7
|
+
export declare function usesPnpm(): CheckResult;
|
|
8
|
+
export declare function hasCliffToml(): CheckResult;
|
|
9
|
+
export declare function notAlreadyInitialized(): CheckResult;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { parseJsonRecord } from "./parseJsonRecord.js";
|
|
4
|
+
function isGitRepo() {
|
|
5
|
+
try {
|
|
6
|
+
execSync("git rev-parse --is-inside-work-tree", { stdio: "pipe" });
|
|
7
|
+
return { ok: true };
|
|
8
|
+
} catch {
|
|
9
|
+
return { ok: false, message: "Not inside a git repository. Run `git init` first." };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function hasPackageJson() {
|
|
13
|
+
if (existsSync("package.json")) {
|
|
14
|
+
return { ok: true };
|
|
15
|
+
}
|
|
16
|
+
return { ok: false, message: "No package.json found. Run `npm init` or `pnpm init` first." };
|
|
17
|
+
}
|
|
18
|
+
function usesPnpm() {
|
|
19
|
+
if (existsSync("pnpm-lock.yaml")) {
|
|
20
|
+
return { ok: true };
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const raw = readFileSync("package.json", "utf8");
|
|
24
|
+
const pkg = parseJsonRecord(raw);
|
|
25
|
+
if (pkg !== void 0 && typeof pkg.packageManager === "string" && pkg.packageManager.startsWith("pnpm")) {
|
|
26
|
+
return { ok: true };
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
message: "This project does not appear to use pnpm. A pnpm-lock.yaml or packageManager field is required."
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function hasCliffToml() {
|
|
36
|
+
if (existsSync("cliff.toml")) {
|
|
37
|
+
return { ok: true };
|
|
38
|
+
}
|
|
39
|
+
return { ok: false, message: "No cliff.toml found. This file is required for changelog generation." };
|
|
40
|
+
}
|
|
41
|
+
function notAlreadyInitialized() {
|
|
42
|
+
if (!existsSync(".config/release-kit.config.ts") && !existsSync(".github/scripts/release.config.ts")) {
|
|
43
|
+
return { ok: true };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
message: "release-kit appears to be already initialized."
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
hasCliffToml,
|
|
52
|
+
hasPackageJson,
|
|
53
|
+
isGitRepo,
|
|
54
|
+
notAlreadyInitialized,
|
|
55
|
+
usesPnpm
|
|
56
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { parseJsonRecord } from "./parseJsonRecord.js";
|
|
3
|
+
function detectRepoType() {
|
|
4
|
+
if (existsSync("pnpm-workspace.yaml")) {
|
|
5
|
+
return "monorepo";
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
const raw = readFileSync("package.json", "utf8");
|
|
9
|
+
const pkg = parseJsonRecord(raw);
|
|
10
|
+
if (pkg !== void 0 && Array.isArray(pkg.workspaces)) {
|
|
11
|
+
return "monorepo";
|
|
12
|
+
}
|
|
13
|
+
} catch {
|
|
14
|
+
}
|
|
15
|
+
return "single-package";
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
detectRepoType
|
|
19
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { hasCliffToml, hasPackageJson, isGitRepo, notAlreadyInitialized, usesPnpm } from "./checks.js";
|
|
2
|
+
import { detectRepoType } from "./detectRepoType.js";
|
|
3
|
+
import { confirm, printError, printStep, printSuccess } from "./prompt.js";
|
|
4
|
+
import { copyCliffTemplate, scaffoldFiles } from "./scaffold.js";
|
|
5
|
+
function runRequiredCheck(label, result) {
|
|
6
|
+
if (result.ok) {
|
|
7
|
+
printSuccess(label);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
printError(result.message ?? `${label} failed`);
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
async function checkEligibility(dryRun) {
|
|
14
|
+
printStep("Checking eligibility");
|
|
15
|
+
if (!runRequiredCheck("Git repository detected", isGitRepo())) return { status: "fail", overwrite: false };
|
|
16
|
+
if (!runRequiredCheck("package.json found", hasPackageJson())) return { status: "fail", overwrite: false };
|
|
17
|
+
if (!runRequiredCheck("pnpm detected", usesPnpm())) return { status: "fail", overwrite: false };
|
|
18
|
+
const cliffCheck = hasCliffToml();
|
|
19
|
+
if (cliffCheck.ok) {
|
|
20
|
+
printSuccess("cliff.toml found");
|
|
21
|
+
} else {
|
|
22
|
+
console.info("");
|
|
23
|
+
const shouldCreate = await confirm("No cliff.toml found. Create one from the bundled template?");
|
|
24
|
+
if (shouldCreate) {
|
|
25
|
+
copyCliffTemplate(dryRun);
|
|
26
|
+
} else {
|
|
27
|
+
printError("cliff.toml is required for changelog generation. Aborting.");
|
|
28
|
+
return { status: "fail", overwrite: false };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const initCheck = notAlreadyInitialized();
|
|
32
|
+
if (!initCheck.ok) {
|
|
33
|
+
console.info("");
|
|
34
|
+
const shouldOverwrite = await confirm("release-kit appears to be already initialized. Overwrite existing files?");
|
|
35
|
+
if (!shouldOverwrite) {
|
|
36
|
+
console.info("Aborting.");
|
|
37
|
+
return { status: "abort", overwrite: false };
|
|
38
|
+
}
|
|
39
|
+
return { status: "pass", overwrite: true };
|
|
40
|
+
}
|
|
41
|
+
return { status: "pass", overwrite: false };
|
|
42
|
+
}
|
|
43
|
+
async function initCommand({ dryRun }) {
|
|
44
|
+
if (dryRun) {
|
|
45
|
+
console.info("[dry-run mode]");
|
|
46
|
+
}
|
|
47
|
+
const eligibility = await checkEligibility(dryRun);
|
|
48
|
+
if (eligibility.status === "fail") return 1;
|
|
49
|
+
if (eligibility.status === "abort") return 0;
|
|
50
|
+
printStep("Detecting repo type");
|
|
51
|
+
const repoType = detectRepoType();
|
|
52
|
+
printSuccess(`Detected: ${repoType}`);
|
|
53
|
+
printStep("Scaffolding files");
|
|
54
|
+
scaffoldFiles({ repoType, dryRun, overwrite: eligibility.overwrite });
|
|
55
|
+
printStep("Next steps");
|
|
56
|
+
console.info(`
|
|
57
|
+
1. (Optional) Customize .config/release-kit.config.ts to exclude components, override version patterns, add custom work types, etc.
|
|
58
|
+
2. Test by running: npx @williamthorsen/release-kit prepare --dry-run
|
|
59
|
+
3. Commit the generated workflow file (and config file if created).
|
|
60
|
+
`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
initCommand
|
|
65
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseJsonRecord(raw: string): Record<string, unknown> | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function isRecord(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
function parseJsonRecord(raw) {
|
|
5
|
+
let parsed;
|
|
6
|
+
try {
|
|
7
|
+
parsed = JSON.parse(raw);
|
|
8
|
+
} catch {
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
return isRecord(parsed) ? parsed : void 0;
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
parseJsonRecord
|
|
15
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function confirm(question: string): Promise<boolean>;
|
|
2
|
+
export declare function printStep(message: string): void;
|
|
3
|
+
export declare function printSuccess(message: string): void;
|
|
4
|
+
export declare function printSkip(message: string): void;
|
|
5
|
+
export declare function printError(message: string): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
async function confirm(question) {
|
|
3
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
4
|
+
try {
|
|
5
|
+
const answer = await rl.question(`${question} (y/n) `);
|
|
6
|
+
return answer.trim().toLowerCase().startsWith("y");
|
|
7
|
+
} finally {
|
|
8
|
+
rl.close();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function printStep(message) {
|
|
12
|
+
console.info(`
|
|
13
|
+
> ${message}`);
|
|
14
|
+
}
|
|
15
|
+
function printSuccess(message) {
|
|
16
|
+
console.info(` [ok] ${message}`);
|
|
17
|
+
}
|
|
18
|
+
function printSkip(message) {
|
|
19
|
+
console.info(` [skip] ${message}`);
|
|
20
|
+
}
|
|
21
|
+
function printError(message) {
|
|
22
|
+
console.error(` [error] ${message}`);
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
confirm,
|
|
26
|
+
printError,
|
|
27
|
+
printSkip,
|
|
28
|
+
printStep,
|
|
29
|
+
printSuccess
|
|
30
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RepoType } from './detectRepoType.ts';
|
|
2
|
+
interface ScaffoldOptions {
|
|
3
|
+
repoType: RepoType;
|
|
4
|
+
dryRun: boolean;
|
|
5
|
+
overwrite: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function copyCliffTemplate(dryRun: boolean): void;
|
|
8
|
+
export declare function scaffoldFiles({ repoType, dryRun, overwrite }: ScaffoldOptions): void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { printError, printSkip, printSuccess } from "./prompt.js";
|
|
5
|
+
import { releaseConfigScript, releaseWorkflow } from "./templates.js";
|
|
6
|
+
function tryWriteFile(filePath, content) {
|
|
7
|
+
try {
|
|
8
|
+
writeFileSync(filePath, content, "utf8");
|
|
9
|
+
return true;
|
|
10
|
+
} catch (error) {
|
|
11
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12
|
+
printError(`Failed to write ${filePath}: ${message}`);
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function writeIfAbsent(filePath, content, dryRun, overwrite) {
|
|
17
|
+
if (existsSync(filePath) && !overwrite) {
|
|
18
|
+
printSkip(`${filePath} (already exists)`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (dryRun) {
|
|
22
|
+
printSuccess(`[dry-run] Would create ${filePath}`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
27
|
+
} catch (error) {
|
|
28
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
+
printError(`Failed to create directory for ${filePath}: ${message}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (tryWriteFile(filePath, content)) {
|
|
33
|
+
printSuccess(`Created ${filePath}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function copyCliffTemplate(dryRun) {
|
|
37
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
38
|
+
const templatePath = resolve(thisDir, "..", "..", "..", "cliff.toml.template");
|
|
39
|
+
if (!existsSync(templatePath)) {
|
|
40
|
+
printError(`Could not find cliff.toml.template at ${templatePath}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
let content;
|
|
44
|
+
try {
|
|
45
|
+
content = readFileSync(templatePath, "utf8");
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
printError(`Failed to read cliff.toml.template: ${message}`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
writeIfAbsent("cliff.toml", content, dryRun, false);
|
|
52
|
+
}
|
|
53
|
+
function scaffoldFiles({ repoType, dryRun, overwrite }) {
|
|
54
|
+
const files = [
|
|
55
|
+
{ path: ".config/release-kit.config.ts", content: releaseConfigScript(repoType) },
|
|
56
|
+
{ path: ".github/workflows/release.yaml", content: releaseWorkflow(repoType) }
|
|
57
|
+
];
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
writeIfAbsent(file.path, file.content, dryRun, overwrite);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
copyCliffTemplate,
|
|
64
|
+
scaffoldFiles
|
|
65
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
function releaseConfigScript(repoType) {
|
|
2
|
+
if (repoType === "monorepo") {
|
|
3
|
+
return `import type { ReleaseKitConfig } from '@williamthorsen/release-kit';
|
|
4
|
+
|
|
5
|
+
const config: ReleaseKitConfig = {
|
|
6
|
+
// Uncomment to exclude components from release processing:
|
|
7
|
+
// components: [
|
|
8
|
+
// { dir: 'my-package', shouldExclude: true },
|
|
9
|
+
// ],
|
|
10
|
+
// Uncomment to override the default version patterns:
|
|
11
|
+
// versionPatterns: { major: ['!'], minor: ['feat', 'feature'] },
|
|
12
|
+
// Uncomment to add custom work types (merged with defaults):
|
|
13
|
+
// workTypes: { perf: { header: 'Performance' } },
|
|
14
|
+
// TODO: Uncomment and adjust if you have a format command
|
|
15
|
+
// formatCommand: 'pnpm run fmt',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default config;
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
return `import type { ReleaseKitConfig } from '@williamthorsen/release-kit';
|
|
22
|
+
|
|
23
|
+
const config: ReleaseKitConfig = {
|
|
24
|
+
// Uncomment to override the default version patterns:
|
|
25
|
+
// versionPatterns: { major: ['!'], minor: ['feat', 'feature'] },
|
|
26
|
+
// Uncomment to add custom work types (merged with defaults):
|
|
27
|
+
// workTypes: { perf: { header: 'Performance' } },
|
|
28
|
+
// TODO: Uncomment and adjust if you have a format command
|
|
29
|
+
// formatCommand: 'pnpm run fmt',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default config;
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
function releaseWorkflow(repoType) {
|
|
36
|
+
if (repoType === "monorepo") {
|
|
37
|
+
return `# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
|
38
|
+
name: Release
|
|
39
|
+
|
|
40
|
+
on:
|
|
41
|
+
workflow_dispatch:
|
|
42
|
+
inputs:
|
|
43
|
+
only:
|
|
44
|
+
description: 'Components to release (comma-separated, leave empty for all)'
|
|
45
|
+
required: false
|
|
46
|
+
type: string
|
|
47
|
+
bump:
|
|
48
|
+
description: 'Override version bump type (leave empty to auto-detect from commits)'
|
|
49
|
+
required: false
|
|
50
|
+
type: choice
|
|
51
|
+
options:
|
|
52
|
+
- ''
|
|
53
|
+
- patch
|
|
54
|
+
- minor
|
|
55
|
+
- major
|
|
56
|
+
|
|
57
|
+
permissions:
|
|
58
|
+
contents: write
|
|
59
|
+
packages: read
|
|
60
|
+
|
|
61
|
+
jobs:
|
|
62
|
+
release:
|
|
63
|
+
uses: williamthorsen/.github/.github/workflows/release-pnpm.yaml@v2
|
|
64
|
+
with:
|
|
65
|
+
# TODO: Set the Node.js and pnpm versions for your project
|
|
66
|
+
node-version: '24'
|
|
67
|
+
pnpm-version: '10.32.1'
|
|
68
|
+
only: \${{ inputs.only }}
|
|
69
|
+
bump: \${{ inputs.bump }}
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
return `# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
|
73
|
+
name: Release
|
|
74
|
+
|
|
75
|
+
on:
|
|
76
|
+
workflow_dispatch:
|
|
77
|
+
inputs:
|
|
78
|
+
bump:
|
|
79
|
+
description: 'Override version bump type (leave empty to auto-detect from commits)'
|
|
80
|
+
required: false
|
|
81
|
+
type: choice
|
|
82
|
+
options:
|
|
83
|
+
- ''
|
|
84
|
+
- patch
|
|
85
|
+
- minor
|
|
86
|
+
- major
|
|
87
|
+
|
|
88
|
+
permissions:
|
|
89
|
+
contents: write
|
|
90
|
+
packages: read
|
|
91
|
+
|
|
92
|
+
jobs:
|
|
93
|
+
release:
|
|
94
|
+
uses: williamthorsen/.github/.github/workflows/release-pnpm.yaml@v2
|
|
95
|
+
with:
|
|
96
|
+
# TODO: Set the Node.js and pnpm versions for your project
|
|
97
|
+
node-version: '24'
|
|
98
|
+
pnpm-version: '10.32.1'
|
|
99
|
+
bump: \${{ inputs.bump }}
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
releaseConfigScript,
|
|
104
|
+
releaseWorkflow
|
|
105
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { MonorepoReleaseConfig, ReleaseConfig, ReleaseKitConfig } from './types.ts';
|
|
2
|
+
export declare const CONFIG_FILE_PATH = ".config/release-kit.config.ts";
|
|
3
|
+
export declare function loadConfig(): Promise<unknown>;
|
|
4
|
+
export declare function mergeMonorepoConfig(discoveredPaths: string[], userConfig: ReleaseKitConfig | undefined): MonorepoReleaseConfig;
|
|
5
|
+
export declare function mergeSinglePackageConfig(userConfig: ReleaseKitConfig | undefined): ReleaseConfig;
|