tegami 0.1.1 → 0.1.4
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/dist/checks-Cwz-Ezkq.js +277 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +210 -211
- package/dist/error-DNy8R5ue.js +45 -0
- package/dist/generators/simple.d.ts +1 -1
- package/dist/generators/simple.js +1 -1
- package/dist/{graph-CUgwuRW5.js → graph-DLKPUXSD.js} +17 -12
- package/dist/index.d.ts +2 -2
- package/dist/index.js +233 -294
- package/dist/plugins/git.d.ts +1 -1
- package/dist/plugins/git.js +4 -5
- package/dist/plugins/github.d.ts +7 -1
- package/dist/plugins/github.js +24 -24
- package/dist/providers/cargo.d.ts +1 -1
- package/dist/providers/cargo.js +6 -6
- package/dist/providers/npm.d.ts +1 -1
- package/dist/providers/npm.js +34 -20
- package/dist/{semver-mWK2Khi2.js → semver-C4vJ4SK8.js} +16 -5
- package/dist/{types-Diwnh8Tn.d.ts → types-BXk2fMqa.d.ts} +52 -23
- package/package.json +1 -1
- package/dist/checks-Bz3Rf2OX.js +0 -98
- package/dist/constants-B9qjNfvr.js +0 -4
- package/dist/error-BaOQJtvf.js +0 -22
package/dist/cli/index.js
CHANGED
|
@@ -1,15 +1,205 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import { t as isCI } from "../constants-B9qjNfvr.js";
|
|
1
|
+
import { r as formatNpmDistTag } from "../semver-C4vJ4SK8.js";
|
|
2
|
+
import { a as changelogFilename, l as renderChangelog, s as generateReplays, t as assertPublishPlanFinished } from "../checks-Cwz-Ezkq.js";
|
|
3
|
+
import { a as isCI, n as execFailure, r as handlePluginError, t as CancelledError } from "../error-DNy8R5ue.js";
|
|
5
4
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
-
import path, { basename, isAbsolute, join, normalize, relative
|
|
5
|
+
import path, { basename, isAbsolute, join, normalize, relative } from "node:path";
|
|
7
6
|
import { x } from "tinyexec";
|
|
8
|
-
import {
|
|
9
|
-
import { detect, resolveCommand } from "package-manager-detector";
|
|
7
|
+
import { resolveCommand } from "package-manager-detector";
|
|
10
8
|
import { autocompleteMultiselect, confirm, intro, isCancel, multiline, note, outro, select, spinner } from "@clack/prompts";
|
|
11
9
|
import { Command, InvalidArgumentError } from "commander";
|
|
12
10
|
import { tmpdir } from "node:os";
|
|
11
|
+
//#region src/utils/git-changes.ts
|
|
12
|
+
async function getChangedPackages(graph, cwd) {
|
|
13
|
+
return resolveChangedPackages(graph, await getChangedFilePaths(cwd), cwd);
|
|
14
|
+
}
|
|
15
|
+
async function getChangedFilePaths(cwd) {
|
|
16
|
+
const files = /* @__PURE__ */ new Set();
|
|
17
|
+
for (const args of [["diff", "--name-only"], [
|
|
18
|
+
"diff",
|
|
19
|
+
"--cached",
|
|
20
|
+
"--name-only"
|
|
21
|
+
]]) await addGitOutput(files, cwd, args);
|
|
22
|
+
return Array.from(files);
|
|
23
|
+
}
|
|
24
|
+
function resolveChangedPackages(graph, files, cwd) {
|
|
25
|
+
const packages = [...graph.getPackages()].sort((a, b) => b.path.length - a.path.length);
|
|
26
|
+
const matched = /* @__PURE__ */ new Map();
|
|
27
|
+
for (const file of files) for (const pkg of packages) if (isUnderDir(file, pkg.path, cwd)) {
|
|
28
|
+
matched.set(pkg.id, pkg);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
return [...matched.values()];
|
|
32
|
+
}
|
|
33
|
+
function isUnderDir(file, dir, cwd) {
|
|
34
|
+
const absolute = join(cwd, file);
|
|
35
|
+
const rel = relative(normalize(dir), absolute);
|
|
36
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
37
|
+
}
|
|
38
|
+
async function addGitOutput(files, cwd, args) {
|
|
39
|
+
const result = await x("git", args, { nodeOptions: { cwd } });
|
|
40
|
+
if (result.exitCode !== 0) return;
|
|
41
|
+
for (const line of result.stdout.split("\n")) {
|
|
42
|
+
const trimmed = line.trim();
|
|
43
|
+
if (trimmed) files.add(trimmed);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/cli/changelog.ts
|
|
48
|
+
async function runChangelogTui(tegami) {
|
|
49
|
+
const context = await tegami._internal.context();
|
|
50
|
+
await assertPublishPlanFinished(context);
|
|
51
|
+
intro("Create changelogs");
|
|
52
|
+
let selectedPackages = [];
|
|
53
|
+
if (!isCI()) selectedPackages = await promptPackageSelection(context.graph, context.cwd);
|
|
54
|
+
if (selectedPackages.length === 0) {
|
|
55
|
+
if (!isCI()) {
|
|
56
|
+
const confirmed = await confirm({
|
|
57
|
+
message: "Auto-generate changelog files from commits?",
|
|
58
|
+
initialValue: true
|
|
59
|
+
});
|
|
60
|
+
if (isCancel(confirmed)) throw new CancelledError();
|
|
61
|
+
if (!confirmed) {
|
|
62
|
+
outro("No changelogs created.");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
await persistChangelogs(context, (await tegami.generateChangelog({ write: false })).map(({ filename, content, packages }) => ({
|
|
67
|
+
filename,
|
|
68
|
+
content,
|
|
69
|
+
packages
|
|
70
|
+
})), "No matching conventional commits were found.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const packageBumpMap = await promptPackageBumpTypes(selectedPackages);
|
|
74
|
+
const message = await multiline({
|
|
75
|
+
message: "Describe change (Markdown supported, press tab then enter to exit)",
|
|
76
|
+
placeholder: "The first line is heading\n\nAdditional description.",
|
|
77
|
+
showSubmit: true,
|
|
78
|
+
validate(value) {
|
|
79
|
+
if (!value?.trim()) return "Enter a message.";
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
if (isCancel(message)) throw new CancelledError();
|
|
83
|
+
const packages = generateReplays(context.graph, packageBumpMap);
|
|
84
|
+
await persistChangelogs(context, [{
|
|
85
|
+
filename: changelogFilename(),
|
|
86
|
+
content: renderChangelog({ packages }, `## ${message.trim()}`),
|
|
87
|
+
packages
|
|
88
|
+
}]);
|
|
89
|
+
}
|
|
90
|
+
async function persistChangelogs(context, entries, emptyMessage = "No changelogs created.") {
|
|
91
|
+
const s = spinner();
|
|
92
|
+
s.start("Creating changelog");
|
|
93
|
+
await mkdir(context.changelogDir, { recursive: true });
|
|
94
|
+
await Promise.all(entries.map(({ filename, content }) => writeFile(join(context.changelogDir, filename), content)));
|
|
95
|
+
s.stop(entries.length === 1 ? "Created 1 changelog file" : entries.length > 0 ? `Created ${entries.length} changelog files` : "No changelogs created");
|
|
96
|
+
if (entries.length === 0) note(emptyMessage, "No changelogs created");
|
|
97
|
+
else {
|
|
98
|
+
const lines = [];
|
|
99
|
+
for (const { filename, packages } of entries) {
|
|
100
|
+
lines.push(filename);
|
|
101
|
+
for (const [name, config] of Object.entries(packages)) {
|
|
102
|
+
if (typeof config === "string") {
|
|
103
|
+
lines.push(`${name}: ${config}`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (config.replay?.length) {
|
|
107
|
+
lines.push(`${name}: ${config.type} (replay on ${config.replay.join(" or ")})`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
lines.push(`${name}: ${config.type}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
note(lines.join("\n"), "Created changelogs");
|
|
114
|
+
}
|
|
115
|
+
outro(entries.length === 1 ? "Changelog ready." : "Changelogs ready.");
|
|
116
|
+
}
|
|
117
|
+
async function promptPackageSelection(graph, cwd) {
|
|
118
|
+
const useShortname = /* @__PURE__ */ new Map();
|
|
119
|
+
for (const pkg of graph.getPackages()) if (useShortname.has(pkg.name)) useShortname.set(pkg.name, false);
|
|
120
|
+
else useShortname.set(pkg.name, true);
|
|
121
|
+
const getPackageLabel = (pkg) => {
|
|
122
|
+
return useShortname.get(pkg.name) ? pkg.name : pkg.id;
|
|
123
|
+
};
|
|
124
|
+
const changedPackages = new Set(await getChangedPackages(graph, cwd));
|
|
125
|
+
const selectOptions = [];
|
|
126
|
+
const groups = [];
|
|
127
|
+
for (const group of graph.getGroups()) {
|
|
128
|
+
const changed = group.packages.some((pkg) => changedPackages.has(pkg));
|
|
129
|
+
groups.push([group, changed]);
|
|
130
|
+
}
|
|
131
|
+
groups.sort((a, b) => (a[1] ? 0 : 1) - (b[1] ? 0 : 1));
|
|
132
|
+
for (const [group, changed] of groups) {
|
|
133
|
+
const members = group.packages.map(getPackageLabel).join(", ");
|
|
134
|
+
selectOptions.push({
|
|
135
|
+
label: `(Group) ${group.name}`,
|
|
136
|
+
value: `group:${group.name}`,
|
|
137
|
+
hint: changed ? `changed · ${members}` : members
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
const packages = graph.getPackages().toSorted((a, b) => (changedPackages.has(a) ? 0 : 1) - (changedPackages.has(b) ? 0 : 1));
|
|
141
|
+
for (const pkg of packages) selectOptions.push({
|
|
142
|
+
label: getPackageLabel(pkg),
|
|
143
|
+
value: pkg.id,
|
|
144
|
+
hint: changedPackages.has(pkg) ? "changed" : void 0
|
|
145
|
+
});
|
|
146
|
+
const selected = await autocompleteMultiselect({
|
|
147
|
+
message: "Select packages (leave empty to auto-generate from commits)",
|
|
148
|
+
required: false,
|
|
149
|
+
options: selectOptions
|
|
150
|
+
});
|
|
151
|
+
if (isCancel(selected)) throw new CancelledError();
|
|
152
|
+
return selected;
|
|
153
|
+
}
|
|
154
|
+
async function promptPackageBumpTypes(selectedPackages) {
|
|
155
|
+
const packageBumpMap = {};
|
|
156
|
+
const bumpType = await select({
|
|
157
|
+
message: "Select release type",
|
|
158
|
+
options: [
|
|
159
|
+
{
|
|
160
|
+
value: "patch",
|
|
161
|
+
label: "patch"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
value: "minor",
|
|
165
|
+
label: "minor"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
value: "major",
|
|
169
|
+
label: "major"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
value: "per-package",
|
|
173
|
+
label: "choose per-package"
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
});
|
|
177
|
+
if (isCancel(bumpType)) throw new CancelledError();
|
|
178
|
+
if (bumpType === "per-package") for (const pkg of selectedPackages) {
|
|
179
|
+
const selectedBump = await select({
|
|
180
|
+
message: `Select release type for "${pkg}"`,
|
|
181
|
+
options: [
|
|
182
|
+
{
|
|
183
|
+
value: "patch",
|
|
184
|
+
label: "patch"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
value: "minor",
|
|
188
|
+
label: "minor"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
value: "major",
|
|
192
|
+
label: "major"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
});
|
|
196
|
+
if (isCancel(selectedBump)) throw new CancelledError();
|
|
197
|
+
packageBumpMap[pkg] = selectedBump;
|
|
198
|
+
}
|
|
199
|
+
else for (const pkg of selectedPackages) packageBumpMap[pkg] = bumpType;
|
|
200
|
+
return packageBumpMap;
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
13
203
|
//#region src/cli/init-agent.ts
|
|
14
204
|
const CHANGELOG_DOCS_URL$1 = "https://tegami.fuma-nama.dev/changelog";
|
|
15
205
|
async function initAgent(context, options = {}) {
|
|
@@ -74,20 +264,14 @@ function renderAgentsMd(context) {
|
|
|
74
264
|
].join("\n");
|
|
75
265
|
}
|
|
76
266
|
//#endregion
|
|
77
|
-
//#region src/utils/package-manager.ts
|
|
78
|
-
async function formatRunScriptCommand(cwd, script, agent) {
|
|
79
|
-
const resolved = resolveCommand(agent ?? (await detect({ cwd }))?.agent ?? "npm", "run", [script]);
|
|
80
|
-
if (!resolved) return `npm run ${script}`;
|
|
81
|
-
return [resolved.command, ...resolved.args].join(" ");
|
|
82
|
-
}
|
|
83
|
-
//#endregion
|
|
84
267
|
//#region src/cli/pr.ts
|
|
85
268
|
const TEGAMI_DOCS_URL = "https://tegami.fuma-nama.dev";
|
|
86
269
|
const CHANGELOG_DOCS_URL = `${TEGAMI_DOCS_URL}/changelog`;
|
|
87
270
|
const COMMENT_MARKER = "<!-- tegami -->";
|
|
88
271
|
async function buildPrPreview(context, draft, options = {}) {
|
|
89
272
|
const pullRequest = await resolvePullRequest(context, options);
|
|
90
|
-
const
|
|
273
|
+
const tegamiCommandRaw = resolveCommand(context.npm?.client ?? "npm", "run", ["tegami"]);
|
|
274
|
+
const tegamiCommand = [tegamiCommandRaw.command, ...tegamiCommandRaw.args].join(" ");
|
|
91
275
|
const prChangelogFiles = await listPullRequestChangelogFiles(context, pullRequest.baseSha, pullRequest.headSha);
|
|
92
276
|
const createLink = createChangelogUrl(context, pullRequest.headRepo, pullRequest.headRef, changelogFilename());
|
|
93
277
|
const lines = [
|
|
@@ -101,10 +285,10 @@ async function buildPrPreview(context, draft, options = {}) {
|
|
|
101
285
|
const pendingPackages = [];
|
|
102
286
|
for (const pkg of context.graph.getPackages()) {
|
|
103
287
|
const plan = draft.getPackagePlan(pkg.id);
|
|
104
|
-
if (!plan
|
|
288
|
+
if (!plan || plan.bumpVersion(pkg) === pkg.version) continue;
|
|
105
289
|
pendingPackages.push({
|
|
106
290
|
name: pkg.name,
|
|
107
|
-
type: plan.type,
|
|
291
|
+
type: plan.type ?? "—",
|
|
108
292
|
from: pkg.version,
|
|
109
293
|
to: plan.bumpVersion(pkg),
|
|
110
294
|
distTag: plan.npm?.distTag,
|
|
@@ -271,59 +455,12 @@ function createChangelogUrl(context, repo, branch, filename) {
|
|
|
271
455
|
return `https://github.com/${repo}/new/${branch.split("/").map(encodeURIComponent).join("/")}?${new URLSearchParams({ filename: filePath })}`;
|
|
272
456
|
}
|
|
273
457
|
//#endregion
|
|
274
|
-
//#region src/utils/git-changes.ts
|
|
275
|
-
async function getChangedPackages(graph, cwd) {
|
|
276
|
-
return resolveChangedPackages(graph, await getChangedFilePaths(cwd), cwd);
|
|
277
|
-
}
|
|
278
|
-
async function getChangedFilePaths(cwd) {
|
|
279
|
-
const files = /* @__PURE__ */ new Set();
|
|
280
|
-
for (const args of [["diff", "--name-only"], [
|
|
281
|
-
"diff",
|
|
282
|
-
"--cached",
|
|
283
|
-
"--name-only"
|
|
284
|
-
]]) await addGitOutput(files, cwd, args);
|
|
285
|
-
return Array.from(files);
|
|
286
|
-
}
|
|
287
|
-
function resolveChangedPackages(graph, files, cwd) {
|
|
288
|
-
const packages = [...graph.getPackages()].sort((a, b) => b.path.length - a.path.length);
|
|
289
|
-
const matched = /* @__PURE__ */ new Map();
|
|
290
|
-
for (const file of files) for (const pkg of packages) if (isUnderDir(file, pkg.path, cwd)) {
|
|
291
|
-
matched.set(pkg.id, pkg);
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
return [...matched.values()];
|
|
295
|
-
}
|
|
296
|
-
function isUnderDir(file, dir, cwd) {
|
|
297
|
-
const absolute = join(cwd, file);
|
|
298
|
-
const rel = relative(normalize(dir), absolute);
|
|
299
|
-
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
300
|
-
}
|
|
301
|
-
async function addGitOutput(files, cwd, args) {
|
|
302
|
-
const result = await x("git", args, { nodeOptions: { cwd } });
|
|
303
|
-
if (result.exitCode !== 0) return;
|
|
304
|
-
for (const line of result.stdout.split("\n")) {
|
|
305
|
-
const trimmed = line.trim();
|
|
306
|
-
if (trimmed) files.add(trimmed);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
//#endregion
|
|
310
458
|
//#region src/cli/index.ts
|
|
311
|
-
var CancelledError = class extends Error {
|
|
312
|
-
constructor() {
|
|
313
|
-
super("Cancelled.");
|
|
314
|
-
}
|
|
315
|
-
};
|
|
316
459
|
function createCli(tegami, options = {}) {
|
|
317
460
|
const program = new Command();
|
|
318
|
-
program.name("tegami").description("create changelogs").action((
|
|
319
|
-
|
|
320
|
-
cli: options
|
|
321
|
-
})));
|
|
322
|
-
program.command("version").description("draft and apply a publish plan").action((commandOptions) => runAction(tegami, async () => {
|
|
323
|
-
await versionPackages(tegami, {
|
|
324
|
-
...commandOptions,
|
|
325
|
-
cli: options
|
|
326
|
-
});
|
|
461
|
+
program.name("tegami").description("create changelogs").action(() => runAction(tegami, () => runChangelogTui(tegami)));
|
|
462
|
+
program.command("version").description("draft and apply a publish plan").action(() => runAction(tegami, async () => {
|
|
463
|
+
await versionPackages(tegami, { cli: options });
|
|
327
464
|
}));
|
|
328
465
|
program.command("ci").description("version and publish packages").action(() => runAction(tegami, async () => {
|
|
329
466
|
if (await versionPackages(tegami, { cli: options })) return;
|
|
@@ -338,10 +475,10 @@ function createCli(tegami, options = {}) {
|
|
|
338
475
|
const context = await tegami._internal.context();
|
|
339
476
|
const body = await buildPrPreview(context, await tegami.draft(), commandOptions);
|
|
340
477
|
if (commandOptions.artifact) {
|
|
341
|
-
const artifactPath = resolve(context.cwd, commandOptions.artifact);
|
|
478
|
+
const artifactPath = path.resolve(context.cwd, commandOptions.artifact);
|
|
342
479
|
await writeFile(artifactPath, body);
|
|
343
480
|
if (!isCI()) {
|
|
344
|
-
note(relative(context.cwd, artifactPath) || commandOptions.artifact, "Release preview");
|
|
481
|
+
note(path.relative(context.cwd, artifactPath) || commandOptions.artifact, "Release preview");
|
|
345
482
|
outro("Release preview ready.");
|
|
346
483
|
}
|
|
347
484
|
return;
|
|
@@ -373,134 +510,6 @@ function createCli(tegami, options = {}) {
|
|
|
373
510
|
program.command("init-agent").description("write AGENTS.md with changelog instructions for AI agents").option("-o, --output <path>", "output path", "AGENTS.md").action((commandOptions) => runAction(tegami, () => runInitAgent(tegami, commandOptions)));
|
|
374
511
|
return program;
|
|
375
512
|
}
|
|
376
|
-
async function createChangelogs(tegami, _options) {
|
|
377
|
-
const context = await tegami._internal.context();
|
|
378
|
-
await assertPublishPlanFinished(context);
|
|
379
|
-
intro("Create changelogs");
|
|
380
|
-
let selectedPackages = [];
|
|
381
|
-
if (!isCI()) {
|
|
382
|
-
const useShortname = /* @__PURE__ */ new Map();
|
|
383
|
-
for (const pkg of context.graph.getPackages()) if (useShortname.has(pkg.name)) useShortname.set(pkg.name, false);
|
|
384
|
-
else useShortname.set(pkg.name, true);
|
|
385
|
-
const getPackageLabel = (pkg) => {
|
|
386
|
-
return useShortname.get(pkg.name) ? pkg.name : pkg.id;
|
|
387
|
-
};
|
|
388
|
-
const changedPackages = new Set(await getChangedPackages(context.graph, context.cwd));
|
|
389
|
-
const selectOptions = [];
|
|
390
|
-
const groups = [];
|
|
391
|
-
for (const group of context.graph.getGroups()) {
|
|
392
|
-
const changed = group.packages.some((pkg) => changedPackages.has(pkg));
|
|
393
|
-
groups.push([group, changed]);
|
|
394
|
-
}
|
|
395
|
-
groups.sort((a, b) => (a[1] ? 0 : 1) - (b[1] ? 0 : 1));
|
|
396
|
-
for (const [group, changed] of groups) {
|
|
397
|
-
const members = group.packages.map(getPackageLabel).join(", ");
|
|
398
|
-
selectOptions.push({
|
|
399
|
-
label: `(Group) ${group.name}`,
|
|
400
|
-
value: `group:${group.name}`,
|
|
401
|
-
hint: changed ? `changed · ${members}` : members
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
const packages = context.graph.getPackages().toSorted((a, b) => (changedPackages.has(a) ? 0 : 1) - (changedPackages.has(b) ? 0 : 1));
|
|
405
|
-
for (const pkg of packages) selectOptions.push({
|
|
406
|
-
label: getPackageLabel(pkg),
|
|
407
|
-
value: pkg.id,
|
|
408
|
-
hint: changedPackages.has(pkg) ? "changed" : void 0
|
|
409
|
-
});
|
|
410
|
-
const selected = await autocompleteMultiselect({
|
|
411
|
-
message: "Select packages (leave empty to auto-generate from commits)",
|
|
412
|
-
required: false,
|
|
413
|
-
options: selectOptions
|
|
414
|
-
});
|
|
415
|
-
if (isCancel(selected)) throw new CancelledError();
|
|
416
|
-
selectedPackages = selected;
|
|
417
|
-
}
|
|
418
|
-
if (selectedPackages.length === 0) {
|
|
419
|
-
if (!isCI()) {
|
|
420
|
-
const confirmed = await confirm({
|
|
421
|
-
message: "Auto-generate changelog files from commits?",
|
|
422
|
-
initialValue: true
|
|
423
|
-
});
|
|
424
|
-
if (isCancel(confirmed)) throw new CancelledError();
|
|
425
|
-
if (!confirmed) {
|
|
426
|
-
outro("No changelogs created.");
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
const s = spinner();
|
|
431
|
-
s.start("Reading commits and creating changelogs");
|
|
432
|
-
const created = await tegami.generateChangelog();
|
|
433
|
-
s.stop(created.length === 1 ? "Created 1 changelog file" : `Created ${created.length} changelog files`);
|
|
434
|
-
if (created.length === 0) note("No matching conventional commits were found.", "No changelogs created");
|
|
435
|
-
else note(created.map((entry) => `${entry.filename} (${entry.changes} changes)`).join("\n"), "Created changelogs");
|
|
436
|
-
outro("Changelogs ready.");
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const packageBumpMap = {};
|
|
440
|
-
const bumpType = await select({
|
|
441
|
-
message: "Select release type",
|
|
442
|
-
options: [
|
|
443
|
-
{
|
|
444
|
-
value: "patch",
|
|
445
|
-
label: "patch"
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
value: "minor",
|
|
449
|
-
label: "minor"
|
|
450
|
-
},
|
|
451
|
-
{
|
|
452
|
-
value: "major",
|
|
453
|
-
label: "major"
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
value: "per-package",
|
|
457
|
-
label: "choose per-package"
|
|
458
|
-
}
|
|
459
|
-
]
|
|
460
|
-
});
|
|
461
|
-
if (isCancel(bumpType)) throw new CancelledError();
|
|
462
|
-
if (bumpType === "per-package") for (const pkg of selectedPackages) {
|
|
463
|
-
const bumpType = await select({
|
|
464
|
-
message: `Select release type for "${pkg}"`,
|
|
465
|
-
options: [
|
|
466
|
-
{
|
|
467
|
-
value: "patch",
|
|
468
|
-
label: "patch"
|
|
469
|
-
},
|
|
470
|
-
{
|
|
471
|
-
value: "minor",
|
|
472
|
-
label: "minor"
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
value: "major",
|
|
476
|
-
label: "major"
|
|
477
|
-
}
|
|
478
|
-
]
|
|
479
|
-
});
|
|
480
|
-
if (isCancel(bumpType)) throw new CancelledError();
|
|
481
|
-
packageBumpMap[pkg] = bumpType;
|
|
482
|
-
}
|
|
483
|
-
else for (const pkg of selectedPackages) packageBumpMap[pkg] = bumpType;
|
|
484
|
-
const message = await multiline({
|
|
485
|
-
message: "Describe change (Markdown supported, press tab then enter to exit)",
|
|
486
|
-
placeholder: "The first line is heading\n\nAdditional description.",
|
|
487
|
-
showSubmit: true,
|
|
488
|
-
validate(value) {
|
|
489
|
-
if (!value?.trim()) return "Enter a message.";
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
|
-
if (isCancel(message)) throw new CancelledError();
|
|
493
|
-
const filename = changelogFilename();
|
|
494
|
-
const s = spinner();
|
|
495
|
-
s.start("Creating changelog");
|
|
496
|
-
await mkdir(context.changelogDir, { recursive: true });
|
|
497
|
-
await writeFile(join(context.changelogDir, filename), renderManualChangelog(packageBumpMap, message.trim()));
|
|
498
|
-
s.stop("Created changelog file");
|
|
499
|
-
const notes = [filename];
|
|
500
|
-
for (const pkg of selectedPackages) notes.push(`${pkg}: ${packageBumpMap[pkg]}`);
|
|
501
|
-
note(notes.join("\n"), "Created changelog");
|
|
502
|
-
outro("Changelog ready.");
|
|
503
|
-
}
|
|
504
513
|
async function versionPackages(tegami, options) {
|
|
505
514
|
intro("Version Packages");
|
|
506
515
|
const { version: customVersion } = options.cli;
|
|
@@ -509,15 +518,15 @@ async function versionPackages(tegami, options) {
|
|
|
509
518
|
if (!draft.canApply()) throw new Error(`The draft plan from custom "version" hook must not be applied`);
|
|
510
519
|
for (const plugin of context.plugins) await handlePluginError(plugin, "cli.publishPlanCreated", () => plugin.cli?.publishPlanCreated?.call(context, draft));
|
|
511
520
|
if (!draft.hasPending()) {
|
|
512
|
-
note("No pending
|
|
521
|
+
note("No pending version changes matched workspace packages.", "Nothing to version");
|
|
513
522
|
outro("No versions changed.");
|
|
514
523
|
return false;
|
|
515
524
|
}
|
|
516
525
|
const planEntries = [];
|
|
517
526
|
for (const pkg of context.graph.getPackages()) {
|
|
518
527
|
const plan = draft.getPackagePlan(pkg.id);
|
|
519
|
-
if (!plan
|
|
520
|
-
planEntries.push(`${pkg.id}: ${plan.
|
|
528
|
+
if (!plan || plan.bumpVersion(pkg) === pkg.version) continue;
|
|
529
|
+
planEntries.push(`${pkg.id}: ${pkg.version} → ${plan.bumpVersion(pkg)} (${plan.changelogs?.length ?? 0} changelogs)`);
|
|
521
530
|
if (plan.bumpReasons) for (const reason of plan.bumpReasons) planEntries.push(` - ${reason}`);
|
|
522
531
|
}
|
|
523
532
|
note(planEntries.join("\n"), "Release plan");
|
|
@@ -568,7 +577,7 @@ async function runInitAgent(tegami, options) {
|
|
|
568
577
|
s.start("Writing AGENTS.md");
|
|
569
578
|
const result = await initAgent(context, options);
|
|
570
579
|
s.stop(result.created ? "Created AGENTS.md" : "Appended to AGENTS.md");
|
|
571
|
-
note(relative(context.cwd, result.path) || "AGENTS.md", "Agent instructions");
|
|
580
|
+
note(path.relative(context.cwd, result.path) || "AGENTS.md", "Agent instructions");
|
|
572
581
|
outro("Agents can follow AGENTS.md to write changelogs.");
|
|
573
582
|
}
|
|
574
583
|
async function runCleanup(tegami) {
|
|
@@ -588,16 +597,6 @@ async function runCleanup(tegami) {
|
|
|
588
597
|
}
|
|
589
598
|
outro(`Publish plan at ${planPath} is still pending. Publish it before cleanup.`);
|
|
590
599
|
}
|
|
591
|
-
function renderManualChangelog(packageBumpMap, message) {
|
|
592
|
-
return [
|
|
593
|
-
"---",
|
|
594
|
-
dump({ packages: packageBumpMap }).trim(),
|
|
595
|
-
"---",
|
|
596
|
-
"",
|
|
597
|
-
`## ${message}`,
|
|
598
|
-
""
|
|
599
|
-
].join("\n");
|
|
600
|
-
}
|
|
601
600
|
async function runAction(tegami, action) {
|
|
602
601
|
try {
|
|
603
602
|
const context = await tegami._internal.context();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/utils/constants.ts
|
|
2
|
+
const isCI = () => Boolean(process.env.CI);
|
|
3
|
+
//#endregion
|
|
4
|
+
//#region src/utils/error.ts
|
|
5
|
+
var CancelledError = class extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("Cancelled.");
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
function execFailure(context, result) {
|
|
11
|
+
const lines = [context, `(exit ${result.exitCode})`];
|
|
12
|
+
const out = result.stdout.trim();
|
|
13
|
+
const err = result.stderr.trim();
|
|
14
|
+
if (out) lines.push(out);
|
|
15
|
+
if (err) lines.push(err);
|
|
16
|
+
return new Error(redactSensitiveTokens(lines.join("\n")));
|
|
17
|
+
}
|
|
18
|
+
function isNodeError(error) {
|
|
19
|
+
return error instanceof Error && "code" in error;
|
|
20
|
+
}
|
|
21
|
+
const SENSITIVE_TOKEN_PATTERNS = [
|
|
22
|
+
/\b(npm_[a-zA-Z0-9]{36,})\b/g,
|
|
23
|
+
/\b(gh[pousrsa]_[A-Za-z0-9_]{36,})\b/g,
|
|
24
|
+
/\b(bearer\s+[a-z0-9\-_]{20,})\b/gi,
|
|
25
|
+
/\b(glpat-[A-Za-z0-9-_]{20,})\b/g
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* should not be needed, but we cannot be sure if the used CLI tools will expose secrets by accident.
|
|
29
|
+
*/
|
|
30
|
+
function redactSensitiveTokens(text) {
|
|
31
|
+
if (!isCI()) return text;
|
|
32
|
+
let redacted = text;
|
|
33
|
+
for (const pattern of SENSITIVE_TOKEN_PATTERNS) redacted = redacted.replace(pattern, "[REDACTED_TOKEN]");
|
|
34
|
+
return redacted;
|
|
35
|
+
}
|
|
36
|
+
async function handlePluginError(plugin, hookName, callback) {
|
|
37
|
+
try {
|
|
38
|
+
return await callback();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
41
|
+
throw new Error(`Plugin "${plugin.name}" failed during ${hookName}:\n${redactSensitiveTokens(details)}`, { cause: error });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { isCI as a, isNodeError as i, execFailure as n, handlePluginError as r, CancelledError as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as bumpVersion } from "./semver-C4vJ4SK8.js";
|
|
2
2
|
//#region src/graph.ts
|
|
3
3
|
/** Package discovered in the workspace. */
|
|
4
4
|
var WorkspacePackage = class {
|
|
@@ -13,17 +13,22 @@ var WorkspacePackage = class {
|
|
|
13
13
|
setPackageOptions(options) {
|
|
14
14
|
this.opts = options;
|
|
15
15
|
}
|
|
16
|
-
/** create
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
/** create the initial draft plan. */
|
|
17
|
+
initPlan() {
|
|
18
|
+
return { bumpVersion(pkg) {
|
|
19
|
+
return bumpVersion(pkg.version, this.type, this.prerelease);
|
|
20
|
+
} };
|
|
21
|
+
}
|
|
22
|
+
/** configure an initial draft plan to match script-level configs. */
|
|
23
|
+
configurePlan(plan, group) {
|
|
24
|
+
const groupOptions = group?.options;
|
|
25
|
+
const { publish, prerelease = groupOptions?.prerelease, npm: { distTag = groupOptions?.npm?.distTag } = {} } = this.opts;
|
|
26
|
+
if (publish !== void 0) plan.publish = publish;
|
|
27
|
+
if (prerelease !== void 0) plan.prerelease = prerelease;
|
|
28
|
+
if (distTag) {
|
|
29
|
+
plan.npm ??= {};
|
|
30
|
+
plan.npm.distTag = distTag;
|
|
31
|
+
}
|
|
27
32
|
}
|
|
28
33
|
};
|
|
29
34
|
/** Dependency graph for discovered workspace packages. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as PackagePlan, C as PublishOptions, D as PackageGroup, E as PackageGraph, O as WorkspacePackage, S as PackagePublishResult, a as PublishPreflight, b as
|
|
2
|
-
export { type
|
|
1
|
+
import { A as PackagePlan, C as PublishOptions, D as PackageGroup, E as PackageGraph, O as WorkspacePackage, S as PackagePublishResult, a as PublishPreflight, b as GenerateChangelogOptions, c as TegamiPlugin, i as PackageOptions, k as DraftPlan, l as TegamiPluginOption, n as GroupOptions, o as RegistryClient, r as LogGenerator, s as TegamiOptions, v as Tegami, w as PublishResult, x as GeneratedChangelog, y as tegami } from "./types-BXk2fMqa.js";
|
|
2
|
+
export { type DraftPlan, type GenerateChangelogOptions, type GeneratedChangelog, type GroupOptions, type LogGenerator, type PackageGraph, type PackageGroup, type PackageOptions, type PackagePlan, type PackagePublishResult, type PublishOptions, type PublishPreflight, type PublishResult, type RegistryClient, Tegami, type TegamiOptions, type TegamiPlugin, type TegamiPluginOption, type WorkspacePackage, tegami };
|