versionary 0.27.0 → 0.28.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.
@@ -1,29 +1,27 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.extractReleaseNotes = extractReleaseNotes;
7
- exports.extractClosingReferencesFromNotes = extractClosingReferencesFromNotes;
8
- exports.resolveTargetChangelogFile = resolveTargetChangelogFile;
9
- exports.runRelease = runRelease;
10
- exports.runReleaseDetailed = runReleaseDetailed;
11
- exports.runSimpleRelease = runSimpleRelease;
12
- exports.runSimpleReleaseDetailed = runSimpleReleaseDetailed;
13
- const node_child_process_1 = require("node:child_process");
14
- const node_fs_1 = __importDefault(require("node:fs"));
15
- const node_path_1 = __importDefault(require("node:path"));
16
- const load_config_js_1 = require("../config/load-config.js");
17
- const client_js_1 = require("../scm/client.js");
18
- const resolve_js_1 = require("../strategy/resolve.js");
19
- const plan_js_1 = require("./plan.js");
20
- const pr_js_1 = require("./pr.js");
21
- const recovery_js_1 = require("./recovery.js");
22
- const state_js_1 = require("./state.js");
1
+ import { execFileSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { loadConfig } from "../config/load-config.js";
5
+ import { getScmClient } from "../scm/client.js";
6
+ import { resolvePackageStrategyContext, resolveReleaseName, } from "../strategy/package-context.js";
7
+ import { resolveVersionStrategy } from "../strategy/resolve.js";
8
+ import { getChangelogDefaults } from "./plan.js";
9
+ import { isReleaseCommitMessage } from "./pr.js";
10
+ import { executeIdempotentReleaseTarget } from "./recovery.js";
11
+ import { readReleaseTargets } from "./state.js";
23
12
  function escapeRegExp(input) {
24
13
  return input.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
25
14
  }
26
- function extractReleaseNotes(content, version, changelogFormat) {
15
+ export function resolveTargetPackageName(cwd, config, targetPath) {
16
+ const packageConfig = config.packages?.[targetPath] ?? {};
17
+ const { strategy, config: strategyConfig } = resolvePackageStrategyContext(config, targetPath, packageConfig);
18
+ const resolved = resolveReleaseName(cwd, targetPath, packageConfig, strategy, strategyConfig);
19
+ if (!resolved || resolved === targetPath) {
20
+ return undefined;
21
+ }
22
+ return resolved;
23
+ }
24
+ export function extractReleaseNotes(content, version, changelogFormat) {
27
25
  const lines = content.split("\n");
28
26
  const shortVersion = version.replace(/\.\d+$/u, "");
29
27
  const start = changelogFormat === "r-news"
@@ -61,22 +59,22 @@ function extractReleaseNotes(content, version, changelogFormat) {
61
59
  .trim();
62
60
  }
63
61
  function getHeadCommitMessage(cwd) {
64
- return (0, node_child_process_1.execFileSync)("git", ["log", "-1", "--pretty=%B"], {
62
+ return execFileSync("git", ["log", "-1", "--pretty=%B"], {
65
63
  cwd,
66
64
  encoding: "utf8",
67
65
  stdio: ["ignore", "pipe", "ignore"],
68
66
  }).trim();
69
67
  }
70
68
  function readReleaseNotes(cwd, version, changelogFile, changelogFormat) {
71
- const changelogPath = node_path_1.default.join(cwd, changelogFile);
72
- if (!node_fs_1.default.existsSync(changelogPath)) {
69
+ const changelogPath = path.join(cwd, changelogFile);
70
+ if (!fs.existsSync(changelogPath)) {
73
71
  return `Automated release for v${version}`;
74
72
  }
75
- const content = node_fs_1.default.readFileSync(changelogPath, "utf8");
73
+ const content = fs.readFileSync(changelogPath, "utf8");
76
74
  const notes = extractReleaseNotes(content, version, changelogFormat);
77
75
  return notes.length > 0 ? notes : `Automated release for v${version}`;
78
76
  }
79
- function extractClosingReferencesFromNotes(notes) {
77
+ export function extractClosingReferencesFromNotes(notes) {
80
78
  const refs = new Set();
81
79
  const linkedPattern = /\b(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\b\s+\[#(\d+)\]\([^)]+\)/giu;
82
80
  for (const match of notes.matchAll(linkedPattern)) {
@@ -94,30 +92,30 @@ function extractClosingReferencesFromNotes(notes) {
94
92
  }
95
93
  return [...refs].sort((a, b) => a - b);
96
94
  }
97
- function resolveTargetChangelogFile(config, rootChangelogFile, targetPath) {
95
+ export function resolveTargetChangelogFile(config, rootChangelogFile, targetPath) {
98
96
  if (targetPath === ".") {
99
97
  return rootChangelogFile;
100
98
  }
101
99
  const packageConfig = config.packages?.[targetPath];
102
- const packageStrategy = (0, resolve_js_1.resolveVersionStrategy)({
100
+ const packageStrategy = resolveVersionStrategy({
103
101
  ...config,
104
102
  "release-type": packageConfig?.["release-type"] ?? config["release-type"],
105
103
  });
106
- const { changelogFile: packageChangelogFile } = (0, plan_js_1.getChangelogDefaults)({
104
+ const { changelogFile: packageChangelogFile } = getChangelogDefaults({
107
105
  "release-type": packageConfig?.["release-type"] ?? config["release-type"],
108
106
  "changelog-file": packageConfig?.["changelog-file"] ?? config["changelog-file"],
109
107
  "changelog-format": packageConfig?.["changelog-format"] ?? config["changelog-format"],
110
108
  defaultChangelogFormat: packageStrategy.getDefaultChangelogFormat?.(),
111
109
  });
112
- return node_path_1.default.posix.join(targetPath, packageChangelogFile);
110
+ return path.posix.join(targetPath, packageChangelogFile);
113
111
  }
114
112
  function resolveTargetChangelogFormat(config, targetPath) {
115
113
  const packageConfig = config.packages?.[targetPath];
116
- const packageStrategy = (0, resolve_js_1.resolveVersionStrategy)({
114
+ const packageStrategy = resolveVersionStrategy({
117
115
  ...config,
118
116
  "release-type": packageConfig?.["release-type"] ?? config["release-type"],
119
117
  });
120
- const { changelogFormat } = (0, plan_js_1.getChangelogDefaults)({
118
+ const { changelogFormat } = getChangelogDefaults({
121
119
  "release-type": packageConfig?.["release-type"] ?? config["release-type"],
122
120
  "changelog-file": packageConfig?.["changelog-file"] ?? config["changelog-file"],
123
121
  "changelog-format": packageConfig?.["changelog-format"] ?? config["changelog-format"],
@@ -125,31 +123,31 @@ function resolveTargetChangelogFormat(config, targetPath) {
125
123
  });
126
124
  return changelogFormat;
127
125
  }
128
- async function runRelease(cwd = process.cwd()) {
126
+ export async function runRelease(cwd = process.cwd()) {
129
127
  const result = await runReleaseDetailed(cwd, { logger: console });
130
128
  if (result.action === "release-skipped") {
131
129
  return result.reason;
132
130
  }
133
131
  return result.message;
134
132
  }
135
- async function runReleaseDetailed(cwd = process.cwd(), options = {}) {
133
+ export async function runReleaseDetailed(cwd = process.cwd(), options = {}) {
136
134
  const commitMessage = getHeadCommitMessage(cwd);
137
- if (!(0, pr_js_1.isReleaseCommitMessage)(commitMessage)) {
135
+ if (!isReleaseCommitMessage(commitMessage)) {
138
136
  return {
139
137
  action: "release-skipped",
140
138
  reason: "No release commit context detected; skipping release stage.",
141
139
  };
142
140
  }
143
- const loaded = (0, load_config_js_1.loadConfig)(cwd);
144
- const strategy = (0, resolve_js_1.resolveVersionStrategy)(loaded.config);
145
- const { changelogFile } = (0, plan_js_1.getChangelogDefaults)({
141
+ const loaded = loadConfig(cwd);
142
+ const strategy = resolveVersionStrategy(loaded.config);
143
+ const { changelogFile } = getChangelogDefaults({
146
144
  ...loaded.config,
147
145
  defaultChangelogFormat: strategy.getDefaultChangelogFormat?.(),
148
146
  });
149
147
  const referenceCommentMode = loaded.config["release-reference-comments"] ?? "off";
150
148
  const version = strategy.readVersion(cwd, loaded.config);
151
149
  const defaultTag = `v${version}`;
152
- const releaseTargets = (0, state_js_1.readReleaseTargets)(cwd);
150
+ const releaseTargets = readReleaseTargets(cwd);
153
151
  const targets = releaseTargets.length > 0
154
152
  ? releaseTargets
155
153
  : [
@@ -170,15 +168,15 @@ async function runReleaseDetailed(cwd = process.cwd(), options = {}) {
170
168
  message: `Dry run: would publish releases ${targetList.join(", ")}`,
171
169
  };
172
170
  }
173
- const scmClient = (0, client_js_1.getScmClient)();
171
+ const scmClient = getScmClient();
174
172
  const releases = [];
175
- const referencesByTag = new Map();
173
+ const referenceReleases = [];
176
174
  for (const target of targets) {
177
175
  const targetChangelogFile = resolveTargetChangelogFile(loaded.config, changelogFile, target.path);
178
176
  const targetChangelogFormat = resolveTargetChangelogFormat(loaded.config, target.path);
179
177
  const releaseNotes = readReleaseNotes(cwd, target.version, targetChangelogFile, targetChangelogFormat);
180
- referencesByTag.set(target.tag, extractClosingReferencesFromNotes(releaseNotes));
181
- const outcome = await (0, recovery_js_1.executeIdempotentReleaseTarget)(cwd, {
178
+ const references = extractClosingReferencesFromNotes(releaseNotes);
179
+ const outcome = await executeIdempotentReleaseTarget(cwd, {
182
180
  tag: target.tag,
183
181
  version: target.version,
184
182
  notes: releaseNotes,
@@ -197,21 +195,27 @@ async function runReleaseDetailed(cwd = process.cwd(), options = {}) {
197
195
  tagStatus: outcome.tagStatus,
198
196
  metadataStatus: outcome.metadataStatus,
199
197
  });
200
- const references = referencesByTag.get(outcome.tag) ?? [];
201
- if (references.length > 0 &&
202
- referenceCommentMode !== "off" &&
203
- scmClient.createReleaseReferenceComments) {
204
- await scmClient.createReleaseReferenceComments({
198
+ if (references.length > 0) {
199
+ referenceReleases.push({
200
+ name: resolveTargetPackageName(cwd, loaded.config, target.path),
201
+ tag: outcome.tag,
205
202
  version: target.version,
206
203
  releaseUrl: outcome.url,
207
204
  references,
208
- mode: referenceCommentMode,
209
- }, {
210
- cwd,
211
- logger: options.logger,
212
205
  });
213
206
  }
214
207
  }
208
+ if (referenceReleases.length > 0 &&
209
+ referenceCommentMode !== "off" &&
210
+ scmClient.createReleaseReferenceComments) {
211
+ await scmClient.createReleaseReferenceComments({
212
+ releases: referenceReleases,
213
+ mode: referenceCommentMode,
214
+ }, {
215
+ cwd,
216
+ logger: options.logger,
217
+ });
218
+ }
215
219
  const published = releases.map((outcome) => `${outcome.tag}: ${outcome.url} (tag=${outcome.tagStatus}, metadata=${outcome.metadataStatus})`);
216
220
  return {
217
221
  action: "release-published",
@@ -220,10 +224,10 @@ async function runReleaseDetailed(cwd = process.cwd(), options = {}) {
220
224
  };
221
225
  }
222
226
  /** @deprecated Use runRelease. */
223
- async function runSimpleRelease(cwd = process.cwd()) {
227
+ export async function runSimpleRelease(cwd = process.cwd()) {
224
228
  return runRelease(cwd);
225
229
  }
226
230
  /** @deprecated Use runReleaseDetailed. */
227
- async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
231
+ export async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
228
232
  return runReleaseDetailed(cwd, options);
229
233
  }
@@ -1,11 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.maxReleaseType = maxReleaseType;
4
- exports.parseVersion = parseVersion;
5
- exports.isValidVersion = isValidVersion;
6
- exports.compareVersions = compareVersions;
7
- exports.bumpVersion = bumpVersion;
8
- function maxReleaseType(types) {
1
+ export function maxReleaseType(types) {
9
2
  if (types.includes("major")) {
10
3
  return "major";
11
4
  }
@@ -28,7 +21,7 @@ function normalizeVersionInput(version) {
28
21
  const [, major, minor, patch, prerelease] = compatMatch;
29
22
  return `${major}.${minor}.${patch}-${prerelease}`;
30
23
  }
31
- function parseVersion(version) {
24
+ export function parseVersion(version) {
32
25
  const normalized = normalizeVersionInput(version);
33
26
  const match = normalized.match(SEMVER_PATTERN);
34
27
  if (!match) {
@@ -42,7 +35,7 @@ function parseVersion(version) {
42
35
  build: match[5] ? match[5].split(".") : [],
43
36
  };
44
37
  }
45
- function isValidVersion(version) {
38
+ export function isValidVersion(version) {
46
39
  return SEMVER_PATTERN.test(normalizeVersionInput(version));
47
40
  }
48
41
  function isNumericIdentifier(identifier) {
@@ -76,7 +69,7 @@ function comparePreReleaseIdentifiers(left, right) {
76
69
  }
77
70
  return 0;
78
71
  }
79
- function compareVersions(leftRaw, rightRaw) {
72
+ export function compareVersions(leftRaw, rightRaw) {
80
73
  const left = parseVersion(leftRaw);
81
74
  const right = parseVersion(rightRaw);
82
75
  if (left.major !== right.major) {
@@ -116,7 +109,7 @@ function compareVersions(leftRaw, rightRaw) {
116
109
  }
117
110
  return 0;
118
111
  }
119
- function bumpVersion(current, releaseType, options = {}) {
112
+ export function bumpVersion(current, releaseType, options = {}) {
120
113
  const parsed = parseVersion(current);
121
114
  const allowStableMajor = options.allowStableMajor ?? false;
122
115
  if (releaseType === "major") {
@@ -1,16 +1,7 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getBaselineStatePath = getBaselineStatePath;
7
- exports.readBaselineSha = readBaselineSha;
8
- exports.readReleaseTargets = readReleaseTargets;
9
- exports.writeBaselineSha = writeBaselineSha;
10
- const node_child_process_1 = require("node:child_process");
11
- const node_fs_1 = __importDefault(require("node:fs"));
12
- const node_path_1 = __importDefault(require("node:path"));
13
- const load_config_js_1 = require("../config/load-config.js");
1
+ import { execFileSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { loadConfig } from "../config/load-config.js";
14
5
  const MANIFEST_VERSION_KEY = "manifest-version";
15
6
  const BASELINE_SHA_KEY = "baseline-sha";
16
7
  const RELEASE_TARGETS_KEY = "release-targets";
@@ -47,48 +38,48 @@ function parseStateFile(raw, filePath) {
47
38
  }
48
39
  return manifest;
49
40
  }
50
- function getBaselineStatePath(cwd) {
51
- const loaded = (0, load_config_js_1.loadConfig)(cwd);
41
+ export function getBaselineStatePath(cwd) {
42
+ const loaded = loadConfig(cwd);
52
43
  const configured = loaded.config["baseline-file"];
53
44
  if (configured) {
54
- return node_path_1.default.join(cwd, configured);
45
+ return path.join(cwd, configured);
55
46
  }
56
- const preferred = node_path_1.default.join(cwd, ".versionary-manifest.json");
57
- if (node_fs_1.default.existsSync(preferred)) {
47
+ const preferred = path.join(cwd, ".versionary-manifest.json");
48
+ if (fs.existsSync(preferred)) {
58
49
  return preferred;
59
50
  }
60
- const legacy = node_path_1.default.join(cwd, "versionary.versions.json");
61
- if (node_fs_1.default.existsSync(legacy)) {
51
+ const legacy = path.join(cwd, "versionary.versions.json");
52
+ if (fs.existsSync(legacy)) {
62
53
  return legacy;
63
54
  }
64
55
  return preferred;
65
56
  }
66
- function readBaselineSha(cwd = process.cwd()) {
57
+ export function readBaselineSha(cwd = process.cwd()) {
67
58
  const filePath = getBaselineStatePath(cwd);
68
- if (!node_fs_1.default.existsSync(filePath)) {
59
+ if (!fs.existsSync(filePath)) {
69
60
  return null;
70
61
  }
71
- const parsed = parseStateFile(node_fs_1.default.readFileSync(filePath, "utf8"), filePath);
62
+ const parsed = parseStateFile(fs.readFileSync(filePath, "utf8"), filePath);
72
63
  return parsed[BASELINE_SHA_KEY] ?? null;
73
64
  }
74
- function readReleaseTargets(cwd = process.cwd()) {
65
+ export function readReleaseTargets(cwd = process.cwd()) {
75
66
  const filePath = getBaselineStatePath(cwd);
76
- if (!node_fs_1.default.existsSync(filePath)) {
67
+ if (!fs.existsSync(filePath)) {
77
68
  return [];
78
69
  }
79
- const parsed = parseStateFile(node_fs_1.default.readFileSync(filePath, "utf8"), filePath);
70
+ const parsed = parseStateFile(fs.readFileSync(filePath, "utf8"), filePath);
80
71
  return parsed[RELEASE_TARGETS_KEY] ?? [];
81
72
  }
82
- function writeBaselineSha(cwd = process.cwd(), sha, releaseTargets) {
73
+ export function writeBaselineSha(cwd = process.cwd(), sha, releaseTargets) {
83
74
  const baselineShaValue = sha ??
84
- (0, node_child_process_1.execFileSync)("git", ["rev-parse", "HEAD"], {
75
+ execFileSync("git", ["rev-parse", "HEAD"], {
85
76
  cwd,
86
77
  encoding: "utf8",
87
78
  stdio: ["ignore", "pipe", "ignore"],
88
79
  }).trim();
89
80
  const filePath = getBaselineStatePath(cwd);
90
- const existing = node_fs_1.default.existsSync(filePath)
91
- ? parseStateFile(node_fs_1.default.readFileSync(filePath, "utf8"), filePath)
81
+ const existing = fs.existsSync(filePath)
82
+ ? parseStateFile(fs.readFileSync(filePath, "utf8"), filePath)
92
83
  : {};
93
84
  const existingTargets = existing[RELEASE_TARGETS_KEY] ?? [];
94
85
  const nextTargets = releaseTargets === undefined
@@ -104,5 +95,5 @@ function writeBaselineSha(cwd = process.cwd(), sha, releaseTargets) {
104
95
  [BASELINE_SHA_KEY]: baselineShaValue,
105
96
  [RELEASE_TARGETS_KEY]: nextTargets,
106
97
  };
107
- node_fs_1.default.writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
98
+ fs.writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
108
99
  }
@@ -1,26 +1,20 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.verifyProject = verifyProject;
7
- const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- const load_config_js_1 = require("../config/load-config.js");
10
- const package_context_js_1 = require("../strategy/package-context.js");
11
- const resolve_js_1 = require("../strategy/resolve.js");
12
- function verifyProject(cwd = process.cwd()) {
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { loadConfig } from "../config/load-config.js";
4
+ import { resolvePackageStrategyContext } from "../strategy/package-context.js";
5
+ import { resolveVersionStrategy } from "../strategy/resolve.js";
6
+ export function verifyProject(cwd = process.cwd()) {
13
7
  const checks = [];
14
- const config = (0, load_config_js_1.loadConfig)(cwd);
8
+ const config = loadConfig(cwd);
15
9
  checks.push({
16
10
  name: "config-load",
17
11
  ok: true,
18
- details: `Loaded ${node_path_1.default.basename(config.path)} (${config.format})`,
12
+ details: `Loaded ${path.basename(config.path)} (${config.format})`,
19
13
  category: "config",
20
14
  });
21
- const strategy = (0, resolve_js_1.resolveVersionStrategy)(config.config);
15
+ const strategy = resolveVersionStrategy(config.config);
22
16
  const versionFile = strategy.getVersionFile(config.config);
23
- const exists = node_fs_1.default.existsSync(node_path_1.default.join(cwd, versionFile));
17
+ const exists = fs.existsSync(path.join(cwd, versionFile));
24
18
  checks.push({
25
19
  name: `version-file:${versionFile}`,
26
20
  ok: exists,
@@ -44,8 +38,8 @@ function verifyProject(cwd = process.cwd()) {
44
38
  }
45
39
  if (config.config.packages) {
46
40
  for (const [pkgPathRaw, packageConfig] of Object.entries(config.config.packages)) {
47
- const pkgPath = node_path_1.default.join(cwd, pkgPathRaw);
48
- const exists = node_fs_1.default.existsSync(pkgPath);
41
+ const pkgPath = path.join(cwd, pkgPathRaw);
42
+ const exists = fs.existsSync(pkgPath);
49
43
  checks.push({
50
44
  name: `package-path:${pkgPathRaw}`,
51
45
  ok: exists,
@@ -56,9 +50,9 @@ function verifyProject(cwd = process.cwd()) {
56
50
  : `Create ${pkgPathRaw} or remove/rename this entry under "packages" in versionary config.`,
57
51
  });
58
52
  if (exists) {
59
- const packageContext = (0, package_context_js_1.resolvePackageStrategyContext)(config.config, pkgPathRaw, packageConfig);
53
+ const packageContext = resolvePackageStrategyContext(config.config, pkgPathRaw, packageConfig);
60
54
  const packageVersionFile = packageContext.versionFile;
61
- const packageVersionExists = node_fs_1.default.existsSync(node_path_1.default.join(cwd, packageVersionFile));
55
+ const packageVersionExists = fs.existsSync(path.join(cwd, packageVersionFile));
62
56
  checks.push({
63
57
  name: `version-file:${packageVersionFile}`,
64
58
  ok: packageVersionExists,
@@ -1,10 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.pluginHasCapability = pluginHasCapability;
4
- exports.findPluginsByCapability = findPluginsByCapability;
5
- function pluginHasCapability(plugin, capability) {
1
+ export function pluginHasCapability(plugin, capability) {
6
2
  return plugin.capabilities.includes(capability);
7
3
  }
8
- function findPluginsByCapability(plugins, capability) {
4
+ export function findPluginsByCapability(plugins, capability) {
9
5
  return plugins.filter((plugin) => pluginHasCapability(plugin, capability));
10
6
  }
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getScmClient = getScmClient;
4
- const github_plugin_js_1 = require("./github-plugin.js");
5
- function getScmClient() {
6
- return (0, github_plugin_js_1.createGitHubPlugin)();
1
+ import { createGitHubPlugin } from "./github-plugin.js";
2
+ export function getScmClient() {
3
+ return createGitHubPlugin();
7
4
  }
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createGitHubPlugin = createGitHubPlugin;
4
- const rest_1 = require("@octokit/rest");
1
+ import { Octokit } from "@octokit/rest";
5
2
  function parseRepoSlug(input) {
6
3
  const [owner, repo] = input.split("/");
7
4
  if (!owner || !repo) {
@@ -86,7 +83,43 @@ async function ensureLabels(octokit, repo, pullNumber, labels, branchContext) {
86
83
  throw new Error(`Failed applying labels to pull request #${pullNumber}: [${repoRef(repo)} base=${branchContext.baseBranch} head=${branchContext.headBranch}] ${message}`);
87
84
  }
88
85
  }
89
- function createGitHubPlugin() {
86
+ function groupReleasesByIssue(releases) {
87
+ const byIssue = new Map();
88
+ for (const release of releases) {
89
+ for (const reference of release.references) {
90
+ const bucket = byIssue.get(reference) ?? [];
91
+ if (!bucket.some((entry) => entry.tag === release.tag)) {
92
+ bucket.push(release);
93
+ }
94
+ byIssue.set(reference, bucket);
95
+ }
96
+ }
97
+ for (const bucket of byIssue.values()) {
98
+ bucket.sort((a, b) => a.tag.localeCompare(b.tag));
99
+ }
100
+ return new Map([...byIssue.entries()].sort(([a], [b]) => a - b));
101
+ }
102
+ function renderReleaseLink(release) {
103
+ if (release.name) {
104
+ return `[\`${release.name}\` v${release.version}](${release.releaseUrl})`;
105
+ }
106
+ return `[version ${release.version}](${release.releaseUrl})`;
107
+ }
108
+ function renderReleaseReferenceCommentBody(releases) {
109
+ const footer = "Released by [Versionary](https://github.com/jolars/versionary).";
110
+ if (releases.length === 1) {
111
+ const release = releases[0];
112
+ if (!release) {
113
+ throw new Error("Expected at least one release for comment body.");
114
+ }
115
+ return `This is included in ${renderReleaseLink(release)}. :tada:\n\n${footer}`;
116
+ }
117
+ const bullets = releases
118
+ .map((release) => `- ${renderReleaseLink(release)}`)
119
+ .join("\n");
120
+ return `This is included in the following releases: :tada:\n\n${bullets}\n\n${footer}`;
121
+ }
122
+ export function createGitHubPlugin() {
90
123
  return {
91
124
  name: "github",
92
125
  capabilities: [
@@ -97,7 +130,7 @@ function createGitHubPlugin() {
97
130
  provider: "github",
98
131
  async createOrUpdateReviewRequest(input, _context) {
99
132
  const repo = getRepoFromEnv();
100
- const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
133
+ const octokit = new Octokit({ auth: getGitHubToken() });
101
134
  const listHead = resolveHeadForList(repo, input.headBranch);
102
135
  let existing;
103
136
  try {
@@ -176,7 +209,7 @@ function createGitHubPlugin() {
176
209
  },
177
210
  async closeReviewRequestIfExists(input, _context) {
178
211
  const repo = getRepoFromEnv();
179
- const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
212
+ const octokit = new Octokit({ auth: getGitHubToken() });
180
213
  const listHead = resolveHeadForList(repo, input.headBranch);
181
214
  let existing;
182
215
  try {
@@ -238,7 +271,7 @@ function createGitHubPlugin() {
238
271
  },
239
272
  async createReleaseMetadata(input, _context) {
240
273
  const repo = getRepoFromEnv();
241
- const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
274
+ const octokit = new Octokit({ auth: getGitHubToken() });
242
275
  try {
243
276
  const existing = await octokit.repos.getReleaseByTag({
244
277
  owner: repo.owner,
@@ -274,12 +307,12 @@ function createGitHubPlugin() {
274
307
  },
275
308
  async createReleaseReferenceComments(input, context) {
276
309
  const repo = getRepoFromEnv();
277
- const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
278
- const uniqueReferences = [...new Set(input.references)].sort((a, b) => a - b);
279
- const commented = [];
310
+ const octokit = new Octokit({ auth: getGitHubToken() });
280
311
  const mode = input.mode ?? "best-effort";
281
- for (const reference of uniqueReferences) {
282
- const body = `This is included in [version ${input.version}](${input.releaseUrl}). :tada:\n\nReleased by [Versionary](https://github.com/jolars/versionary).`;
312
+ const releasesByIssue = groupReleasesByIssue(input.releases);
313
+ const commented = [];
314
+ for (const [reference, releases] of releasesByIssue) {
315
+ const body = renderReleaseReferenceCommentBody(releases);
283
316
  try {
284
317
  await octokit.issues.createComment({
285
318
  owner: repo.owner,
@@ -41,10 +41,15 @@ export interface ScmReleaseMetadataResult {
41
41
  url: string;
42
42
  status?: "created" | "exists";
43
43
  }
44
- export interface ScmReleaseReferenceCommentsInput {
44
+ export interface ScmReleaseReferenceCommentsRelease {
45
+ name?: string;
46
+ tag: string;
45
47
  version: string;
46
48
  releaseUrl: string;
47
49
  references: number[];
50
+ }
51
+ export interface ScmReleaseReferenceCommentsInput {
52
+ releases: ScmReleaseReferenceCommentsRelease[];
48
53
  mode?: "best-effort" | "strict";
49
54
  }
50
55
  export interface ScmReleaseReferenceCommentsResult {
package/dist/scm/types.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compositeVersionStrategy = compositeVersionStrategy;
4
1
  function configForSecondary(config) {
5
2
  if (config["version-file"] === undefined) {
6
3
  return config;
@@ -11,7 +8,7 @@ function configForSecondary(config) {
11
8
  function dedupedSorted(values) {
12
9
  return [...new Set(values)].sort((a, b) => a.localeCompare(b));
13
10
  }
14
- function compositeVersionStrategy(strategies) {
11
+ export function compositeVersionStrategy(strategies) {
15
12
  if (strategies.length === 0) {
16
13
  throw new Error("compositeVersionStrategy requires at least one strategy.");
17
14
  }