tegami 0.0.1 → 0.1.0-beta.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.
@@ -0,0 +1,187 @@
1
+ import { t as execFailure } from "../error-DBK-9uBa.js";
2
+ import { n as formatNpmDistTag, r as formatPackageVersion } from "../semver-mWK2Khi2.js";
3
+ import { t as isCI } from "../constants-B9qjNfvr.js";
4
+ import { git } from "./git.js";
5
+ import { x } from "tinyexec";
6
+ import { prerelease } from "semver";
7
+ //#region src/plugins/github.ts
8
+ /** Create GitHub releases for successfully published packages after the whole plan succeeds. */
9
+ function github(options = {}) {
10
+ async function runGithubRelease(gitTag, title, notes, prerelease) {
11
+ const args = [
12
+ "release",
13
+ "create",
14
+ gitTag,
15
+ "--title",
16
+ title,
17
+ "--notes",
18
+ notes
19
+ ];
20
+ if (options.repo) args.push("--repo", options.repo);
21
+ if (prerelease) args.push("--prerelease");
22
+ const result = await x("gh", args);
23
+ if (result.exitCode !== 0) throw execFailure(`Failed to create GitHub release for ${gitTag}.`, result);
24
+ }
25
+ async function createRelease(pkg) {
26
+ if (!pkg.gitTag) return;
27
+ const release = await options.onCreateRelease?.(pkg) ?? {};
28
+ if (release === false) return;
29
+ await runGithubRelease(pkg.gitTag, release.title ?? defaultTitle(pkg), release.notes ?? defaultNotes(pkg), release.prerelease ?? prerelease(pkg.version) !== null);
30
+ }
31
+ async function createGroupedRelease(packages) {
32
+ const primary = packages[0];
33
+ if (!primary?.gitTag) return;
34
+ const release = await options.onCreateGroupedRelease?.(packages) ?? {};
35
+ if (release === false) return;
36
+ await runGithubRelease(primary.gitTag, release.title ?? defaultGroupedTitle(packages), release.notes ?? defaultGroupedNotes(packages), release.prerelease ?? packages.some((pkg) => prerelease(pkg.version) !== null));
37
+ }
38
+ function resolvePROptions() {
39
+ const setting = options.cli?.versionPr ?? isCI();
40
+ if (setting === false) return [false];
41
+ return [true, typeof setting === "object" ? setting : {}];
42
+ }
43
+ function defaultVersionPRBody(draft, context) {
44
+ const packageLines = [];
45
+ for (const pkg of context.graph.getPackages()) {
46
+ const packagePlan = draft.getPackagePlan(pkg.id);
47
+ if (!packagePlan?.type) continue;
48
+ const publishTxt = packagePlan.publish ? "" : " (no publish)";
49
+ const distTagTxt = formatNpmDistTag(packagePlan.npm?.distTag);
50
+ packageLines.push(`- ${pkg.name}@${cliOriginalPackageVersions.get(pkg.id) ?? pkg.version} → ${pkg.name}@${pkg.version}${distTagTxt}${publishTxt}`);
51
+ }
52
+ const changelogLines = draft.getChangelogs().map((entry) => `- ${entry.title}`);
53
+ const sections = ["## Summary", ...packageLines];
54
+ if (changelogLines.length > 0) sections.push("", "## Changelogs", ...changelogLines);
55
+ sections.push("", "Merge this PR to publish the versioned packages.");
56
+ return sections.join("\n");
57
+ }
58
+ const cliOriginalPackageVersions = /* @__PURE__ */ new Map();
59
+ return [git(options), {
60
+ name: "github",
61
+ cli: {
62
+ async init() {
63
+ if (!isCI()) return;
64
+ const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
65
+ const repository = options.repo ?? process.env.GITHUB_REPOSITORY;
66
+ if (!token || !repository) return;
67
+ const result = await x("git", [
68
+ "remote",
69
+ "set-url",
70
+ "origin",
71
+ `https://x-access-token:${token}@github.com/${repository}.git`
72
+ ], { nodeOptions: { cwd: this.cwd } });
73
+ if (result.exitCode !== 0) throw execFailure("Failed to configure git remote for GitHub Actions.", result);
74
+ },
75
+ publishPlanCreated() {
76
+ for (const pkg of this.graph.getPackages()) cliOriginalPackageVersions.set(pkg.id, pkg.version);
77
+ },
78
+ async publishPlanApplied(draft) {
79
+ const { cwd } = this;
80
+ const [enabled, config] = resolvePROptions();
81
+ if (!enabled || !await hasGitChanges(cwd)) return;
82
+ const { branch = "tegami/version-packages", base = "main" } = config;
83
+ const basePR = await options.onCreateVersionPullRequest?.(draft);
84
+ if (basePR === false) return;
85
+ const pr = {
86
+ title: basePR?.title ?? "Version Packages",
87
+ body: basePR?.body ?? defaultVersionPRBody(draft, this)
88
+ };
89
+ const gitOptions = { nodeOptions: { cwd } };
90
+ let result = await x("git", [
91
+ "checkout",
92
+ "-B",
93
+ branch
94
+ ], gitOptions);
95
+ if (result.exitCode !== 0) throw execFailure("Failed to create the version pull request branch.", result);
96
+ result = await x("git", ["add", "-A"], gitOptions);
97
+ if (result.exitCode !== 0) throw execFailure("Failed to stage version changes.", result);
98
+ result = await x("git", [
99
+ "commit",
100
+ "-m",
101
+ pr.title
102
+ ], gitOptions);
103
+ if (result.exitCode !== 0) throw execFailure("Failed to commit version changes.", result);
104
+ result = await x("git", [
105
+ "push",
106
+ "--force",
107
+ "-u",
108
+ "origin",
109
+ branch
110
+ ], gitOptions);
111
+ if (result.exitCode !== 0) throw execFailure("Failed to push the version branch to origin. Ensure `origin` is configured and you have push access.", result);
112
+ if (await hasOpenPullRequest(branch, options.repo)) return;
113
+ const args = [
114
+ "pr",
115
+ "create",
116
+ "--title",
117
+ pr.title,
118
+ "--body",
119
+ pr.body,
120
+ "--head",
121
+ branch,
122
+ "--base",
123
+ base
124
+ ];
125
+ if (options.repo) args.push("--repo", options.repo);
126
+ const prResult = await x("gh", args);
127
+ if (prResult.exitCode !== 0) throw execFailure("Failed to create the version pull request.", prResult);
128
+ }
129
+ },
130
+ async afterPublish(result) {
131
+ if (result.state !== "created") return;
132
+ for (const packages of groupPackagesByGitTag(result.packages).values()) if (packages.length > 1) await createGroupedRelease(packages);
133
+ else await createRelease(packages[0]);
134
+ }
135
+ }];
136
+ }
137
+ async function hasGitChanges(cwd) {
138
+ return (await x("git", ["status", "--porcelain"], { nodeOptions: { cwd } })).stdout.trim().length > 0;
139
+ }
140
+ async function hasOpenPullRequest(branch, repo) {
141
+ const args = [
142
+ "pr",
143
+ "list",
144
+ "--head",
145
+ branch,
146
+ "--state",
147
+ "open",
148
+ "--json",
149
+ "number"
150
+ ];
151
+ if (repo) args.push("--repo", repo);
152
+ const result = await x("gh", args);
153
+ if (result.exitCode !== 0) throw execFailure("Failed to check for an existing version pull request.", result);
154
+ return result.stdout.trim() !== "[]";
155
+ }
156
+ function groupPackagesByGitTag(packages) {
157
+ const groups = /* @__PURE__ */ new Map();
158
+ for (const pkg of packages) {
159
+ if (!pkg.gitTag) continue;
160
+ const group = groups.get(pkg.gitTag);
161
+ if (group) group.push(pkg);
162
+ else groups.set(pkg.gitTag, [pkg]);
163
+ }
164
+ return groups;
165
+ }
166
+ function defaultTitle(pkg) {
167
+ return formatPackageVersion(pkg.name, pkg.version, pkg.npm?.distTag);
168
+ }
169
+ function defaultGroupedTitle(packages) {
170
+ const primary = packages[0];
171
+ const distTag = packages.every((pkg) => pkg.npm?.distTag === primary.npm?.distTag) ? primary.npm?.distTag : void 0;
172
+ return formatPackageVersion(primary.gitTag.slice(0, primary.gitTag.lastIndexOf("@")), primary.version, distTag);
173
+ }
174
+ function defaultNotes(pkg) {
175
+ if (pkg.changelogs.length > 0) return pkg.changelogs.map((entry) => [`### ${entry.title}`, entry.content].filter(Boolean).join("\n\n")).join("\n\n");
176
+ return `Published ${formatPackageVersion(pkg.name, pkg.version, pkg.npm?.distTag)}.`;
177
+ }
178
+ function defaultGroupedNotes(packages) {
179
+ const changelogs = /* @__PURE__ */ new Map();
180
+ for (const pkg of packages) for (const entry of pkg.changelogs) changelogs.set(entry.id, entry);
181
+ const sections = [packages.map((pkg) => `- ${formatPackageVersion(pkg.name, pkg.version, pkg.npm?.distTag)}`).join("\n")];
182
+ if (changelogs.size > 0) sections.push("", Array.from(changelogs.values()).map((entry) => [`### ${entry.title}`, entry.content].filter(Boolean).join("\n\n")).join("\n\n"));
183
+ else sections.push("", `Published ${packages[0].gitTag}.`);
184
+ return sections.join("\n");
185
+ }
186
+ //#endregion
187
+ export { github };
@@ -0,0 +1,2 @@
1
+ import { g as cargo, h as CargoRegistryClient, m as CargoPluginOptions, p as CargoPackage } from "../index-Da6P6gSc.js";
2
+ export { CargoPackage, CargoPluginOptions, CargoRegistryClient, cargo };
@@ -1,4 +1,5 @@
1
- import { n as WorkspacePackage, r as isNodeError } from "../workspace-B5_i21S0.mjs";
1
+ import { r as isNodeError } from "../error-DBK-9uBa.js";
2
+ import { n as WorkspacePackage } from "../graph-CUgwuRW5.js";
2
3
  import { readFile, writeFile } from "node:fs/promises";
3
4
  import { join, normalize } from "node:path";
4
5
  import { x } from "tinyexec";
@@ -11,7 +12,7 @@ const DEP_FIELDS = [
11
12
  "dev-dependencies",
12
13
  "build-dependencies"
13
14
  ];
14
- var CargoPackage = class CargoPackage extends WorkspacePackage {
15
+ var CargoPackage = class extends WorkspacePackage {
15
16
  path;
16
17
  manifest;
17
18
  workspaceManifest;
@@ -23,69 +24,42 @@ var CargoPackage = class CargoPackage extends WorkspacePackage {
23
24
  this.workspaceManifest = workspaceManifest;
24
25
  }
25
26
  get name() {
26
- return stringValue(this.packageInfo.name);
27
+ return this.packageInfo.name;
27
28
  }
28
29
  get version() {
29
30
  return stringValue(this.packageInfo.version) ?? this.workspaceVersion ?? "0.0.0";
30
31
  }
31
- get publish() {
32
- return this.packageInfo.publish !== false;
33
- }
34
- setVersion(version) {
35
- this.packageInfo.version = version;
36
- }
37
- async updateDependency(target, version, context) {
38
- if (!(target instanceof CargoPackage)) return;
39
- const next = semver.parse(version);
40
- if (!next) return;
41
- for (const table of dependencyTables(this.manifest)) for (const [rawName, rawSpec] of Object.entries(table)) {
42
- const spec = tableValue(rawSpec);
43
- const packageName = stringValue(spec?.package) ?? rawName;
44
- if (packageName !== target.name) continue;
45
- if (typeof rawSpec === "string") {
46
- table[rawName] = (await this.updateRange(context, {
47
- name: packageName,
48
- range: rawSpec
49
- }, next)).range;
50
- continue;
51
- }
52
- if (spec) {
53
- const current = stringValue(spec.version);
54
- spec.version = current ? (await this.updateRange(context, {
55
- name: packageName,
56
- range: current
57
- }, next)).range : next.format();
58
- }
59
- }
32
+ onPlan(context) {
33
+ const defaults = super.onPlan(context);
34
+ defaults.publish ??= this.packageInfo.publish !== false;
35
+ return defaults;
60
36
  }
61
37
  async write() {
62
38
  await writeFile(join(this.path, "Cargo.toml"), stringify(this.manifest));
63
39
  }
64
40
  get packageInfo() {
65
- return tableValue(this.manifest.package) ?? {};
41
+ this.manifest.package ??= {};
42
+ return this.manifest.package;
66
43
  }
67
44
  get workspaceVersion() {
68
45
  return stringValue(tableValue(tableValue(this.workspaceManifest?.workspace)?.package)?.version);
69
46
  }
70
47
  };
71
48
  var CargoRegistryClient = class {
72
- graph;
73
49
  id = "cargo";
74
50
  #versionMap = /* @__PURE__ */ new Map();
75
- constructor(graph) {
76
- this.graph = graph;
77
- }
51
+ constructor(_graph) {}
78
52
  supports(pkg) {
79
53
  return pkg instanceof CargoPackage;
80
54
  }
81
- async packageVersionExists(pkg, version) {
82
- const cacheKey = `${pkg.id}@${version}`;
55
+ async isPackagePublished(pkg) {
56
+ const cacheKey = `${pkg.id}@${pkg.version}`;
83
57
  let info = this.#versionMap.get(cacheKey);
84
58
  if (!info) {
85
- info = fetch(`https://crates.io/api/v1/crates/${encodeURIComponent(pkg.name)}/${version}`).then(async (response) => {
59
+ info = fetch(`https://crates.io/api/v1/crates/${encodeURIComponent(pkg.name)}/${pkg.version}`).then(async (response) => {
86
60
  if (response.status === 200) return true;
87
61
  if (response.status === 404) return false;
88
- throw new Error(`Unable to validate ${pkg.name}@${version} against crates.io: ${await response.text()}`);
62
+ throw new Error(`Unable to validate ${pkg.name}@${pkg.version} against crates.io: ${await response.text()}`);
89
63
  });
90
64
  this.#versionMap.set(cacheKey, info);
91
65
  }
@@ -97,16 +71,46 @@ var CargoRegistryClient = class {
97
71
  throwOnError: true
98
72
  });
99
73
  }
100
- async publishPlanStatus(plan) {
101
- for (const [id, pkgPlan] of Object.entries(plan.packages)) {
102
- const pkg = this.graph.get(id);
103
- if (!(pkg instanceof CargoPackage) || !pkgPlan.publish) continue;
104
- if (!await this.packageVersionExists(pkg, pkg.version)) return { state: "pending" };
105
- }
106
- return { state: "success" };
107
- }
108
74
  };
109
- function cargo() {
75
+ function cargo({ bumpDep: getBumpDepType = ({ kind }) => {
76
+ switch (kind) {
77
+ case "dependencies": return "patch";
78
+ case "build-dependencies":
79
+ case "dev-dependencies": return false;
80
+ }
81
+ } } = {}) {
82
+ function updateRange(range, next) {
83
+ if (!semver.validRange(range)) return false;
84
+ if (new semver.Range(range).test(next)) return false;
85
+ return next;
86
+ }
87
+ function depsPolicy({ graph }) {
88
+ return {
89
+ id: "cargo:deps",
90
+ onUpdate({ pkg, plan }) {
91
+ for (const other of graph.getPackages()) {
92
+ if (!(other instanceof CargoPackage)) continue;
93
+ for (const { table, kind } of dependencyTables(other.manifest)) for (const [rawName, rawSpec] of Object.entries(table)) {
94
+ const spec = parseSpec(rawSpec);
95
+ if (!spec) continue;
96
+ const packageName = spec.package ?? rawName;
97
+ if (pkg.id !== `cargo:${packageName}`) continue;
98
+ if (updateRange(spec.version, plan.bumpVersion(pkg)) === false) continue;
99
+ const bumpType = getBumpDepType({
100
+ kind,
101
+ name: packageName,
102
+ version: spec.version
103
+ });
104
+ if (bumpType === false) continue;
105
+ this.bumpPackage(other, {
106
+ type: bumpType,
107
+ reason: `update dependency "${rawName}"`
108
+ });
109
+ }
110
+ }
111
+ }
112
+ };
113
+ }
110
114
  return {
111
115
  name: "cargo",
112
116
  enforce: "pre",
@@ -115,6 +119,33 @@ function cargo() {
115
119
  },
116
120
  createRegistryClient() {
117
121
  return new CargoRegistryClient(this.graph);
122
+ },
123
+ initPlan(plan) {
124
+ plan.addPolicy(depsPolicy(this));
125
+ },
126
+ async applyPlan(draft) {
127
+ const { graph } = this;
128
+ const writes = [];
129
+ for (const pkg of graph.getPackages()) {
130
+ if (!(pkg instanceof CargoPackage)) continue;
131
+ const plan = draft.getPackagePlan(pkg.id);
132
+ if (plan) pkg.packageInfo.version = plan.bumpVersion(pkg);
133
+ }
134
+ for (const pkg of graph.getPackages()) {
135
+ if (!(pkg instanceof CargoPackage)) continue;
136
+ for (const { table } of dependencyTables(pkg.manifest)) for (const [rawName, rawSpec] of Object.entries(table)) {
137
+ const spec = parseSpec(rawSpec);
138
+ if (!spec) continue;
139
+ const packageName = spec.package ?? rawName;
140
+ const linked = graph.get(`cargo:${packageName}`);
141
+ if (!linked || !(linked instanceof CargoPackage)) continue;
142
+ const result = updateRange(spec.version, linked.version);
143
+ if (result === false) continue;
144
+ table[rawName] = spec.setVersion(result);
145
+ }
146
+ writes.push(pkg.write());
147
+ }
148
+ await Promise.all(writes);
118
149
  }
119
150
  };
120
151
  }
@@ -159,7 +190,10 @@ function dependencyTables(manifest) {
159
190
  const tables = [];
160
191
  for (const field of DEP_FIELDS) {
161
192
  const table = tableValue(manifest[field]);
162
- if (table) tables.push(table);
193
+ if (table) tables.push({
194
+ kind: field,
195
+ table
196
+ });
163
197
  }
164
198
  const target = tableValue(manifest.target);
165
199
  if (target) for (const targetConfig of Object.values(target)) {
@@ -167,14 +201,38 @@ function dependencyTables(manifest) {
167
201
  if (!targetTable) continue;
168
202
  for (const field of DEP_FIELDS) {
169
203
  const table = tableValue(targetTable[field]);
170
- if (table) tables.push(table);
204
+ if (table) tables.push({
205
+ kind: field,
206
+ table
207
+ });
171
208
  }
172
209
  }
173
210
  return tables;
174
211
  }
212
+ function parseSpec(v) {
213
+ if (typeof v === "string") return {
214
+ version: v,
215
+ setVersion(version) {
216
+ return version;
217
+ }
218
+ };
219
+ if (isTableValue(v)) return {
220
+ package: stringValue(v.package),
221
+ version: v.version,
222
+ setVersion(version) {
223
+ return {
224
+ ...v,
225
+ version
226
+ };
227
+ }
228
+ };
229
+ }
175
230
  async function readCargoManifest(path) {
176
231
  return parse$1(await readFile(join(path, "Cargo.toml"), "utf8"));
177
232
  }
233
+ function isTableValue(value) {
234
+ return value !== null && typeof value === "object" && !Array.isArray(value);
235
+ }
178
236
  function tableValue(value) {
179
237
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
180
238
  return value;
@@ -0,0 +1,2 @@
1
+ import { _ as NpmPackage, b as npm, v as NpmPluginOptions, y as NpmRegistryClient } from "../index-Da6P6gSc.js";
2
+ export { NpmPackage, NpmPluginOptions, NpmRegistryClient, npm };