versionary 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +39 -21
  2. package/dist/cli/index.js +22 -33
  3. package/dist/config/schema.d.ts +9 -0
  4. package/dist/config/schema.js +3 -0
  5. package/dist/{infra/git → git}/commits.d.ts +1 -1
  6. package/dist/index.d.ts +6 -5
  7. package/dist/index.js +10 -10
  8. package/dist/release/artifact-rules.d.ts +3 -0
  9. package/dist/release/changelog.d.ts +32 -0
  10. package/dist/{domain/release → release}/changelog.js +66 -7
  11. package/dist/release/plan.d.ts +37 -0
  12. package/dist/{domain/release → release}/plan.js +54 -19
  13. package/dist/{app/release → release}/pr.d.ts +13 -5
  14. package/dist/{app/release → release}/pr.js +109 -91
  15. package/dist/{app/release → release}/recovery.d.ts +2 -1
  16. package/dist/{app/release → release}/release.d.ts +14 -5
  17. package/dist/{app/release → release}/release.js +80 -40
  18. package/dist/{domain/release → release}/semver.js +14 -3
  19. package/dist/{app/release → release}/state.js +1 -1
  20. package/dist/{verify → release}/verify-project.js +2 -2
  21. package/dist/scm/client.d.ts +2 -0
  22. package/dist/scm/client.js +7 -0
  23. package/dist/scm/github-plugin.d.ts +2 -1
  24. package/dist/scm/github-plugin.js +2 -0
  25. package/dist/scm/types.d.ts +37 -0
  26. package/dist/{domain/strategy → strategy}/node.js +13 -0
  27. package/dist/{domain/strategy → strategy}/package-context.d.ts +1 -1
  28. package/dist/{domain/strategy → strategy}/package-context.js +2 -2
  29. package/dist/{domain/strategy → strategy}/r.js +14 -0
  30. package/dist/{domain/strategy → strategy}/resolve.d.ts +2 -1
  31. package/dist/strategy/resolve.js +21 -0
  32. package/dist/{domain/strategy → strategy}/rust.js +42 -0
  33. package/dist/strategy/types.d.ts +21 -0
  34. package/dist/types/config.d.ts +4 -0
  35. package/dist/types/plugins.d.ts +7 -30
  36. package/package.json +1 -1
  37. package/dist/app/release/artifact-rules.d.ts +0 -3
  38. package/dist/app/release/verify.d.ts +0 -2
  39. package/dist/app/release/verify.js +0 -5
  40. package/dist/domain/release/changelog.d.ts +0 -19
  41. package/dist/domain/release/plan.d.ts +0 -21
  42. package/dist/domain/strategy/resolve.js +0 -19
  43. package/dist/domain/strategy/types.d.ts +0 -7
  44. package/dist/infra/scm/github/plugin.d.ts +0 -1
  45. package/dist/infra/scm/github/plugin.js +0 -5
  46. package/dist/infra/scm/runtime.d.ts +0 -2
  47. package/dist/infra/scm/runtime.js +0 -7
  48. package/dist/infra/scm/types.d.ts +0 -1
  49. package/dist/plugins/runtime.d.ts +0 -1
  50. package/dist/plugins/runtime.js +0 -5
  51. package/dist/strategies/node.d.ts +0 -1
  52. package/dist/strategies/node.js +0 -5
  53. package/dist/strategies/resolve.d.ts +0 -1
  54. package/dist/strategies/resolve.js +0 -5
  55. package/dist/strategies/simple.d.ts +0 -1
  56. package/dist/strategies/simple.js +0 -5
  57. package/dist/strategies/types.d.ts +0 -1
  58. package/dist/strategies/types.js +0 -2
  59. /package/dist/{infra/git → git}/commits.js +0 -0
  60. /package/dist/{infra/git → git}/repo-url.d.ts +0 -0
  61. /package/dist/{infra/git → git}/repo-url.js +0 -0
  62. /package/dist/{app/release → release}/artifact-rules.js +0 -0
  63. /package/dist/{app/release → release}/recovery.js +0 -0
  64. /package/dist/{domain/release → release}/semver.d.ts +0 -0
  65. /package/dist/{app/release → release}/state.d.ts +0 -0
  66. /package/dist/{verify → release}/verify-project.d.ts +0 -0
  67. /package/dist/{plugins → scm}/capabilities.d.ts +0 -0
  68. /package/dist/{plugins → scm}/capabilities.js +0 -0
  69. /package/dist/{domain/strategy → scm}/types.js +0 -0
  70. /package/dist/{domain/strategy → strategy}/node.d.ts +0 -0
  71. /package/dist/{domain/strategy → strategy}/r.d.ts +0 -0
  72. /package/dist/{domain/strategy → strategy}/rust.d.ts +0 -0
  73. /package/dist/{domain/strategy → strategy}/simple.d.ts +0 -0
  74. /package/dist/{domain/strategy → strategy}/simple.js +0 -0
  75. /package/dist/{infra/scm → strategy}/types.js +0 -0
@@ -3,68 +3,107 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.extractReleaseNotes = extractReleaseNotes;
6
7
  exports.resolveTargetChangelogFile = resolveTargetChangelogFile;
8
+ exports.runRelease = runRelease;
9
+ exports.runReleaseDetailed = runReleaseDetailed;
7
10
  exports.runSimpleRelease = runSimpleRelease;
8
11
  exports.runSimpleReleaseDetailed = runSimpleReleaseDetailed;
9
12
  const node_child_process_1 = require("node:child_process");
10
13
  const node_fs_1 = __importDefault(require("node:fs"));
11
14
  const node_path_1 = __importDefault(require("node:path"));
12
- const load_config_js_1 = require("../../config/load-config.js");
13
- const resolve_js_1 = require("../../domain/strategy/resolve.js");
14
- const capabilities_js_1 = require("../../plugins/capabilities.js");
15
- const runtime_js_1 = require("../../plugins/runtime.js");
15
+ const load_config_js_1 = require("../config/load-config.js");
16
+ const client_js_1 = require("../scm/client.js");
17
+ const resolve_js_1 = require("../strategy/resolve.js");
18
+ const plan_js_1 = require("./plan.js");
16
19
  const pr_js_1 = require("./pr.js");
17
20
  const recovery_js_1 = require("./recovery.js");
18
21
  const state_js_1 = require("./state.js");
19
- function getHeadCommitMessage(cwd) {
20
- return (0, node_child_process_1.execFileSync)("git", ["log", "-1", "--pretty=%B"], {
21
- cwd,
22
- encoding: "utf8",
23
- stdio: ["ignore", "pipe", "ignore"],
24
- }).trim();
22
+ function escapeRegExp(input) {
23
+ return input.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
25
24
  }
26
- function readReleaseNotes(cwd, version, changelogFile) {
27
- const changelogPath = node_path_1.default.join(cwd, changelogFile);
28
- if (!node_fs_1.default.existsSync(changelogPath)) {
29
- return `Automated release for v${version}`;
30
- }
31
- const content = node_fs_1.default.readFileSync(changelogPath, "utf8");
25
+ function extractReleaseNotes(content, version, changelogFormat) {
32
26
  const lines = content.split("\n");
33
- const start = lines.findIndex((line) => line.startsWith(`## ${version} -`) || line.startsWith(`## [${version}](`));
27
+ const shortVersion = version.replace(/\.\d+$/u, "");
28
+ const start = changelogFormat === "r-news"
29
+ ? lines.findIndex((line) => {
30
+ const fullMatch = new RegExp(`^#\\s+.+\\s+${escapeRegExp(version)}\\s*$`, "u");
31
+ const shortMatch = new RegExp(`^#\\s+.+\\s+${escapeRegExp(shortVersion)}\\s*$`, "u");
32
+ return fullMatch.test(line) || shortMatch.test(line);
33
+ })
34
+ : lines.findIndex((line) => line.startsWith(`## ${version} -`) ||
35
+ line.startsWith(`## [${version}](`) ||
36
+ line.startsWith(`# ${shortVersion} -`) ||
37
+ line.startsWith(`# [${shortVersion}](`) ||
38
+ new RegExp(`^#\\s+.+\\s+${shortVersion}\\s*$`, "u").test(line));
34
39
  if (start < 0) {
35
- return `Automated release for v${version}`;
40
+ return "";
36
41
  }
37
42
  let end = lines.length;
38
43
  for (let idx = start + 1; idx < lines.length; idx += 1) {
39
- if (lines[idx]?.startsWith("## ")) {
44
+ const line = lines[idx] ?? "";
45
+ if (changelogFormat === "r-news") {
46
+ if (/^#(?!#)\s+/u.test(line)) {
47
+ end = idx;
48
+ break;
49
+ }
50
+ continue;
51
+ }
52
+ if (line.startsWith("## ")) {
40
53
  end = idx;
41
54
  break;
42
55
  }
43
56
  }
44
- const notes = lines
57
+ return lines
45
58
  .slice(start + 1, end)
46
59
  .join("\n")
47
60
  .trim();
61
+ }
62
+ function getHeadCommitMessage(cwd) {
63
+ return (0, node_child_process_1.execFileSync)("git", ["log", "-1", "--pretty=%B"], {
64
+ cwd,
65
+ encoding: "utf8",
66
+ stdio: ["ignore", "pipe", "ignore"],
67
+ }).trim();
68
+ }
69
+ function readReleaseNotes(cwd, version, changelogFile, changelogFormat) {
70
+ const changelogPath = node_path_1.default.join(cwd, changelogFile);
71
+ if (!node_fs_1.default.existsSync(changelogPath)) {
72
+ return `Automated release for v${version}`;
73
+ }
74
+ const content = node_fs_1.default.readFileSync(changelogPath, "utf8");
75
+ const notes = extractReleaseNotes(content, version, changelogFormat);
48
76
  return notes.length > 0 ? notes : `Automated release for v${version}`;
49
77
  }
50
78
  function resolveTargetChangelogFile(config, rootChangelogFile, targetPath) {
51
79
  if (targetPath === ".") {
52
80
  return rootChangelogFile;
53
81
  }
54
- const packageChangelogFile = config.packages?.[targetPath]?.["changelog-file"];
55
- if (!packageChangelogFile) {
56
- return rootChangelogFile;
57
- }
82
+ const packageConfig = config.packages?.[targetPath];
83
+ const { changelogFile: packageChangelogFile } = (0, plan_js_1.getChangelogDefaults)({
84
+ "release-type": packageConfig?.["release-type"] ?? config["release-type"],
85
+ "changelog-file": packageConfig?.["changelog-file"] ?? config["changelog-file"],
86
+ "changelog-format": packageConfig?.["changelog-format"] ?? config["changelog-format"],
87
+ });
58
88
  return node_path_1.default.posix.join(targetPath, packageChangelogFile);
59
89
  }
60
- async function runSimpleRelease(cwd = process.cwd()) {
61
- const result = await runSimpleReleaseDetailed(cwd, { logger: console });
90
+ function resolveTargetChangelogFormat(config, targetPath) {
91
+ const packageConfig = targetPath === "." ? undefined : config.packages?.[targetPath];
92
+ const { changelogFormat } = (0, plan_js_1.getChangelogDefaults)({
93
+ "release-type": packageConfig?.["release-type"] ?? config["release-type"],
94
+ "changelog-file": packageConfig?.["changelog-file"] ?? config["changelog-file"],
95
+ "changelog-format": packageConfig?.["changelog-format"] ?? config["changelog-format"],
96
+ });
97
+ return changelogFormat;
98
+ }
99
+ async function runRelease(cwd = process.cwd()) {
100
+ const result = await runReleaseDetailed(cwd, { logger: console });
62
101
  if (result.action === "release-skipped") {
63
102
  return result.reason;
64
103
  }
65
104
  return result.message;
66
105
  }
67
- async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
106
+ async function runReleaseDetailed(cwd = process.cwd(), options = {}) {
68
107
  const commitMessage = getHeadCommitMessage(cwd);
69
108
  if (!(0, pr_js_1.isReleaseCommitMessage)(commitMessage)) {
70
109
  return {
@@ -74,7 +113,7 @@ async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
74
113
  }
75
114
  const loaded = (0, load_config_js_1.loadConfig)(cwd);
76
115
  const strategy = (0, resolve_js_1.resolveVersionStrategy)(loaded.config);
77
- const changelogFile = loaded.config["changelog-file"] ?? "CHANGELOG.md";
116
+ const { changelogFile } = (0, plan_js_1.getChangelogDefaults)(loaded.config);
78
117
  const version = strategy.readVersion(cwd, loaded.config);
79
118
  const defaultTag = `v${version}`;
80
119
  const releaseTargets = (0, state_js_1.readReleaseTargets)(cwd);
@@ -98,25 +137,18 @@ async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
98
137
  message: `Dry run: would publish releases ${targetList.join(", ")}`,
99
138
  };
100
139
  }
101
- const plugins = (0, runtime_js_1.loadRuntimePlugins)();
102
- const scmPlugins = (0, capabilities_js_1.findPluginsByCapability)(plugins, "scm.releaseMetadata");
103
- if (scmPlugins.length === 0) {
104
- throw new Error("No scm.releaseMetadata plugin is available.");
105
- }
106
- const plugin = scmPlugins[0];
107
- if (!plugin?.createReleaseMetadata) {
108
- throw new Error(`Plugin "${plugin?.name ?? "unknown"}" does not implement createReleaseMetadata.`);
109
- }
110
- const createReleaseMetadata = plugin.createReleaseMetadata;
140
+ const scmClient = (0, client_js_1.getScmClient)();
111
141
  const releases = [];
112
142
  for (const target of targets) {
113
143
  const targetChangelogFile = resolveTargetChangelogFile(loaded.config, changelogFile, target.path);
144
+ const targetChangelogFormat = resolveTargetChangelogFormat(loaded.config, target.path);
114
145
  const outcome = await (0, recovery_js_1.executeIdempotentReleaseTarget)(cwd, {
115
146
  tag: target.tag,
116
147
  version: target.version,
117
- notes: readReleaseNotes(cwd, target.version, targetChangelogFile),
148
+ notes: readReleaseNotes(cwd, target.version, targetChangelogFile, targetChangelogFormat),
149
+ draft: loaded.config["release-draft"] ?? false,
118
150
  }, {
119
- createReleaseMetadata: (input) => createReleaseMetadata(input, {
151
+ createReleaseMetadata: (input) => scmClient.createReleaseMetadata(input, {
120
152
  cwd,
121
153
  logger: options.logger,
122
154
  }),
@@ -136,3 +168,11 @@ async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
136
168
  message: `Published releases ${published.join(", ")}`,
137
169
  };
138
170
  }
171
+ /** @deprecated Use runRelease. */
172
+ async function runSimpleRelease(cwd = process.cwd()) {
173
+ return runRelease(cwd);
174
+ }
175
+ /** @deprecated Use runReleaseDetailed. */
176
+ async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
177
+ return runReleaseDetailed(cwd, options);
178
+ }
@@ -5,10 +5,21 @@ exports.isValidVersion = isValidVersion;
5
5
  exports.compareVersions = compareVersions;
6
6
  exports.bumpVersion = bumpVersion;
7
7
  const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/u;
8
+ const COMPAT_DOTTED_PRERELEASE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
9
+ function normalizeVersionInput(version) {
10
+ const trimmed = version.trim();
11
+ const compatMatch = trimmed.match(COMPAT_DOTTED_PRERELEASE_PATTERN);
12
+ if (!compatMatch) {
13
+ return trimmed;
14
+ }
15
+ const [, major, minor, patch, prerelease] = compatMatch;
16
+ return `${major}.${minor}.${patch}-${prerelease}`;
17
+ }
8
18
  function parseVersion(version) {
9
- const match = version.trim().match(SEMVER_PATTERN);
19
+ const normalized = normalizeVersionInput(version);
20
+ const match = normalized.match(SEMVER_PATTERN);
10
21
  if (!match) {
11
- throw new Error(`Invalid version in version file: "${version}". Expected SemVer 2.0.0 format.`);
22
+ throw new Error(`Invalid version in version file: "${version}". Expected SemVer 2.0.0 format (or compatibility form X.Y.Z.W).`);
12
23
  }
13
24
  return {
14
25
  major: Number(match[1]),
@@ -19,7 +30,7 @@ function parseVersion(version) {
19
30
  };
20
31
  }
21
32
  function isValidVersion(version) {
22
- return SEMVER_PATTERN.test(version.trim());
33
+ return SEMVER_PATTERN.test(normalizeVersionInput(version));
23
34
  }
24
35
  function isNumericIdentifier(identifier) {
25
36
  return /^(0|[1-9]\d*)$/u.test(identifier);
@@ -10,7 +10,7 @@ exports.writeBaselineSha = writeBaselineSha;
10
10
  const node_child_process_1 = require("node:child_process");
11
11
  const node_fs_1 = __importDefault(require("node:fs"));
12
12
  const node_path_1 = __importDefault(require("node:path"));
13
- const load_config_js_1 = require("../../config/load-config.js");
13
+ const load_config_js_1 = require("../config/load-config.js");
14
14
  const MANIFEST_VERSION_KEY = "manifest-version";
15
15
  const BASELINE_SHA_KEY = "baseline-sha";
16
16
  const RELEASE_TARGETS_KEY = "release-targets";
@@ -7,8 +7,8 @@ exports.verifyProject = verifyProject;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const load_config_js_1 = require("../config/load-config.js");
10
- const package_context_js_1 = require("../domain/strategy/package-context.js");
11
- const resolve_js_1 = require("../domain/strategy/resolve.js");
10
+ const package_context_js_1 = require("../strategy/package-context.js");
11
+ const resolve_js_1 = require("../strategy/resolve.js");
12
12
  function verifyProject(cwd = process.cwd()) {
13
13
  const checks = [];
14
14
  const config = (0, load_config_js_1.loadConfig)(cwd);
@@ -0,0 +1,2 @@
1
+ import type { ScmClient } from "./types.js";
2
+ export declare function getScmClient(): ScmClient;
@@ -0,0 +1,7 @@
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)();
7
+ }
@@ -1,2 +1,3 @@
1
1
  import type { VersionaryPluginRuntime } from "../types/plugins.js";
2
- export declare function createGitHubPlugin(): VersionaryPluginRuntime;
2
+ import type { ScmClient } from "./types.js";
3
+ export declare function createGitHubPlugin(): VersionaryPluginRuntime & ScmClient;
@@ -90,6 +90,7 @@ function createGitHubPlugin() {
90
90
  return {
91
91
  name: "github",
92
92
  capabilities: ["scm.reviewRequest", "scm.releaseMetadata"],
93
+ provider: "github",
93
94
  async createOrUpdateReviewRequest(input, _context) {
94
95
  const repo = getRepoFromEnv();
95
96
  const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
@@ -194,6 +195,7 @@ function createGitHubPlugin() {
194
195
  tag_name: input.tag,
195
196
  name: input.tag,
196
197
  body: input.notes,
198
+ draft: input.draft ?? false,
197
199
  });
198
200
  data = response.data;
199
201
  }
@@ -0,0 +1,37 @@
1
+ export type ScmProvider = "github";
2
+ export interface ScmClientContext {
3
+ cwd: string;
4
+ logger?: {
5
+ info: (message: string) => void;
6
+ warn: (message: string) => void;
7
+ error: (message: string) => void;
8
+ };
9
+ }
10
+ export interface ScmReviewRequestInput {
11
+ baseBranch: string;
12
+ headBranch: string;
13
+ title: string;
14
+ body: string;
15
+ labels?: string[];
16
+ }
17
+ export interface ScmReviewRequestResult {
18
+ id: string;
19
+ number?: number;
20
+ url: string;
21
+ state: "open" | "closed" | "merged";
22
+ }
23
+ export interface ScmReleaseMetadataInput {
24
+ tag: string;
25
+ version: string;
26
+ notes: string;
27
+ draft?: boolean;
28
+ }
29
+ export interface ScmReleaseMetadataResult {
30
+ url: string;
31
+ status?: "created" | "exists";
32
+ }
33
+ export interface ScmClient {
34
+ provider: ScmProvider;
35
+ createOrUpdateReviewRequest: (input: ScmReviewRequestInput, context: ScmClientContext) => Promise<ScmReviewRequestResult>;
36
+ createReleaseMetadata: (input: ScmReleaseMetadataInput, context: ScmClientContext) => Promise<ScmReleaseMetadataResult>;
37
+ }
@@ -64,4 +64,17 @@ exports.nodeVersionStrategy = {
64
64
  ];
65
65
  return updatedFiles;
66
66
  },
67
+ readPackageName(cwd, config) {
68
+ const versionFile = this.getVersionFile(config);
69
+ const versionPath = node_path_1.default.join(cwd, versionFile);
70
+ if (!node_fs_1.default.existsSync(versionPath)) {
71
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
72
+ }
73
+ const packageJson = readJsonFile(versionPath);
74
+ const name = packageJson.name;
75
+ if (typeof name !== "string" || name.trim().length === 0) {
76
+ return null;
77
+ }
78
+ return name.trim();
79
+ },
67
80
  };
@@ -1,4 +1,4 @@
1
- import type { VersionaryConfig, VersionaryPackage } from "../../types/config.js";
1
+ import type { VersionaryConfig, VersionaryPackage } from "../types/config.js";
2
2
  import type { VersionStrategy } from "./types.js";
3
3
  export declare function resolvePackageStrategyContext(rootConfig: VersionaryConfig, packagePath: string, packageConfig: VersionaryPackage): {
4
4
  strategy: VersionStrategy;
@@ -34,7 +34,7 @@ function resolvePackageStrategyContext(rootConfig, packagePath, packageConfig) {
34
34
  }));
35
35
  const config = withVersionFile({
36
36
  ...baseConfig,
37
- packages: undefined,
37
+ packages: packagePath === "." ? baseConfig.packages : undefined,
38
38
  }, versionFile);
39
39
  return {
40
40
  strategy: baseStrategy,
@@ -50,7 +50,7 @@ function resolvePackageStrategyContext(rootConfig, packagePath, packageConfig) {
50
50
  }));
51
51
  const config = withVersionFile({
52
52
  ...baseConfig,
53
- packages: undefined,
53
+ packages: packagePath === "." ? baseConfig.packages : undefined,
54
54
  }, packageVersionFile);
55
55
  const strategy = (0, resolve_js_1.resolveVersionStrategy)(config);
56
56
  return {
@@ -43,4 +43,18 @@ exports.rVersionStrategy = {
43
43
  node_fs_1.default.writeFileSync(versionPath, updated, "utf8");
44
44
  return [versionFile];
45
45
  },
46
+ readPackageName(cwd, config) {
47
+ const versionFile = this.getVersionFile(config);
48
+ const versionPath = node_path_1.default.join(cwd, versionFile);
49
+ if (!node_fs_1.default.existsSync(versionPath)) {
50
+ throw new Error(`Versionary requires ${versionFile} to exist.`);
51
+ }
52
+ const content = node_fs_1.default.readFileSync(versionPath, "utf8");
53
+ const match = content.match(/^Package:\s*(.+)\s*$/mu);
54
+ if (!match?.[1]) {
55
+ return null;
56
+ }
57
+ const name = match[1].trim();
58
+ return name.length > 0 ? name : null;
59
+ },
46
60
  };
@@ -1,3 +1,4 @@
1
- import type { VersionaryConfig } from "../../types/config.js";
1
+ import type { VersionaryConfig } from "../types/config.js";
2
2
  import type { VersionStrategy } from "./types.js";
3
+ export declare function listKnownReleaseTypes(): string[];
3
4
  export declare function resolveVersionStrategy(config: VersionaryConfig): VersionStrategy;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listKnownReleaseTypes = listKnownReleaseTypes;
4
+ exports.resolveVersionStrategy = resolveVersionStrategy;
5
+ const node_js_1 = require("./node.js");
6
+ const r_js_1 = require("./r.js");
7
+ const rust_js_1 = require("./rust.js");
8
+ const simple_js_1 = require("./simple.js");
9
+ const strategyRegistry = {
10
+ simple: simple_js_1.simpleVersionStrategy,
11
+ node: node_js_1.nodeVersionStrategy,
12
+ rust: rust_js_1.rustVersionStrategy,
13
+ r: r_js_1.rVersionStrategy,
14
+ };
15
+ function listKnownReleaseTypes() {
16
+ return Object.keys(strategyRegistry).sort((a, b) => a.localeCompare(b));
17
+ }
18
+ function resolveVersionStrategy(config) {
19
+ const releaseType = config["release-type"] ?? "simple";
20
+ return strategyRegistry[releaseType] ?? simple_js_1.simpleVersionStrategy;
21
+ }
@@ -613,4 +613,46 @@ exports.rustVersionStrategy = {
613
613
  }
614
614
  return updatedFiles.sort((a, b) => a.localeCompare(b));
615
615
  },
616
+ readPackageName(cwd, config) {
617
+ const versionFile = this.getVersionFile(config);
618
+ const manifests = collectRustTargetManifests(cwd, versionFile, !config.packages);
619
+ const selectedManifest = manifests[0];
620
+ if (!selectedManifest) {
621
+ throw new Error(`Configured rust target "${versionFile}" did not resolve to a Rust crate manifest.`);
622
+ }
623
+ const versionPath = node_path_1.default.join(cwd, selectedManifest);
624
+ if (!node_fs_1.default.existsSync(versionPath)) {
625
+ throw new Error(`Versionary requires ${selectedManifest} to exist.`);
626
+ }
627
+ const cargoTomlRaw = node_fs_1.default.readFileSync(versionPath, "utf8");
628
+ if (!isCrateManifest(selectedManifest, cargoTomlRaw)) {
629
+ return null;
630
+ }
631
+ return readCargoPackageName(cargoTomlRaw, selectedManifest);
632
+ },
633
+ propagateDependentPatchImpacts(cwd, packages) {
634
+ const manifestToVersion = {};
635
+ const candidateManifests = [];
636
+ const manifestToPath = new Map();
637
+ for (const pkg of packages) {
638
+ const manifest = pkg.versionFile;
639
+ candidateManifests.push(manifest);
640
+ manifestToPath.set(manifest, pkg.packagePath);
641
+ if (pkg.nextVersion) {
642
+ manifestToVersion[manifest] = pkg.nextVersion;
643
+ }
644
+ }
645
+ const impactedManifests = detectRustDependencyImpact(cwd, manifestToVersion, candidateManifests);
646
+ return impactedManifests
647
+ .map((manifest) => manifestToPath.get(manifest))
648
+ .filter((pkgPath) => Boolean(pkgPath))
649
+ .sort((a, b) => a.localeCompare(b));
650
+ },
651
+ finalizeVersionWrites(cwd, writes) {
652
+ const manifestToVersion = {};
653
+ for (const write of writes) {
654
+ manifestToVersion[write.versionFile] = write.version;
655
+ }
656
+ return applyRustWorkspaceDependencyUpdates(cwd, manifestToVersion);
657
+ },
616
658
  };
@@ -0,0 +1,21 @@
1
+ import type { VersionaryConfig } from "../types/config.js";
2
+ export interface StrategyPackagePlanContext {
3
+ packagePath: string;
4
+ versionFile: string;
5
+ currentVersion: string;
6
+ nextVersion: string | null;
7
+ }
8
+ export interface StrategyVersionWriteContext {
9
+ packagePath: string;
10
+ versionFile: string;
11
+ version: string;
12
+ }
13
+ export interface VersionStrategy {
14
+ name: string;
15
+ getVersionFile(config: VersionaryConfig): string;
16
+ readVersion(cwd: string, config: VersionaryConfig): string;
17
+ writeVersion(cwd: string, config: VersionaryConfig, version: string): string[];
18
+ readPackageName?(cwd: string, config: VersionaryConfig): string | null;
19
+ propagateDependentPatchImpacts?(cwd: string, packages: StrategyPackagePlanContext[]): string[];
20
+ finalizeVersionWrites?(cwd: string, writes: StrategyVersionWriteContext[]): string[];
21
+ }
@@ -1,4 +1,5 @@
1
1
  export type ConfigFileFormat = "jsonc" | "json" | "toml" | "js";
2
+ export type VersionaryChangelogFormat = "markdown-changelog" | "r-news";
2
3
  export interface VersionaryArtifactRule {
3
4
  type: "json" | "toml" | "yaml" | "regex";
4
5
  path: string;
@@ -10,6 +11,7 @@ export interface VersionaryPackage {
10
11
  "release-type"?: string;
11
12
  "package-name"?: string;
12
13
  "changelog-file"?: string;
14
+ "changelog-format"?: VersionaryChangelogFormat;
13
15
  "exclude-paths"?: string[];
14
16
  "extra-files"?: VersionaryArtifactRule[];
15
17
  }
@@ -18,6 +20,8 @@ export interface VersionaryConfig {
18
20
  "review-mode"?: "direct" | "pr" | "review";
19
21
  "version-file"?: string;
20
22
  "changelog-file"?: string;
23
+ "changelog-format"?: VersionaryChangelogFormat;
24
+ "release-draft"?: boolean;
21
25
  "release-branch"?: string;
22
26
  "baseline-file"?: string;
23
27
  "bootstrap-sha"?: string;
@@ -1,37 +1,14 @@
1
+ import type { ScmClientContext, ScmProvider, ScmReleaseMetadataInput, ScmReleaseMetadataResult, ScmReviewRequestInput, ScmReviewRequestResult } from "../scm/types.js";
1
2
  export type VersionaryPluginCapability = "scm.reviewRequest" | "scm.releaseMetadata";
2
- export interface VersionaryScmReviewRequestInput {
3
- baseBranch: string;
4
- headBranch: string;
5
- title: string;
6
- body: string;
7
- labels?: string[];
8
- }
9
- export interface VersionaryScmReviewRequestResult {
10
- id: string;
11
- number?: number;
12
- url: string;
13
- state: "open" | "closed" | "merged";
14
- }
15
- export interface VersionaryScmReleaseMetadataInput {
16
- tag: string;
17
- version: string;
18
- notes: string;
19
- }
20
- export interface VersionaryScmReleaseMetadataResult {
21
- url: string;
22
- status?: "created" | "exists";
23
- }
24
- export interface VersionaryPluginContext {
25
- cwd: string;
26
- logger?: {
27
- info: (message: string) => void;
28
- warn: (message: string) => void;
29
- error: (message: string) => void;
30
- };
31
- }
3
+ export type VersionaryScmReviewRequestInput = ScmReviewRequestInput;
4
+ export type VersionaryScmReviewRequestResult = ScmReviewRequestResult;
5
+ export type VersionaryScmReleaseMetadataInput = ScmReleaseMetadataInput;
6
+ export type VersionaryScmReleaseMetadataResult = ScmReleaseMetadataResult;
7
+ export type VersionaryPluginContext = ScmClientContext;
32
8
  export interface VersionaryPluginRuntime {
33
9
  name: string;
34
10
  capabilities: VersionaryPluginCapability[];
11
+ provider?: ScmProvider;
35
12
  createOrUpdateReviewRequest?: (input: VersionaryScmReviewRequestInput, context: VersionaryPluginContext) => Promise<VersionaryScmReviewRequestResult>;
36
13
  createReleaseMetadata?: (input: VersionaryScmReleaseMetadataInput, context: VersionaryPluginContext) => Promise<VersionaryScmReleaseMetadataResult>;
37
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "versionary",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "Automatic release framework based on conventional commits and semantic versioning",
5
5
  "keywords": [
6
6
  "releasing",
@@ -1,3 +0,0 @@
1
- import type { SimplePlan } from "../../domain/release/plan.js";
2
- import type { VersionaryConfig } from "../../types/config.js";
3
- export declare function applyConfiguredArtifactRules(cwd: string, config: VersionaryConfig, plan: SimplePlan): string[];
@@ -1,2 +0,0 @@
1
- export type { VerifyResult } from "../../verify/verify-project.js";
2
- export { verifyProject } from "../../verify/verify-project.js";
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifyProject = void 0;
4
- var verify_project_js_1 = require("../../verify/verify-project.js");
5
- Object.defineProperty(exports, "verifyProject", { enumerable: true, get: function () { return verify_project_js_1.verifyProject; } });
@@ -1,19 +0,0 @@
1
- import { type ParsedCommit } from "../../infra/git/commits.js";
2
- import type { SimplePlan } from "./plan.js";
3
- export declare function renderSimpleReleaseNotes(input: {
4
- currentVersion: string;
5
- nextVersion: string;
6
- commits: ParsedCommit[];
7
- cwd?: string;
8
- }, options?: {
9
- includeFooter?: boolean;
10
- }): string;
11
- export declare function renderSimpleChangelog(plan: SimplePlan): string;
12
- export declare function renderPackageChangelogSection(input: {
13
- currentVersion: string;
14
- nextVersion: string;
15
- commits: ParsedCommit[];
16
- tagPrefix: string;
17
- cwd?: string;
18
- }): string;
19
- export declare function prependChangelog(cwd: string, changelogFile: string, section: string): void;
@@ -1,21 +0,0 @@
1
- import { type ParsedCommit } from "../../infra/git/commits.js";
2
- import { type ReleaseType } from "./semver.js";
3
- export interface SimplePlan {
4
- mode: "simple";
5
- releaseType: ReleaseType;
6
- currentVersion: string;
7
- nextVersion: string | null;
8
- versionFile: string;
9
- changelogFile: string;
10
- releaseBranchPrefix: string;
11
- baselineSha: string | null;
12
- commits: ParsedCommit[];
13
- packages?: Array<{
14
- path: string;
15
- releaseType: ReleaseType;
16
- currentVersion: string;
17
- nextVersion: string | null;
18
- commits: ParsedCommit[];
19
- }>;
20
- }
21
- export declare function createSimplePlan(cwd?: string): SimplePlan;
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveVersionStrategy = resolveVersionStrategy;
4
- const node_js_1 = require("./node.js");
5
- const r_js_1 = require("./r.js");
6
- const rust_js_1 = require("./rust.js");
7
- const simple_js_1 = require("./simple.js");
8
- function resolveVersionStrategy(config) {
9
- if (config["release-type"] === "node") {
10
- return node_js_1.nodeVersionStrategy;
11
- }
12
- if (config["release-type"] === "rust") {
13
- return rust_js_1.rustVersionStrategy;
14
- }
15
- if (config["release-type"] === "r") {
16
- return r_js_1.rVersionStrategy;
17
- }
18
- return simple_js_1.simpleVersionStrategy;
19
- }
@@ -1,7 +0,0 @@
1
- import type { VersionaryConfig } from "../../types/config.js";
2
- export interface VersionStrategy {
3
- name: string;
4
- getVersionFile(config: VersionaryConfig): string;
5
- readVersion(cwd: string, config: VersionaryConfig): string;
6
- writeVersion(cwd: string, config: VersionaryConfig, version: string): string[];
7
- }
@@ -1 +0,0 @@
1
- export { createGitHubPlugin } from "../../../scm/github-plugin.js";