tegami 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cli/index.d.mts +12 -0
- package/dist/cli/index.mjs +199 -0
- package/dist/constants-B9qjNfvr.mjs +4 -0
- package/dist/{types-DdIMewK9.d.mts → context-CC36jtz1.d.mts} +64 -50
- package/dist/exec-hOS852t5.mjs +12 -0
- package/dist/generators/simple.d.mts +1 -1
- package/dist/generators/simple.mjs +3 -2
- package/dist/index-DJ7YxxdM.d.mts +35 -0
- package/dist/index.d.mts +2 -30
- package/dist/index.mjs +47 -37
- package/dist/{npm-BSE_dtB3.mjs → npm-BpFlXy8I.mjs} +5 -4
- package/dist/plugins/git.d.mts +4 -5
- package/dist/plugins/git.mjs +50 -7
- package/dist/plugins/github.d.mts +19 -1
- package/dist/plugins/github.mjs +107 -2
- package/dist/providers/cargo.d.mts +1 -1
- package/dist/providers/cargo.mjs +2 -2
- package/dist/providers/npm.d.mts +1 -1
- package/dist/providers/npm.mjs +1 -1
- package/dist/semver-FSWx3_34.mjs +35 -0
- package/package.json +32 -26
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fuma Nama
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { _ as PublishResult, n as Awaitable, y as DraftPlan } from "../context-CC36jtz1.mjs";
|
|
2
|
+
import { t as Tegami } from "../index-DJ7YxxdM.mjs";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/index.d.ts
|
|
6
|
+
interface TegamiCLIOptions {
|
|
7
|
+
version?: () => Awaitable<DraftPlan>;
|
|
8
|
+
publish?: () => Awaitable<PublishResult>;
|
|
9
|
+
}
|
|
10
|
+
declare function createCli(tegami: Tegami, options?: TegamiCLIOptions): Command;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { TegamiCLIOptions, createCli };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { t as isCI } from "../constants-B9qjNfvr.mjs";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { confirm, intro, isCancel, multiselect, note, outro, select, spinner, text } from "@clack/prompts";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
//#region src/cli/index.ts
|
|
7
|
+
var CancelledError = class extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("Cancelled.");
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
function createCli(tegami, options = {}) {
|
|
13
|
+
const program = new Command();
|
|
14
|
+
program.name("tegami").description("Create changelogs, version packages, and publish releases.").action((commandOptions) => runAction(tegami, () => createChangelogs(tegami, {
|
|
15
|
+
...commandOptions,
|
|
16
|
+
cli: options
|
|
17
|
+
})));
|
|
18
|
+
program.command("version").description("create a publish plan and update package versions").action((commandOptions) => runAction(tegami, () => versionPackages(tegami, {
|
|
19
|
+
...commandOptions,
|
|
20
|
+
cli: options
|
|
21
|
+
})));
|
|
22
|
+
program.command("publish").description("publish packages from the current publish plan").option("--dry-run", "validate the publish plan without publishing packages").action((commandOptions) => runAction(tegami, () => publishPackages(tegami, {
|
|
23
|
+
...commandOptions,
|
|
24
|
+
cli: options
|
|
25
|
+
})));
|
|
26
|
+
return program;
|
|
27
|
+
}
|
|
28
|
+
async function createChangelogs(tegami, _options) {
|
|
29
|
+
intro("Create changelogs");
|
|
30
|
+
const { graph, cwd, changelogDir } = await tegami._internal.context();
|
|
31
|
+
const packages = graph.getPackages();
|
|
32
|
+
let selectedPackages = [];
|
|
33
|
+
if (isCI()) selectedPackages = [];
|
|
34
|
+
else {
|
|
35
|
+
const selected = await multiselect({
|
|
36
|
+
message: "Select packages (leave empty to auto-generate from commits)",
|
|
37
|
+
required: false,
|
|
38
|
+
options: packages.map((pkg) => ({
|
|
39
|
+
value: pkg.id,
|
|
40
|
+
label: pkg.id,
|
|
41
|
+
hint: pkg.version
|
|
42
|
+
}))
|
|
43
|
+
});
|
|
44
|
+
if (isCancel(selected)) throw new CancelledError();
|
|
45
|
+
selectedPackages = selected;
|
|
46
|
+
}
|
|
47
|
+
if (selectedPackages.length === 0) {
|
|
48
|
+
if (!isCI()) {
|
|
49
|
+
const confirmed = await confirm({
|
|
50
|
+
message: "Auto-generate changelog files from commits?",
|
|
51
|
+
initialValue: true
|
|
52
|
+
});
|
|
53
|
+
if (isCancel(confirmed)) throw new CancelledError();
|
|
54
|
+
if (!confirmed) {
|
|
55
|
+
outro("No changelogs created.");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const s = spinner();
|
|
60
|
+
s.start("Reading commits and creating changelogs");
|
|
61
|
+
const created = await tegami.generateChangelog();
|
|
62
|
+
s.stop(created.length === 1 ? "Created 1 changelog file" : `Created ${created.length} changelog files`);
|
|
63
|
+
if (created.length === 0) note("No matching conventional commits were found.", "No changelogs created");
|
|
64
|
+
else note(created.map((entry) => `${entry.filename} (${entry.changes} changes)`).join("\n"), "Created changelogs");
|
|
65
|
+
outro("Changelogs ready.");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const type = await select({
|
|
69
|
+
message: "Select release type",
|
|
70
|
+
options: [
|
|
71
|
+
{
|
|
72
|
+
value: "patch",
|
|
73
|
+
label: "patch"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
value: "minor",
|
|
77
|
+
label: "minor"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
value: "major",
|
|
81
|
+
label: "major"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
if (isCancel(type)) throw new CancelledError();
|
|
86
|
+
const message = await text({
|
|
87
|
+
message: "Change message",
|
|
88
|
+
placeholder: "Add a concise release note",
|
|
89
|
+
validate(value) {
|
|
90
|
+
if (!value?.trim()) return "Enter a message.";
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
if (isCancel(message)) throw new CancelledError();
|
|
94
|
+
const filename = changelogFilename();
|
|
95
|
+
const directory = resolve(cwd, changelogDir);
|
|
96
|
+
const s = spinner();
|
|
97
|
+
s.start("Creating changelog");
|
|
98
|
+
await mkdir(directory, { recursive: true });
|
|
99
|
+
await writeFile(join(directory, filename), renderManualChangelog(selectedPackages, type, message.trim()));
|
|
100
|
+
s.stop("Created changelog file");
|
|
101
|
+
note(`${filename}\n${selectedPackages.join(", ")}: ${type}`, "Created changelog");
|
|
102
|
+
outro("Changelog ready.");
|
|
103
|
+
}
|
|
104
|
+
async function versionPackages(tegami, options) {
|
|
105
|
+
intro("Version Packages");
|
|
106
|
+
const { version: customVersion } = options.cli;
|
|
107
|
+
const draft = customVersion ? await customVersion() : await tegami.draft();
|
|
108
|
+
const packageIds = draft.getPackageIds();
|
|
109
|
+
if (packageIds.length === 0) {
|
|
110
|
+
note("No pending changelog entries matched workspace packages.", "Nothing to version");
|
|
111
|
+
outro("No versions changed.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
note(packageIds.map((id) => {
|
|
115
|
+
const plan = draft.getPackage(id);
|
|
116
|
+
return `${id}: ${plan.type} (${plan.changelogIds.size} changelogs)`;
|
|
117
|
+
}).join("\n"), "Release plan");
|
|
118
|
+
if (draft.editable()) {
|
|
119
|
+
const s = spinner();
|
|
120
|
+
s.start("Updating package versions");
|
|
121
|
+
try {
|
|
122
|
+
await draft.createPublishPlan();
|
|
123
|
+
} catch (error) {
|
|
124
|
+
s.stop("Failed to create publish plan");
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
s.stop("Package versions updated");
|
|
128
|
+
}
|
|
129
|
+
const context = await tegami._internal.context();
|
|
130
|
+
for (const plugin of context.plugins) try {
|
|
131
|
+
await plugin.cli?.afterVersion?.call(context, draft);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
134
|
+
throw new Error(`Plugin "${plugin.name}" failed during afterVersion:\n${details}`, { cause: error });
|
|
135
|
+
}
|
|
136
|
+
outro("Publish plan created.");
|
|
137
|
+
}
|
|
138
|
+
async function publishPackages(tegami, options) {
|
|
139
|
+
const dryRun = options.dryRun ?? false;
|
|
140
|
+
const { publish: customPublish } = options.cli;
|
|
141
|
+
intro(dryRun ? "Publish packages (dry run)" : "Publish packages");
|
|
142
|
+
const s = spinner();
|
|
143
|
+
s.start(dryRun ? "Validating publish plan" : "Publishing packages");
|
|
144
|
+
const result = customPublish ? await customPublish() : await tegami.publish({ dryRun });
|
|
145
|
+
if (result.state === "skipped") {
|
|
146
|
+
s.stop(dryRun ? "No publish plan to validate" : "Nothing to publish");
|
|
147
|
+
outro(`No publishable packages were found in ${result.planPath}.`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
s.stop(dryRun ? "Publish plan validated" : "Publish complete");
|
|
151
|
+
note(result.packages.map((pkg) => {
|
|
152
|
+
const tag = pkg.distTag ? ` (${pkg.distTag})` : "";
|
|
153
|
+
const suffix = pkg.state === "failed" && pkg.error ? `: ${pkg.error}` : "";
|
|
154
|
+
return `${pkg.state === "success" ? "success" : "failed"} ${pkg.name}@${pkg.version}${tag}${suffix}`;
|
|
155
|
+
}).join("\n"), dryRun ? "Publish dry run" : "Publish result");
|
|
156
|
+
if (result.state === "failed") {
|
|
157
|
+
process.exitCode = 1;
|
|
158
|
+
outro("Some packages failed to publish.");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
outro(dryRun ? "Publish plan is valid." : "Packages published.");
|
|
162
|
+
}
|
|
163
|
+
function renderManualChangelog(packages, type, message) {
|
|
164
|
+
const heading = "#".repeat(type === "major" ? 1 : type === "minor" ? 2 : 3);
|
|
165
|
+
return [
|
|
166
|
+
"---",
|
|
167
|
+
`packages: ${JSON.stringify(packages)}`,
|
|
168
|
+
"---",
|
|
169
|
+
"",
|
|
170
|
+
`${heading} ${message}`,
|
|
171
|
+
""
|
|
172
|
+
].join("\n");
|
|
173
|
+
}
|
|
174
|
+
function changelogFilename() {
|
|
175
|
+
const date = /* @__PURE__ */ new Date();
|
|
176
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}-${Date.now().toString(36)}.md`;
|
|
177
|
+
}
|
|
178
|
+
async function runAction(tegami, action) {
|
|
179
|
+
try {
|
|
180
|
+
const context = await tegami._internal.context();
|
|
181
|
+
for (const plugin of context.plugins) try {
|
|
182
|
+
await plugin.cli?.init?.call(context);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
185
|
+
throw new Error(`Plugin "${plugin.name}" failed during cli.init:\n${details}`, { cause: error });
|
|
186
|
+
}
|
|
187
|
+
await action();
|
|
188
|
+
} catch (error) {
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
if (error instanceof CancelledError) {
|
|
191
|
+
outro(error.message);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
note(error instanceof Error ? error.message : String(error), "Error");
|
|
195
|
+
outro("Command failed.");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
export { createCli };
|
|
@@ -2,48 +2,6 @@ import * as semver from "semver";
|
|
|
2
2
|
import { SemVer } from "semver";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
|
-
//#region src/workspace.d.ts
|
|
6
|
-
/** Package discovered in the workspace. */
|
|
7
|
-
declare abstract class WorkspacePackage {
|
|
8
|
-
abstract readonly name: string;
|
|
9
|
-
abstract readonly path: string;
|
|
10
|
-
abstract readonly manager: string;
|
|
11
|
-
abstract readonly version: string;
|
|
12
|
-
abstract readonly publish: boolean;
|
|
13
|
-
get distTag(): string | undefined;
|
|
14
|
-
get id(): string;
|
|
15
|
-
setVersion?(version: string): void;
|
|
16
|
-
updateDependency?(target: WorkspacePackage, version: string, context: TegamiContext): Awaitable<void>;
|
|
17
|
-
write?(): Awaitable<void>;
|
|
18
|
-
protected updateRange(context: TegamiContext, spec: DependencySpec, next: semver.SemVer): Promise<DependencySpec>;
|
|
19
|
-
}
|
|
20
|
-
/** Dependency graph for discovered workspace packages. */
|
|
21
|
-
declare class PackageGraph {
|
|
22
|
-
private readonly byId;
|
|
23
|
-
private readonly byName;
|
|
24
|
-
constructor(packages?: WorkspacePackage[]);
|
|
25
|
-
getPackages(): WorkspacePackage[];
|
|
26
|
-
/** Get a package by exact id. */
|
|
27
|
-
get(id: string): WorkspacePackage | undefined;
|
|
28
|
-
/** Get packages by id, or every package matching a name. */
|
|
29
|
-
getByName(nameOrId: string): WorkspacePackage[];
|
|
30
|
-
/** scan package into graph, if the package id already exists, replace the existing one in graph */
|
|
31
|
-
add(pkg: WorkspacePackage): void;
|
|
32
|
-
delete(pkg: WorkspacePackage): void;
|
|
33
|
-
}
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/context.d.ts
|
|
36
|
-
interface TegamiContext {
|
|
37
|
-
cwd: string;
|
|
38
|
-
changelogDir: string;
|
|
39
|
-
planPath: string;
|
|
40
|
-
options: TegamiOptions;
|
|
41
|
-
plugins: TegamiPlugin[];
|
|
42
|
-
graph: PackageGraph;
|
|
43
|
-
/** error if doesn't exist */
|
|
44
|
-
getRegistryClient(pkgOrId: WorkspacePackage | string): RegistryClient;
|
|
45
|
-
}
|
|
46
|
-
//#endregion
|
|
47
5
|
//#region src/utils/semver.d.ts
|
|
48
6
|
type BumpType = "major" | "minor" | "patch";
|
|
49
7
|
//#endregion
|
|
@@ -89,6 +47,7 @@ declare class DraftPlan {
|
|
|
89
47
|
deleteChangelog(id: string): boolean;
|
|
90
48
|
/** Write the publish plan, update package versions, and consume changelog files. */
|
|
91
49
|
createPublishPlan(): Promise<void>;
|
|
50
|
+
editable(): boolean;
|
|
92
51
|
private assertPublishPlanFinished;
|
|
93
52
|
private applyVersionChanges;
|
|
94
53
|
private removeConsumedChangelogs;
|
|
@@ -157,14 +116,21 @@ interface PublishOptions {
|
|
|
157
116
|
/** Validate the publish plan without publishing packages, creating tags, or running release plugins. */
|
|
158
117
|
dryRun?: boolean;
|
|
159
118
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
packages: PackagePublishResult[];
|
|
165
|
-
/** the persisted plan object. This is not a public API, can be changed without notice */
|
|
119
|
+
type PublishResult = {
|
|
120
|
+
state: "created";
|
|
121
|
+
packages: PackagePublishResult[]; /** Path to the publish plan that was executed. */
|
|
122
|
+
planPath: string; /** the persisted plan object. This is not a public API, can be changed without notice */
|
|
166
123
|
_rawPlan: PlanStore;
|
|
167
|
-
}
|
|
124
|
+
} | {
|
|
125
|
+
state: "failed";
|
|
126
|
+
error?: string;
|
|
127
|
+
packages: PackagePublishResult[]; /** Path to the publish plan that was executed. */
|
|
128
|
+
planPath: string; /** the persisted plan object. This is not a public API, can be changed without notice */
|
|
129
|
+
_rawPlan: PlanStore;
|
|
130
|
+
} | {
|
|
131
|
+
state: "skipped"; /** Path to the publish plan that was executed or missing. */
|
|
132
|
+
planPath: string;
|
|
133
|
+
};
|
|
168
134
|
type PackagePublishResult = ({
|
|
169
135
|
state: "failed";
|
|
170
136
|
error?: string;
|
|
@@ -179,6 +145,36 @@ type PackagePublishResult = ({
|
|
|
179
145
|
changelogs: ChangelogEntry[];
|
|
180
146
|
};
|
|
181
147
|
//#endregion
|
|
148
|
+
//#region src/workspace.d.ts
|
|
149
|
+
/** Package discovered in the workspace. */
|
|
150
|
+
declare abstract class WorkspacePackage {
|
|
151
|
+
abstract readonly name: string;
|
|
152
|
+
abstract readonly path: string;
|
|
153
|
+
abstract readonly manager: string;
|
|
154
|
+
abstract readonly version: string;
|
|
155
|
+
abstract readonly publish: boolean;
|
|
156
|
+
get distTag(): string | undefined;
|
|
157
|
+
get id(): string;
|
|
158
|
+
setVersion?(version: string): void;
|
|
159
|
+
updateDependency?(target: WorkspacePackage, version: string, context: TegamiContext): Awaitable<void>;
|
|
160
|
+
write?(): Awaitable<void>;
|
|
161
|
+
protected updateRange(context: TegamiContext, spec: DependencySpec, next: semver.SemVer): Promise<DependencySpec>;
|
|
162
|
+
}
|
|
163
|
+
/** Dependency graph for discovered workspace packages. */
|
|
164
|
+
declare class PackageGraph {
|
|
165
|
+
private readonly byId;
|
|
166
|
+
private readonly byName;
|
|
167
|
+
constructor(packages?: WorkspacePackage[]);
|
|
168
|
+
getPackages(): WorkspacePackage[];
|
|
169
|
+
/** Get a package by exact id. */
|
|
170
|
+
get(id: string): WorkspacePackage | undefined;
|
|
171
|
+
/** Get packages by id, or every package matching a name. */
|
|
172
|
+
getByName(nameOrId: string): WorkspacePackage[];
|
|
173
|
+
/** scan package into graph, if the package id already exists, replace the existing one in graph */
|
|
174
|
+
add(pkg: WorkspacePackage): void;
|
|
175
|
+
delete(pkg: WorkspacePackage): void;
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
182
178
|
//#region src/providers/npm.d.ts
|
|
183
179
|
declare class NpmPackage extends WorkspacePackage {
|
|
184
180
|
readonly path: string;
|
|
@@ -219,6 +215,7 @@ interface LogGenerator {
|
|
|
219
215
|
generate(this: TegamiContext, opts: {
|
|
220
216
|
packageName: string;
|
|
221
217
|
version: string;
|
|
218
|
+
distTag?: string;
|
|
222
219
|
changelogs: ChangelogEntry[];
|
|
223
220
|
}): string | Promise<string>;
|
|
224
221
|
}
|
|
@@ -253,6 +250,11 @@ interface TegamiPlugin {
|
|
|
253
250
|
afterPublish?(this: TegamiContext & {
|
|
254
251
|
publishOptions: PublishOptions;
|
|
255
252
|
}, result: PublishResult): Awaitable<PublishResult | void | undefined>;
|
|
253
|
+
/** CLI lifecycle hooks. */
|
|
254
|
+
cli?: {
|
|
255
|
+
/** Called once before a CLI command runs. */init?(this: TegamiContext): Awaitable<void>; /** Called after `tegami version` creates a publish plan. */
|
|
256
|
+
afterVersion?(this: TegamiContext, draft: DraftPlan): Awaitable<void>;
|
|
257
|
+
};
|
|
256
258
|
/**
|
|
257
259
|
* @param pkg - the package that referenced the dependency
|
|
258
260
|
* @param spec - the referenced dependency & its range
|
|
@@ -280,4 +282,16 @@ interface DependencySpec {
|
|
|
280
282
|
range: string;
|
|
281
283
|
}
|
|
282
284
|
//#endregion
|
|
283
|
-
|
|
285
|
+
//#region src/context.d.ts
|
|
286
|
+
interface TegamiContext {
|
|
287
|
+
cwd: string;
|
|
288
|
+
changelogDir: string;
|
|
289
|
+
planPath: string;
|
|
290
|
+
options: TegamiOptions;
|
|
291
|
+
plugins: TegamiPlugin[];
|
|
292
|
+
graph: PackageGraph;
|
|
293
|
+
/** error if doesn't exist */
|
|
294
|
+
getRegistryClient(pkgOrId: WorkspacePackage | string): RegistryClient;
|
|
295
|
+
}
|
|
296
|
+
//#endregion
|
|
297
|
+
export { PublishResult as _, RegistryClient as a, PackageOptions as b, TegamiPluginOption as c, NpmRegistryClient as d, npm as f, PublishOptions as g, PackagePublishResult as h, PublishPlanStatus as i, NpmClient as l, WorkspacePackage as m, Awaitable as n, TegamiOptions as o, PackageGraph as p, LogGenerator as r, TegamiPlugin as s, TegamiContext as t, NpmPackage as u, PlanStore as v, PackagePlan as x, DraftPlan as y };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/utils/exec.ts
|
|
2
|
+
function commandOutput(result) {
|
|
3
|
+
return [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
4
|
+
}
|
|
5
|
+
function execFailure(context, result) {
|
|
6
|
+
const lines = [context, `(exit ${result.exitCode})`];
|
|
7
|
+
const output = commandOutput(result);
|
|
8
|
+
if (output) lines.push(output);
|
|
9
|
+
return lines.join("\n");
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { execFailure as t };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { n as formatPackageVersion } from "../semver-FSWx3_34.mjs";
|
|
1
2
|
//#region src/generators/simple.ts
|
|
2
3
|
function simpleGenerator() {
|
|
3
|
-
return { generate({ changelogs, version }) {
|
|
4
|
+
return { generate({ changelogs, version, packageName, distTag }) {
|
|
4
5
|
return [
|
|
5
|
-
`## ${version}`,
|
|
6
|
+
`## ${formatPackageVersion(packageName, version, distTag)}`,
|
|
6
7
|
"",
|
|
7
8
|
...changelogs.flatMap((entry) => [
|
|
8
9
|
`### ${entry.title}`,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { _ as PublishResult, g as PublishOptions, o as TegamiOptions, p as PackageGraph, t as TegamiContext, y as DraftPlan } from "./context-CC36jtz1.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/changelog/create.d.ts
|
|
4
|
+
interface CreateChangelogOptions {
|
|
5
|
+
/** Start revision. Defaults to the latest reachable git tag, or all history if none exists. */
|
|
6
|
+
from?: string;
|
|
7
|
+
/** End revision. Defaults to HEAD. */
|
|
8
|
+
to?: string;
|
|
9
|
+
}
|
|
10
|
+
interface CreatedChangelog {
|
|
11
|
+
filename: string;
|
|
12
|
+
path: string;
|
|
13
|
+
packages: string[];
|
|
14
|
+
changes: number;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/index.d.ts
|
|
18
|
+
interface Tegami {
|
|
19
|
+
/** Create pending changelog files from git commit history. */
|
|
20
|
+
generateChangelog(options?: CreateChangelogOptions): Promise<CreatedChangelog[]>;
|
|
21
|
+
/** Build an editable draft from pending changelog files. */
|
|
22
|
+
draft(): Promise<DraftPlan>;
|
|
23
|
+
/** Publish the current publish plan. */
|
|
24
|
+
publish(options?: PublishOptions): Promise<PublishResult>;
|
|
25
|
+
/** Internal APIs, do not use it unless you know what you are doing */
|
|
26
|
+
_internal: {
|
|
27
|
+
context(): Promise<TegamiContext>;
|
|
28
|
+
graph(): Promise<PackageGraph>;
|
|
29
|
+
options: TegamiOptions;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Create a Tegami project handle. */
|
|
33
|
+
declare function tegami(options?: TegamiOptions): Tegami;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { CreatedChangelog as i, tegami as n, CreateChangelogOptions as r, Tegami as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
|
|
3
|
-
//#region src/changelog/create.d.ts
|
|
4
|
-
interface CreateChangelogOptions {
|
|
5
|
-
/** Start revision. Defaults to the latest reachable git tag, or all history if none exists. */
|
|
6
|
-
from?: string;
|
|
7
|
-
/** End revision. Defaults to HEAD. */
|
|
8
|
-
to?: string;
|
|
9
|
-
}
|
|
10
|
-
interface CreatedChangelog {
|
|
11
|
-
filename: string;
|
|
12
|
-
path: string;
|
|
13
|
-
packages: string[];
|
|
14
|
-
changes: number;
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region src/index.d.ts
|
|
18
|
-
interface Tegami {
|
|
19
|
-
/** Create pending changelog files from git commit history. */
|
|
20
|
-
createChangelog(options?: CreateChangelogOptions): Promise<CreatedChangelog[]>;
|
|
21
|
-
/** Build an editable draft from pending changelog files. */
|
|
22
|
-
draft(): Promise<DraftPlan>;
|
|
23
|
-
/** Discover workspace packages and their dependency relationships. */
|
|
24
|
-
graph(): Promise<PackageGraph>;
|
|
25
|
-
/** Publish the current publish plan. */
|
|
26
|
-
publish(options?: PublishOptions): Promise<PublishResult>;
|
|
27
|
-
}
|
|
28
|
-
/** Create a Tegami project handle. */
|
|
29
|
-
declare function tegami(options?: TegamiOptions): Tegami;
|
|
30
|
-
//#endregion
|
|
1
|
+
import { _ as PublishResult, a as RegistryClient, b as PackageOptions, c as TegamiPluginOption, g as PublishOptions, h as PackagePublishResult, m as WorkspacePackage, o as TegamiOptions, p as PackageGraph, r as LogGenerator, s as TegamiPlugin, x as PackagePlan, y as DraftPlan } from "./context-CC36jtz1.mjs";
|
|
2
|
+
import { i as CreatedChangelog, n as tegami, r as CreateChangelogOptions, t as Tegami } from "./index-DJ7YxxdM.mjs";
|
|
31
3
|
export { type CreateChangelogOptions, type CreatedChangelog, type DraftPlan, type LogGenerator, type PackageGraph, type PackageOptions, type PackagePlan, type PackagePublishResult, type PublishOptions, type PublishResult, type RegistryClient, Tegami, type TegamiOptions, type TegamiPlugin, type TegamiPluginOption, type WorkspacePackage, tegami };
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { r as isNodeError, t as PackageGraph } from "./workspace-B5_i21S0.mjs";
|
|
2
2
|
import { cargo } from "./providers/cargo.mjs";
|
|
3
|
-
import { a as planStoreSchema, i as changelogFrontmatterSchema, r as npm } from "./npm-
|
|
3
|
+
import { a as planStoreSchema, i as changelogFrontmatterSchema, r as npm } from "./npm-BpFlXy8I.mjs";
|
|
4
|
+
import { i as maxBump, t as bumpVersion } from "./semver-FSWx3_34.mjs";
|
|
4
5
|
import { simpleGenerator } from "./generators/simple.mjs";
|
|
5
6
|
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
6
7
|
import path, { basename, dirname, join, resolve } from "node:path";
|
|
7
8
|
import { x } from "tinyexec";
|
|
8
|
-
import { inc } from "semver";
|
|
9
9
|
import { load } from "js-yaml";
|
|
10
10
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
11
11
|
import { toMarkdown } from "mdast-util-to-markdown";
|
|
@@ -178,18 +178,6 @@ function resolvePlugins(plugins = []) {
|
|
|
178
178
|
return plugins.flat(Infinity).sort((a, b) => PLUGIN_ORDER[a.enforce ?? "default"] - PLUGIN_ORDER[b.enforce ?? "default"]);
|
|
179
179
|
}
|
|
180
180
|
//#endregion
|
|
181
|
-
//#region src/utils/semver.ts
|
|
182
|
-
function maxBump(a, b) {
|
|
183
|
-
if (a === "major" || b === "major") return "major";
|
|
184
|
-
if (a === "minor" || b === "minor") return "minor";
|
|
185
|
-
return "patch";
|
|
186
|
-
}
|
|
187
|
-
function bumpVersion(version, type) {
|
|
188
|
-
const next = inc(version, type);
|
|
189
|
-
if (!next) throw new Error(`Invalid semver version: ${version}`);
|
|
190
|
-
return next;
|
|
191
|
-
}
|
|
192
|
-
//#endregion
|
|
193
181
|
//#region src/draft.ts
|
|
194
182
|
var DraftPlan = class {
|
|
195
183
|
changelogs;
|
|
@@ -253,6 +241,9 @@ var DraftPlan = class {
|
|
|
253
241
|
await writeFile(this.context.planPath, planStoreSchema.encode(plan));
|
|
254
242
|
await this.removeConsumedChangelogs();
|
|
255
243
|
}
|
|
244
|
+
editable() {
|
|
245
|
+
return !this.#created;
|
|
246
|
+
}
|
|
256
247
|
async assertPublishPlanFinished() {
|
|
257
248
|
const content = await readFile(this.context.planPath, "utf8").catch(() => void 0);
|
|
258
249
|
if (!content) return;
|
|
@@ -312,6 +303,7 @@ var DraftPlan = class {
|
|
|
312
303
|
const generated = await generator.generate.call(this.context, {
|
|
313
304
|
packageName: pkg.name,
|
|
314
305
|
version: pkg.version,
|
|
306
|
+
distTag: plan.distTag,
|
|
315
307
|
changelogs
|
|
316
308
|
});
|
|
317
309
|
const path = join(pkg.path, "CHANGELOG.md");
|
|
@@ -470,17 +462,9 @@ function headingToBump(depth) {
|
|
|
470
462
|
}
|
|
471
463
|
//#endregion
|
|
472
464
|
//#region src/publish.ts
|
|
473
|
-
async function publishFromPlan(context,
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
planPath: context.planPath,
|
|
477
|
-
state: packages.some((pkg) => pkg.state === "failed") ? "failed" : "success",
|
|
478
|
-
packages,
|
|
479
|
-
_rawPlan: plan
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
async function publishStoredPlan(store, context, { dryRun = false }) {
|
|
483
|
-
const results = [];
|
|
465
|
+
async function publishFromPlan(context, store, options) {
|
|
466
|
+
const { dryRun = false } = options;
|
|
467
|
+
const packages = [];
|
|
484
468
|
for (const [id, plan] of Object.entries(store.packages)) {
|
|
485
469
|
if (!plan.publish) continue;
|
|
486
470
|
const pkg = context.graph.get(id);
|
|
@@ -496,7 +480,7 @@ async function publishStoredPlan(store, context, { dryRun = false }) {
|
|
|
496
480
|
}
|
|
497
481
|
if (!dryRun) {
|
|
498
482
|
if (await context.getRegistryClient(pkg).packageVersionExists(pkg, pkg.version)) {
|
|
499
|
-
|
|
483
|
+
packages.push({
|
|
500
484
|
id: pkg.id,
|
|
501
485
|
name: pkg.name,
|
|
502
486
|
version: pkg.version,
|
|
@@ -509,7 +493,7 @@ async function publishStoredPlan(store, context, { dryRun = false }) {
|
|
|
509
493
|
}
|
|
510
494
|
try {
|
|
511
495
|
if (!dryRun) await context.getRegistryClient(pkg).publish(pkg, { distTag: plan.distTag });
|
|
512
|
-
|
|
496
|
+
packages.push({
|
|
513
497
|
id: pkg.id,
|
|
514
498
|
name: pkg.name,
|
|
515
499
|
version: pkg.version,
|
|
@@ -518,7 +502,7 @@ async function publishStoredPlan(store, context, { dryRun = false }) {
|
|
|
518
502
|
changelogs
|
|
519
503
|
});
|
|
520
504
|
} catch (error) {
|
|
521
|
-
|
|
505
|
+
packages.push({
|
|
522
506
|
id: pkg.id,
|
|
523
507
|
name: pkg.name,
|
|
524
508
|
version: pkg.version,
|
|
@@ -529,32 +513,58 @@ async function publishStoredPlan(store, context, { dryRun = false }) {
|
|
|
529
513
|
});
|
|
530
514
|
}
|
|
531
515
|
}
|
|
532
|
-
return
|
|
516
|
+
if (packages.length === 0) return {
|
|
517
|
+
state: "skipped",
|
|
518
|
+
planPath: context.planPath
|
|
519
|
+
};
|
|
520
|
+
return {
|
|
521
|
+
planPath: context.planPath,
|
|
522
|
+
state: packages.some((pkg) => pkg.state === "failed") ? "failed" : "created",
|
|
523
|
+
packages,
|
|
524
|
+
_rawPlan: store
|
|
525
|
+
};
|
|
533
526
|
}
|
|
534
527
|
//#endregion
|
|
535
528
|
//#region src/index.ts
|
|
536
529
|
/** Create a Tegami project handle. */
|
|
537
530
|
function tegami(options = {}) {
|
|
531
|
+
const $context = init();
|
|
532
|
+
async function init() {
|
|
533
|
+
return createTegamiContext(options);
|
|
534
|
+
}
|
|
538
535
|
return {
|
|
539
|
-
async
|
|
540
|
-
return createChangelog(await
|
|
536
|
+
async generateChangelog(createOptions = {}) {
|
|
537
|
+
return createChangelog(await $context, createOptions);
|
|
538
|
+
},
|
|
539
|
+
_internal: {
|
|
540
|
+
options,
|
|
541
|
+
context() {
|
|
542
|
+
return $context;
|
|
543
|
+
},
|
|
544
|
+
async graph() {
|
|
545
|
+
return (await $context).graph;
|
|
546
|
+
}
|
|
541
547
|
},
|
|
542
548
|
async draft() {
|
|
543
|
-
const context = await
|
|
549
|
+
const context = await $context;
|
|
544
550
|
let plan = createDraftPlan(await readChangelogEntries(context.cwd, context.changelogDir), context);
|
|
545
551
|
for (const plugin of context.plugins) plan = await plugin.initPlan?.call(context, plan) ?? plan;
|
|
546
552
|
return plan;
|
|
547
553
|
},
|
|
548
|
-
async graph() {
|
|
549
|
-
return (await createTegamiContext(options)).graph;
|
|
550
|
-
},
|
|
551
554
|
async publish(publishOptions = {}) {
|
|
552
|
-
const context = await
|
|
555
|
+
const context = await $context;
|
|
556
|
+
if ((await readChangelogEntries(context.cwd, context.changelogDir)).length > 0) return {
|
|
557
|
+
state: "skipped",
|
|
558
|
+
planPath: context.planPath
|
|
559
|
+
};
|
|
553
560
|
const parsed = await readFile(context.planPath, "utf8").then((content) => planStoreSchema.decode(content)).catch((error) => {
|
|
554
561
|
if (isNodeError(error) && error.code === "ENOENT") return void 0;
|
|
555
562
|
throw error;
|
|
556
563
|
});
|
|
557
|
-
if (parsed === void 0)
|
|
564
|
+
if (parsed === void 0) return {
|
|
565
|
+
state: "skipped",
|
|
566
|
+
planPath: context.planPath
|
|
567
|
+
};
|
|
558
568
|
let result = await publishFromPlan(context, parsed, publishOptions);
|
|
559
569
|
const publishCtx = {
|
|
560
570
|
...context,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { n as WorkspacePackage, r as isNodeError } from "./workspace-B5_i21S0.mjs";
|
|
2
|
+
import { t as execFailure } from "./exec-hOS852t5.mjs";
|
|
2
3
|
import { readFile, writeFile } from "node:fs/promises";
|
|
3
4
|
import { join, normalize } from "node:path";
|
|
4
5
|
import { x } from "tinyexec";
|
|
@@ -168,13 +169,13 @@ var NpmRegistryClient = class {
|
|
|
168
169
|
return info;
|
|
169
170
|
}
|
|
170
171
|
async publish(pkg, options = {}) {
|
|
172
|
+
const client = await this.resolveClient();
|
|
171
173
|
const args = ["publish"];
|
|
172
174
|
const distTag = options.distTag ?? pkg.distTag;
|
|
173
175
|
if (distTag) args.push("--tag", distTag);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
176
|
+
if (client === "pnpm") args.push("--no-git-checks");
|
|
177
|
+
const result = await x(client, args, { nodeOptions: { cwd: pkg.path } });
|
|
178
|
+
if (result.exitCode !== 0) throw new Error(execFailure(`Failed to publish ${pkg.name}@${pkg.version}${distTag ? ` with dist-tag "${distTag}"` : ""}.`, result));
|
|
178
179
|
}
|
|
179
180
|
async publishPlanStatus(plan) {
|
|
180
181
|
for (const [name, pkgPlan] of Object.entries(plan.packages)) {
|
package/dist/plugins/git.d.mts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { s as TegamiPlugin } from "../context-CC36jtz1.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/plugins/git.d.ts
|
|
4
4
|
interface GitPluginOptions {
|
|
5
5
|
/** Set to false to skip creating git tags after all packages publish successfully. */
|
|
6
6
|
createTags?: boolean;
|
|
7
|
+
/** Push created tags to origin. Defaults to true in CI. */
|
|
8
|
+
pushTags?: boolean;
|
|
7
9
|
}
|
|
8
10
|
/**
|
|
9
11
|
* Basic Git integrations:
|
|
@@ -12,8 +14,5 @@ interface GitPluginOptions {
|
|
|
12
14
|
* Note: you do not need this with `github` plugin enabled.
|
|
13
15
|
*/
|
|
14
16
|
declare function git(options?: GitPluginOptions): TegamiPlugin;
|
|
15
|
-
/** create a Git tag, ignored if already exists */
|
|
16
|
-
declare function createGitTag(cwd: string, tag: string): Promise<void>;
|
|
17
|
-
declare function gitTagExists(cwd: string, tag: string): Promise<boolean>;
|
|
18
17
|
//#endregion
|
|
19
|
-
export { GitPluginOptions,
|
|
18
|
+
export { GitPluginOptions, git };
|
package/dist/plugins/git.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { t as execFailure } from "../exec-hOS852t5.mjs";
|
|
2
|
+
import { t as isCI } from "../constants-B9qjNfvr.mjs";
|
|
1
3
|
import { x } from "tinyexec";
|
|
2
4
|
//#region src/plugins/git.ts
|
|
3
5
|
/**
|
|
@@ -7,42 +9,72 @@ import { x } from "tinyexec";
|
|
|
7
9
|
* Note: you do not need this with `github` plugin enabled.
|
|
8
10
|
*/
|
|
9
11
|
function git(options = {}) {
|
|
10
|
-
const { createTags = true } = options;
|
|
12
|
+
const { createTags = true, pushTags = isCI() } = options;
|
|
11
13
|
return {
|
|
12
14
|
name: "git",
|
|
13
15
|
enforce: "pre",
|
|
16
|
+
cli: { async init() {
|
|
17
|
+
if (!isCI()) return;
|
|
18
|
+
const gitOptions = { nodeOptions: { cwd: this.cwd } };
|
|
19
|
+
for (const args of [[
|
|
20
|
+
"config",
|
|
21
|
+
"user.name",
|
|
22
|
+
"github-actions[bot]"
|
|
23
|
+
], [
|
|
24
|
+
"config",
|
|
25
|
+
"user.email",
|
|
26
|
+
"41898282+github-actions[bot]@users.noreply.github.com"
|
|
27
|
+
]]) {
|
|
28
|
+
const result = await x("git", [...args], gitOptions);
|
|
29
|
+
if (result.exitCode !== 0) throw new Error(execFailure("Failed to configure git user for GitHub Actions.", result));
|
|
30
|
+
}
|
|
31
|
+
} },
|
|
14
32
|
async afterPublish(result) {
|
|
15
|
-
const { graph, publishOptions: { dryRun = false } } = this;
|
|
16
|
-
if (dryRun || !createTags || result.state
|
|
33
|
+
const { cwd, graph, publishOptions: { dryRun = false } } = this;
|
|
34
|
+
if (dryRun || !createTags || result.state !== "created") return result;
|
|
35
|
+
const createdTags = [];
|
|
17
36
|
for (const pkg of result.packages) try {
|
|
18
37
|
const gitTag = `${pkg.name}@${pkg.version}`;
|
|
19
|
-
|
|
38
|
+
const packagePath = graph.get(pkg.id).path;
|
|
39
|
+
if (await createGitTag(packagePath, gitTag)) createdTags.push(gitTag);
|
|
20
40
|
pkg.gitTag = gitTag;
|
|
21
41
|
} catch (error) {
|
|
42
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
22
43
|
return {
|
|
23
44
|
...result,
|
|
24
45
|
state: "failed",
|
|
46
|
+
error: errorMessage,
|
|
25
47
|
packages: result.packages.map((pkgResult) => {
|
|
26
48
|
if (pkgResult.id === pkg.id) return {
|
|
27
49
|
...pkgResult,
|
|
28
50
|
state: "failed",
|
|
29
|
-
error:
|
|
51
|
+
error: errorMessage
|
|
30
52
|
};
|
|
31
53
|
return pkgResult;
|
|
32
54
|
})
|
|
33
55
|
};
|
|
34
56
|
}
|
|
57
|
+
if (pushTags && createdTags.length > 0) try {
|
|
58
|
+
await pushGitTags(cwd, createdTags);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return {
|
|
61
|
+
...result,
|
|
62
|
+
state: "failed",
|
|
63
|
+
error: error instanceof Error ? error.message : String(error)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
35
66
|
return result;
|
|
36
67
|
}
|
|
37
68
|
};
|
|
38
69
|
}
|
|
39
70
|
/** create a Git tag, ignored if already exists */
|
|
40
71
|
async function createGitTag(cwd, tag) {
|
|
41
|
-
if (await gitTagExists(cwd, tag)) return;
|
|
72
|
+
if (await gitTagExists(cwd, tag)) return false;
|
|
42
73
|
await x("git", ["tag", tag], {
|
|
43
74
|
nodeOptions: { cwd },
|
|
44
75
|
throwOnError: true
|
|
45
76
|
});
|
|
77
|
+
return true;
|
|
46
78
|
}
|
|
47
79
|
async function gitTagExists(cwd, tag) {
|
|
48
80
|
return (await x("git", [
|
|
@@ -52,5 +84,16 @@ async function gitTagExists(cwd, tag) {
|
|
|
52
84
|
`refs/tags/${tag}`
|
|
53
85
|
], { nodeOptions: { cwd } })).exitCode === 0;
|
|
54
86
|
}
|
|
87
|
+
async function pushGitTags(cwd, tags) {
|
|
88
|
+
if (tags.length === 0) return;
|
|
89
|
+
await x("git", [
|
|
90
|
+
"push",
|
|
91
|
+
"origin",
|
|
92
|
+
...tags
|
|
93
|
+
], {
|
|
94
|
+
nodeOptions: { cwd },
|
|
95
|
+
throwOnError: true
|
|
96
|
+
});
|
|
97
|
+
}
|
|
55
98
|
//#endregion
|
|
56
|
-
export {
|
|
99
|
+
export { git };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { h as PackagePublishResult, n as Awaitable, s as TegamiPlugin } from "../context-CC36jtz1.mjs";
|
|
2
2
|
import { GitPluginOptions } from "./git.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/plugins/github.d.ts
|
|
@@ -10,12 +10,30 @@ interface GithubRelease {
|
|
|
10
10
|
/** Whether to mark release as prerelease */
|
|
11
11
|
prerelease?: boolean;
|
|
12
12
|
}
|
|
13
|
+
interface VersionPullRequestOptions {
|
|
14
|
+
/** Pull request branch. */
|
|
15
|
+
branch?: string;
|
|
16
|
+
/** Pull request base branch. */
|
|
17
|
+
base?: string;
|
|
18
|
+
/** Pull request title. */
|
|
19
|
+
title?: string;
|
|
20
|
+
/** Pull request body. */
|
|
21
|
+
body?: string;
|
|
22
|
+
}
|
|
13
23
|
/** Options for creating GitHub releases after a successful publish. */
|
|
14
24
|
interface GitHubPluginOptions extends GitPluginOptions {
|
|
15
25
|
/** GitHub repository. */
|
|
16
26
|
repo?: string;
|
|
17
27
|
/** override release details, return `false` to skip */
|
|
18
28
|
onCreateRelease?: (result: PackagePublishResult) => Awaitable<GithubRelease | false>;
|
|
29
|
+
cli?: {
|
|
30
|
+
/**
|
|
31
|
+
* Open a version pull request after versioning.
|
|
32
|
+
* Defaults to enabled in CI and disabled locally.
|
|
33
|
+
* Set to `true` to always create the pull request.
|
|
34
|
+
*/
|
|
35
|
+
createVersionPR?: boolean | VersionPullRequestOptions;
|
|
36
|
+
};
|
|
19
37
|
}
|
|
20
38
|
/** Create GitHub releases for successfully published packages after the whole plan succeeds. */
|
|
21
39
|
declare function github(options?: GitHubPluginOptions): TegamiPlugin[];
|
package/dist/plugins/github.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { t as execFailure } from "../exec-hOS852t5.mjs";
|
|
2
|
+
import { a as previousVersion, r as formatVersionBump } from "../semver-FSWx3_34.mjs";
|
|
3
|
+
import { t as isCI } from "../constants-B9qjNfvr.mjs";
|
|
1
4
|
import { git } from "./git.mjs";
|
|
2
5
|
import { x } from "tinyexec";
|
|
3
6
|
//#region src/plugins/github.ts
|
|
@@ -18,16 +21,118 @@ function github(options = {}) {
|
|
|
18
21
|
];
|
|
19
22
|
if (options.repo) args.push("--repo", options.repo);
|
|
20
23
|
if (release.prerelease) args.push("--prerelease");
|
|
21
|
-
await x("gh", args
|
|
24
|
+
const result = await x("gh", args);
|
|
25
|
+
if (result.exitCode !== 0) throw new Error(execFailure(`Failed to create GitHub release for ${pkg.name}@${pkg.version}.`, result));
|
|
26
|
+
}
|
|
27
|
+
function resolvePROptions() {
|
|
28
|
+
const setting = options.cli?.createVersionPR ?? isCI();
|
|
29
|
+
if (setting === false) return [false];
|
|
30
|
+
return [true, typeof setting === "object" ? setting : {}];
|
|
22
31
|
}
|
|
23
32
|
return [git(options), {
|
|
24
33
|
name: "github",
|
|
34
|
+
cli: {
|
|
35
|
+
async init() {
|
|
36
|
+
if (!isCI()) return;
|
|
37
|
+
const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
38
|
+
const repository = options.repo ?? process.env.GITHUB_REPOSITORY;
|
|
39
|
+
if (!token || !repository) return;
|
|
40
|
+
const result = await x("git", [
|
|
41
|
+
"remote",
|
|
42
|
+
"set-url",
|
|
43
|
+
"origin",
|
|
44
|
+
`https://x-access-token:${token}@github.com/${repository}.git`
|
|
45
|
+
], { nodeOptions: { cwd: this.cwd } });
|
|
46
|
+
if (result.exitCode !== 0) throw new Error(execFailure("Failed to configure git remote for GitHub Actions.", result));
|
|
47
|
+
},
|
|
48
|
+
async afterVersion(draft) {
|
|
49
|
+
const { cwd } = this;
|
|
50
|
+
const [enabled, config] = resolvePROptions();
|
|
51
|
+
if (!enabled || !await hasGitChanges(cwd)) return;
|
|
52
|
+
const { branch = "tegami/version-packages", base = "main", title = "Version Packages", body = defaultVersionPRBody(draft, this) } = config;
|
|
53
|
+
const gitOptions = { nodeOptions: { cwd } };
|
|
54
|
+
let result = await x("git", [
|
|
55
|
+
"checkout",
|
|
56
|
+
"-B",
|
|
57
|
+
branch
|
|
58
|
+
], gitOptions);
|
|
59
|
+
if (result.exitCode !== 0) throw new Error(execFailure("Failed to create the version pull request branch.", result));
|
|
60
|
+
result = await x("git", ["add", "-A"], gitOptions);
|
|
61
|
+
if (result.exitCode !== 0) throw new Error(execFailure("Failed to stage version changes.", result));
|
|
62
|
+
result = await x("git", [
|
|
63
|
+
"commit",
|
|
64
|
+
"-m",
|
|
65
|
+
title
|
|
66
|
+
], gitOptions);
|
|
67
|
+
if (result.exitCode !== 0) throw new Error(execFailure("Failed to commit version changes.", result));
|
|
68
|
+
result = await x("git", [
|
|
69
|
+
"push",
|
|
70
|
+
"--force",
|
|
71
|
+
"-u",
|
|
72
|
+
"origin",
|
|
73
|
+
branch
|
|
74
|
+
], gitOptions);
|
|
75
|
+
if (result.exitCode !== 0) throw new Error(execFailure("Failed to push the version branch to origin. Ensure `origin` is configured and you have push access.", result));
|
|
76
|
+
if (await hasOpenPullRequest(branch, options.repo)) return;
|
|
77
|
+
const args = [
|
|
78
|
+
"pr",
|
|
79
|
+
"create",
|
|
80
|
+
"--title",
|
|
81
|
+
title,
|
|
82
|
+
"--body",
|
|
83
|
+
body,
|
|
84
|
+
"--head",
|
|
85
|
+
branch,
|
|
86
|
+
"--base",
|
|
87
|
+
base
|
|
88
|
+
];
|
|
89
|
+
if (options.repo) args.push("--repo", options.repo);
|
|
90
|
+
const prResult = await x("gh", args);
|
|
91
|
+
if (prResult.exitCode !== 0) throw new Error(execFailure("Failed to create the version pull request.", prResult));
|
|
92
|
+
}
|
|
93
|
+
},
|
|
25
94
|
async afterPublish(result) {
|
|
26
|
-
if (result.state !== "
|
|
95
|
+
if (result.state !== "created") return;
|
|
27
96
|
await Promise.all(result.packages.map(createGithubRelease));
|
|
28
97
|
}
|
|
29
98
|
}];
|
|
30
99
|
}
|
|
100
|
+
async function hasGitChanges(cwd) {
|
|
101
|
+
return (await x("git", ["status", "--porcelain"], { nodeOptions: { cwd } })).stdout.trim().length > 0;
|
|
102
|
+
}
|
|
103
|
+
async function hasOpenPullRequest(branch, repo) {
|
|
104
|
+
const args = [
|
|
105
|
+
"pr",
|
|
106
|
+
"list",
|
|
107
|
+
"--head",
|
|
108
|
+
branch,
|
|
109
|
+
"--state",
|
|
110
|
+
"open",
|
|
111
|
+
"--json",
|
|
112
|
+
"number"
|
|
113
|
+
];
|
|
114
|
+
if (repo) args.push("--repo", repo);
|
|
115
|
+
const result = await x("gh", args);
|
|
116
|
+
if (result.exitCode !== 0) throw new Error(execFailure("Failed to check for an existing version pull request.", result));
|
|
117
|
+
return result.stdout.trim() !== "[]";
|
|
118
|
+
}
|
|
119
|
+
function defaultVersionPRBody(draft, context) {
|
|
120
|
+
const packageLines = [];
|
|
121
|
+
for (const id of draft.getPackageIds()) {
|
|
122
|
+
const packagePlan = draft.getPackage(id);
|
|
123
|
+
if (!packagePlan) continue;
|
|
124
|
+
const pkg = context.graph.get(id);
|
|
125
|
+
if (!pkg) continue;
|
|
126
|
+
const publish = packagePlan.publish ? "" : " (no publish)";
|
|
127
|
+
const previous = previousVersion(pkg.version, packagePlan.type);
|
|
128
|
+
packageLines.push(`- ${formatVersionBump(pkg.name, previous, pkg.version, packagePlan.distTag)}${publish}`);
|
|
129
|
+
}
|
|
130
|
+
const changelogLines = draft.getChangelogIds().map((id) => draft.getChangelog(id)).filter((entry) => entry !== void 0).map((entry) => `- ${entry.title}`);
|
|
131
|
+
const sections = ["## Summary", ...packageLines];
|
|
132
|
+
if (changelogLines.length > 0) sections.push("", "## Changelogs", ...changelogLines);
|
|
133
|
+
sections.push("", "Merge this PR to publish the versioned packages.");
|
|
134
|
+
return sections.join("\n");
|
|
135
|
+
}
|
|
31
136
|
function defaultNotes(pkg) {
|
|
32
137
|
const entries = pkg.changelogs;
|
|
33
138
|
if (entries.length > 0) return entries.map((entry) => [`### ${entry.title}`, entry.content].filter(Boolean).join("\n\n")).join("\n\n");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as RegistryClient, i as PublishPlanStatus, m as WorkspacePackage, p as PackageGraph, s as TegamiPlugin, t as TegamiContext, v as PlanStore } from "../context-CC36jtz1.mjs";
|
|
2
2
|
import { TomlTable } from "smol-toml";
|
|
3
3
|
|
|
4
4
|
//#region src/providers/cargo.d.ts
|
package/dist/providers/cargo.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { join, normalize } from "node:path";
|
|
4
4
|
import { x } from "tinyexec";
|
|
5
5
|
import * as semver from "semver";
|
|
6
|
-
import { parse, stringify } from "smol-toml";
|
|
6
|
+
import { parse as parse$1, stringify } from "smol-toml";
|
|
7
7
|
import { glob } from "tinyglobby";
|
|
8
8
|
//#region src/providers/cargo.ts
|
|
9
9
|
const DEP_FIELDS = [
|
|
@@ -173,7 +173,7 @@ function dependencyTables(manifest) {
|
|
|
173
173
|
return tables;
|
|
174
174
|
}
|
|
175
175
|
async function readCargoManifest(path) {
|
|
176
|
-
return parse(await readFile(join(path, "Cargo.toml"), "utf8"));
|
|
176
|
+
return parse$1(await readFile(join(path, "Cargo.toml"), "utf8"));
|
|
177
177
|
}
|
|
178
178
|
function tableValue(value) {
|
|
179
179
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
package/dist/providers/npm.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as NpmRegistryClient, f as npm, l as NpmClient, u as NpmPackage } from "../context-CC36jtz1.mjs";
|
|
2
2
|
export { NpmClient, NpmPackage, NpmRegistryClient, npm };
|
package/dist/providers/npm.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as NpmRegistryClient, r as npm, t as NpmPackage } from "../npm-
|
|
1
|
+
import { n as NpmRegistryClient, r as npm, t as NpmPackage } from "../npm-BpFlXy8I.mjs";
|
|
2
2
|
export { NpmPackage, NpmRegistryClient, npm };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { inc, parse } from "semver";
|
|
2
|
+
//#region src/utils/semver.ts
|
|
3
|
+
function formatPackageVersion(name, version, distTag) {
|
|
4
|
+
return `${name}@${version}${distTag ? ` (${distTag})` : ""}`;
|
|
5
|
+
}
|
|
6
|
+
function previousVersion(version, type) {
|
|
7
|
+
const parsed = parse(version);
|
|
8
|
+
if (!parsed) throw new Error(`Invalid semver version: ${version}`);
|
|
9
|
+
if (type === "major") {
|
|
10
|
+
parsed.major = Math.max(0, parsed.major - 1);
|
|
11
|
+
parsed.minor = 0;
|
|
12
|
+
parsed.patch = 0;
|
|
13
|
+
} else if (type === "minor") {
|
|
14
|
+
parsed.minor = Math.max(0, parsed.minor - 1);
|
|
15
|
+
parsed.patch = 0;
|
|
16
|
+
} else parsed.patch = Math.max(0, parsed.patch - 1);
|
|
17
|
+
parsed.prerelease = [];
|
|
18
|
+
parsed.build = [];
|
|
19
|
+
return parsed.format();
|
|
20
|
+
}
|
|
21
|
+
function formatVersionBump(name, from, to, distTag) {
|
|
22
|
+
return `${name}@${from} → ${name}@${to}${distTag ? ` (${distTag})` : ""}`;
|
|
23
|
+
}
|
|
24
|
+
function maxBump(a, b) {
|
|
25
|
+
if (a === "major" || b === "major") return "major";
|
|
26
|
+
if (a === "minor" || b === "minor") return "minor";
|
|
27
|
+
return "patch";
|
|
28
|
+
}
|
|
29
|
+
function bumpVersion(version, type) {
|
|
30
|
+
const next = inc(version, type);
|
|
31
|
+
if (!next) throw new Error(`Invalid semver version: ${version}`);
|
|
32
|
+
return next;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { previousVersion as a, maxBump as i, formatPackageVersion as n, formatVersionBump as r, bumpVersion as t };
|
package/package.json
CHANGED
|
@@ -1,32 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tegami",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Utility for package versioning & publish",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Fuma Nama",
|
|
7
|
-
"repository": "github:fuma-nama/tegami",
|
|
8
|
-
"files": [
|
|
9
|
-
"dist"
|
|
10
|
-
],
|
|
11
|
-
"type": "module",
|
|
12
|
-
"exports": {
|
|
13
|
-
".": "./dist/index.mjs",
|
|
14
|
-
"./generators/simple": "./dist/generators/simple.mjs",
|
|
15
|
-
"./plugins/git": "./dist/plugins/git.mjs",
|
|
16
|
-
"./plugins/github": "./dist/plugins/github.mjs",
|
|
17
|
-
"./providers/cargo": "./dist/providers/cargo.mjs",
|
|
18
|
-
"./providers/npm": "./dist/providers/npm.mjs",
|
|
19
|
-
"./package.json": "./package.json"
|
|
20
|
-
},
|
|
3
|
+
"version": "0.0.1",
|
|
21
4
|
"publishConfig": {
|
|
22
5
|
"access": "public"
|
|
23
6
|
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"types:check": "tsc --noEmit",
|
|
26
|
-
"build": "tsdown",
|
|
27
|
-
"dev": "tsdown --watch"
|
|
28
|
-
},
|
|
29
7
|
"dependencies": {
|
|
8
|
+
"@clack/prompts": "^1.5.1",
|
|
9
|
+
"commander": "^15.0.0",
|
|
30
10
|
"js-yaml": "^4.2.0",
|
|
31
11
|
"mdast-util-from-markdown": "^2.0.3",
|
|
32
12
|
"mdast-util-to-markdown": "^2.1.2",
|
|
@@ -38,12 +18,38 @@
|
|
|
38
18
|
"zod": "^4.4.3"
|
|
39
19
|
},
|
|
40
20
|
"devDependencies": {
|
|
41
|
-
"@repo/typescript-config": "workspace:*",
|
|
42
21
|
"@types/js-yaml": "^4.0.9",
|
|
43
22
|
"@types/mdast": "^4.0.4",
|
|
44
23
|
"@types/node": "^25.5.0",
|
|
45
24
|
"@types/semver": "^7.7.1",
|
|
46
25
|
"tsdown": "^0.22.2",
|
|
47
|
-
"typescript": "6.0.3"
|
|
26
|
+
"typescript": "6.0.3",
|
|
27
|
+
"@repo/typescript-config": "0.0.0"
|
|
28
|
+
},
|
|
29
|
+
"description": "Utility for package versioning & publish",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"author": "Fuma Nama",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "github:fuma-nama/tegami"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"type": "module",
|
|
40
|
+
"exports": {
|
|
41
|
+
".": "./dist/index.mjs",
|
|
42
|
+
"./cli": "./dist/cli/index.mjs",
|
|
43
|
+
"./generators/simple": "./dist/generators/simple.mjs",
|
|
44
|
+
"./plugins/git": "./dist/plugins/git.mjs",
|
|
45
|
+
"./plugins/github": "./dist/plugins/github.mjs",
|
|
46
|
+
"./providers/cargo": "./dist/providers/cargo.mjs",
|
|
47
|
+
"./providers/npm": "./dist/providers/npm.mjs",
|
|
48
|
+
"./package.json": "./package.json"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"types:check": "tsc --noEmit",
|
|
52
|
+
"build": "tsdown",
|
|
53
|
+
"dev": "tsdown --watch"
|
|
48
54
|
}
|
|
49
|
-
}
|
|
55
|
+
}
|