versionary 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -1,23 +1,55 @@
1
1
  # versionary
2
2
 
3
- Versionary is a software-agnostic automated release tool focused on SemVer, conventional commits, release PR workflows, and extensibility.
3
+ Versionary is a software-agnostic automated release tool focused on SemVer,
4
+ conventional commits, release PR workflows, and extensibility.
4
5
 
5
6
  Configuration is loaded from `versionary.jsonc` by default.
6
7
 
7
- ## Simple mode (MVP)
8
+ Schema URL for editor support:
9
+
10
+ - `https://raw.githubusercontent.com/jolars/versionary/main/schemas/versionary-schema.json`
11
+
12
+ ## Config (manifest style)
8
13
 
9
14
  For a quick trial, use:
10
15
 
11
- - `versionary.jsonc` with `"mode": "simple"`
12
- - `version.txt` as the version source
13
- - `CHANGELOG.md` as release notes output
16
+ - `version-file` (default `version.txt`) as version source
17
+ - `changelog-file` (default `CHANGELOG.md`) as release notes output
18
+ - stable release branch (`release-branch`, default:
19
+ `versionary/release`) so release PRs are updated in-place
20
+ - `baseline-file` (default `.versionary-manifest.json`) tracks baseline SHA for deterministic commit
21
+ ranges independent of tags
22
+ - review mode (`review-mode`): `review` (PR/MR style) or `direct` (no
23
+ review request)
24
+ - optional monorepo planning with `monorepo-mode` and `packages`:
25
+ - `independent` computes package bumps per path
26
+ - `fixed` computes one shared bump across configured package paths
14
27
 
15
28
  Commands:
16
29
 
17
30
  - `pnpm verify`
31
+ - `pnpm run` (default orchestration: no-op, create/update release PR, or publish
32
+ release based on context)
18
33
  - `pnpm plan`
19
34
  - `pnpm changelog -- --write`
20
35
  - `pnpm pr`
36
+ - `pnpm release`
37
+
38
+ `pnpm pr` prepares release commit + branch and opens/updates the review request
39
+ via SCM plugin capability. `pnpm run` is the recommended CI entrypoint and
40
+ auto-dispatches between PR/update and release publish.
41
+
42
+ For first-run bootstrapping, set `bootstrap-sha` (similar to release-please). Subsequent runs use
43
+ the baseline state file.
44
+
45
+ ## Built-in plugins
46
+
47
+ Versionary ships with built-in SCM plugin support:
48
+
49
+ - `github` (default): review request + release metadata
50
+
51
+ Package publication is intentionally out of scope in the current release flow.
52
+ Use separate CI workflows for publishing after Versionary has prepared/tagged the release.
21
53
 
22
54
  ## Install from GitHub
23
55
 
@@ -31,4 +63,5 @@ You can install directly from a git ref:
31
63
  }
32
64
  ```
33
65
 
34
- The package runs a `prepare` build during git installation so the `versionary` CLI binary is available after `pnpm install`.
66
+ The package runs a `prepare` build during git installation so the `versionary`
67
+ CLI binary is available after `pnpm install`.
package/dist/cli/index.js CHANGED
@@ -6,9 +6,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
+ const node_child_process_1 = require("node:child_process");
9
10
  const plan_js_1 = require("../simple/plan.js");
10
11
  const changelog_js_1 = require("../simple/changelog.js");
11
12
  const pr_js_1 = require("../simple/pr.js");
13
+ const release_js_1 = require("../simple/release.js");
12
14
  const verify_project_js_1 = require("../verify/verify-project.js");
13
15
  function printVerifyResult() {
14
16
  const result = (0, verify_project_js_1.verifyProject)();
@@ -18,8 +20,31 @@ function printVerifyResult() {
18
20
  }
19
21
  return result.ok ? 0 : 1;
20
22
  }
21
- function main() {
23
+ async function main() {
22
24
  const [, , command, ...args] = process.argv;
25
+ if (!command || command === "run") {
26
+ const subject = (0, node_child_process_1.execFileSync)("git", ["log", "-1", "--pretty=%s"], {
27
+ encoding: "utf8",
28
+ stdio: ["ignore", "pipe", "ignore"],
29
+ }).trim();
30
+ if ((0, pr_js_1.isReleaseCommitMessage)(subject)) {
31
+ const message = await (0, release_js_1.runSimpleRelease)(process.cwd());
32
+ console.log(message);
33
+ return 0;
34
+ }
35
+ const plan = (0, plan_js_1.createSimplePlan)();
36
+ if (!plan.nextVersion) {
37
+ console.log("No releasable commits found. Nothing to do.");
38
+ return 0;
39
+ }
40
+ const pr = (0, pr_js_1.prepareSimpleReleasePr)();
41
+ (0, pr_js_1.pushReleaseBranch)(process.cwd(), pr.branch);
42
+ const reviewResult = await (0, pr_js_1.openOrUpdateSimpleReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.commits);
43
+ console.log(`Prepared release PR branch ${pr.branch}`);
44
+ console.log(`Title: ${pr.title}`);
45
+ console.log(reviewResult);
46
+ return 0;
47
+ }
23
48
  if (command === "verify") {
24
49
  return printVerifyResult();
25
50
  }
@@ -50,16 +75,32 @@ function main() {
50
75
  }
51
76
  if (command === "pr") {
52
77
  const pr = (0, pr_js_1.prepareSimpleReleasePr)();
78
+ (0, pr_js_1.pushReleaseBranch)(process.cwd(), pr.branch);
79
+ const reviewResult = await (0, pr_js_1.openOrUpdateSimpleReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.commits);
53
80
  console.log(`Prepared release PR branch ${pr.branch}`);
54
81
  console.log(`Title: ${pr.title}`);
82
+ console.log(reviewResult);
83
+ return 0;
84
+ }
85
+ if (command === "release") {
86
+ const message = await (0, release_js_1.runSimpleRelease)(process.cwd());
87
+ console.log(message);
55
88
  return 0;
56
89
  }
57
90
  console.log("Usage: versionary <command>");
58
91
  console.log("Commands:");
92
+ console.log(" run Auto-dispatch release PR/update or release publish by context");
59
93
  console.log(" verify Validate config and basic repository shape");
60
94
  console.log(" plan Print release plan (simple mode)");
61
95
  console.log(" changelog [--write] Print or write changelog section");
62
96
  console.log(" pr Prepare release PR commit and branch");
97
+ console.log(" release Publish release metadata for release commit context");
63
98
  return 1;
64
99
  }
65
- process.exit(main());
100
+ main()
101
+ .then((code) => process.exit(code))
102
+ .catch((error) => {
103
+ const message = error instanceof Error ? error.message : String(error);
104
+ console.error(message);
105
+ process.exit(1);
106
+ });
@@ -29,6 +29,9 @@ function parseConfig(raw, format) {
29
29
  }
30
30
  throw new Error("JavaScript config loading is not implemented yet. Use JSONC/JSON/TOML for now.");
31
31
  }
32
+ function isRecord(value) {
33
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
34
+ }
32
35
  function findConfigFile(cwd) {
33
36
  for (const candidate of SUPPORTED_FILES) {
34
37
  const candidatePath = node_path_1.default.join(cwd, candidate.file);
@@ -45,6 +48,9 @@ function loadConfig(cwd = process.cwd()) {
45
48
  }
46
49
  const raw = node_fs_1.default.readFileSync(found.path, "utf8");
47
50
  const parsed = parseConfig(raw, found.format);
51
+ if (!isRecord(parsed)) {
52
+ throw new Error("Invalid config: expected an object at the root.");
53
+ }
48
54
  const validated = schema_js_1.configSchema.parse(parsed);
49
55
  return {
50
56
  path: found.path,
@@ -1,99 +1,38 @@
1
1
  import { z } from "zod";
2
2
  export declare const configSchema: z.ZodObject<{
3
3
  version: z.ZodLiteral<1>;
4
- mode: z.ZodOptional<z.ZodEnum<{
5
- simple: "simple";
6
- standard: "standard";
4
+ "review-mode": z.ZodOptional<z.ZodEnum<{
5
+ direct: "direct";
6
+ review: "review";
7
7
  }>>;
8
- history: z.ZodOptional<z.ZodObject<{
9
- bootstrap: z.ZodOptional<z.ZodObject<{
10
- sha: z.ZodOptional<z.ZodString>;
11
- tag: z.ZodOptional<z.ZodString>;
12
- }, z.core.$strip>>;
13
- }, z.core.$strip>>;
14
- monorepo: z.ZodOptional<z.ZodObject<{
15
- mode: z.ZodDefault<z.ZodEnum<{
16
- independent: "independent";
17
- fixed: "fixed";
18
- }>>;
19
- }, z.core.$strip>>;
20
- defaults: z.ZodOptional<z.ZodObject<{
21
- strategy: z.ZodOptional<z.ZodString>;
22
- versioning: z.ZodOptional<z.ZodObject<{
23
- bumpMinorPreMajor: z.ZodOptional<z.ZodBoolean>;
24
- }, z.core.$strip>>;
25
- changelog: z.ZodOptional<z.ZodObject<{
26
- includeAuthors: z.ZodOptional<z.ZodBoolean>;
27
- }, z.core.$strip>>;
28
- commitConventions: z.ZodOptional<z.ZodObject<{
29
- preset: z.ZodDefault<z.ZodEnum<{
30
- conventional: "conventional";
31
- angular: "angular";
32
- custom: "custom";
33
- }>>;
34
- }, z.core.$strip>>;
35
- }, z.core.$strip>>;
36
- packages: z.ZodOptional<z.ZodArray<z.ZodObject<{
37
- path: z.ZodString;
38
- strategy: z.ZodOptional<z.ZodString>;
39
- packageName: z.ZodOptional<z.ZodString>;
40
- excludePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
- artifacts: z.ZodOptional<z.ZodArray<z.ZodObject<{
42
- file: z.ZodString;
43
- format: z.ZodEnum<{
8
+ "version-file": z.ZodOptional<z.ZodString>;
9
+ "changelog-file": z.ZodOptional<z.ZodString>;
10
+ "release-branch": z.ZodOptional<z.ZodString>;
11
+ "baseline-file": z.ZodOptional<z.ZodString>;
12
+ "bootstrap-sha": z.ZodOptional<z.ZodString>;
13
+ "monorepo-mode": z.ZodOptional<z.ZodEnum<{
14
+ independent: "independent";
15
+ fixed: "fixed";
16
+ }>>;
17
+ "bump-minor-pre-major": z.ZodOptional<z.ZodBoolean>;
18
+ "include-commit-authors": z.ZodOptional<z.ZodBoolean>;
19
+ "release-type": z.ZodOptional<z.ZodString>;
20
+ packages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
21
+ "release-type": z.ZodOptional<z.ZodString>;
22
+ "package-name": z.ZodOptional<z.ZodString>;
23
+ "exclude-paths": z.ZodOptional<z.ZodArray<z.ZodString>>;
24
+ "extra-files": z.ZodOptional<z.ZodArray<z.ZodObject<{
25
+ type: z.ZodEnum<{
44
26
  json: "json";
45
27
  toml: "toml";
46
28
  yaml: "yaml";
47
29
  regex: "regex";
48
30
  }>;
49
- path: z.ZodOptional<z.ZodString>;
31
+ path: z.ZodString;
32
+ jsonpath: z.ZodOptional<z.ZodString>;
50
33
  pattern: z.ZodOptional<z.ZodString>;
51
34
  }, z.core.$strip>>>;
52
35
  }, z.core.$strip>>>;
53
- pluginConfig: z.ZodOptional<z.ZodObject<{
54
- extends: z.ZodOptional<z.ZodArray<z.ZodObject<{
55
- name: z.ZodString;
56
- }, z.core.$strip>>>;
57
- globalOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
58
- execution: z.ZodOptional<z.ZodArray<z.ZodObject<{
59
- step: z.ZodEnum<{
60
- verifyConditions: "verifyConditions";
61
- analyzeCommits: "analyzeCommits";
62
- resolveReverts: "resolveReverts";
63
- verifyRelease: "verifyRelease";
64
- generateNotes: "generateNotes";
65
- updateArtifacts: "updateArtifacts";
66
- preparePr: "preparePr";
67
- publish: "publish";
68
- postRelease: "postRelease";
69
- success: "success";
70
- fail: "fail";
71
- }>;
72
- lifecycle: z.ZodOptional<z.ZodArray<z.ZodEnum<{
73
- plan: "plan";
74
- pr: "pr";
75
- release: "release";
76
- }>>>;
77
- merge: z.ZodOptional<z.ZodEnum<{
78
- highest: "highest";
79
- concat: "concat";
80
- override: "override";
81
- reduce: "reduce";
82
- }>>;
83
- }, z.core.$strip>>>;
84
- plugins: z.ZodOptional<z.ZodArray<z.ZodObject<{
85
- name: z.ZodString;
86
- options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
87
- }, z.core.$strip>>>;
88
- }, z.core.$strip>>;
89
- plugins: z.ZodOptional<z.ZodArray<z.ZodObject<{
90
- name: z.ZodString;
91
- options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
92
- }, z.core.$strip>>>;
93
- simple: z.ZodOptional<z.ZodObject<{
94
- versionFile: z.ZodOptional<z.ZodString>;
95
- changelogFile: z.ZodOptional<z.ZodString>;
96
- releaseBranchPrefix: z.ZodOptional<z.ZodString>;
97
- }, z.core.$strip>>;
36
+ plugins: z.ZodOptional<z.ZodArray<z.ZodString>>;
98
37
  }, z.core.$strip>;
99
38
  export type ConfigSchema = z.infer<typeof configSchema>;
@@ -3,91 +3,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.configSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const artifactRuleSchema = zod_1.z.object({
6
- file: zod_1.z.string().min(1),
7
- format: zod_1.z.enum(["json", "toml", "yaml", "regex"]),
8
- path: zod_1.z.string().optional(),
6
+ type: zod_1.z.enum(["json", "toml", "yaml", "regex"]),
7
+ path: zod_1.z.string().min(1),
8
+ jsonpath: zod_1.z.string().optional(),
9
9
  pattern: zod_1.z.string().optional(),
10
10
  });
11
11
  const packageSchema = zod_1.z.object({
12
- path: zod_1.z.string().min(1),
13
- strategy: zod_1.z.string().optional(),
14
- packageName: zod_1.z.string().optional(),
15
- excludePaths: zod_1.z.array(zod_1.z.string()).optional(),
16
- artifacts: zod_1.z.array(artifactRuleSchema).optional(),
17
- });
18
- const pluginSchema = zod_1.z.object({
19
- name: zod_1.z.string().min(1),
20
- options: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
21
- });
22
- const pluginExecutionSchema = zod_1.z.object({
23
- step: zod_1.z.enum([
24
- "verifyConditions",
25
- "analyzeCommits",
26
- "resolveReverts",
27
- "verifyRelease",
28
- "generateNotes",
29
- "updateArtifacts",
30
- "preparePr",
31
- "publish",
32
- "postRelease",
33
- "success",
34
- "fail",
35
- ]),
36
- lifecycle: zod_1.z.array(zod_1.z.enum(["plan", "pr", "release"])).optional(),
37
- merge: zod_1.z.enum(["highest", "concat", "override", "reduce"]).optional(),
38
- });
39
- const pluginConfigSchema = zod_1.z.object({
40
- extends: zod_1.z.array(zod_1.z.object({ name: zod_1.z.string().min(1) })).optional(),
41
- globalOptions: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
42
- execution: zod_1.z.array(pluginExecutionSchema).optional(),
43
- plugins: zod_1.z.array(pluginSchema).optional(),
12
+ "release-type": zod_1.z.string().optional(),
13
+ "package-name": zod_1.z.string().optional(),
14
+ "exclude-paths": zod_1.z.array(zod_1.z.string()).optional(),
15
+ "extra-files": zod_1.z.array(artifactRuleSchema).optional(),
44
16
  });
45
17
  exports.configSchema = zod_1.z.object({
46
18
  version: zod_1.z.literal(1),
47
- mode: zod_1.z.enum(["simple", "standard"]).optional(),
48
- history: zod_1.z
49
- .object({
50
- bootstrap: zod_1.z
51
- .object({
52
- sha: zod_1.z.string().optional(),
53
- tag: zod_1.z.string().optional(),
54
- })
55
- .optional(),
56
- })
57
- .optional(),
58
- monorepo: zod_1.z
59
- .object({
60
- mode: zod_1.z.enum(["independent", "fixed"]).default("independent"),
61
- })
62
- .optional(),
63
- defaults: zod_1.z
64
- .object({
65
- strategy: zod_1.z.string().optional(),
66
- versioning: zod_1.z
67
- .object({
68
- bumpMinorPreMajor: zod_1.z.boolean().optional(),
69
- })
70
- .optional(),
71
- changelog: zod_1.z
72
- .object({
73
- includeAuthors: zod_1.z.boolean().optional(),
74
- })
75
- .optional(),
76
- commitConventions: zod_1.z
77
- .object({
78
- preset: zod_1.z.enum(["conventional", "angular", "custom"]).default("conventional"),
79
- })
80
- .optional(),
81
- })
82
- .optional(),
83
- packages: zod_1.z.array(packageSchema).optional(),
84
- pluginConfig: pluginConfigSchema.optional(),
85
- plugins: zod_1.z.array(pluginSchema).optional(),
86
- simple: zod_1.z
87
- .object({
88
- versionFile: zod_1.z.string().optional(),
89
- changelogFile: zod_1.z.string().optional(),
90
- releaseBranchPrefix: zod_1.z.string().optional(),
91
- })
92
- .optional(),
19
+ "review-mode": zod_1.z.enum(["direct", "review"]).optional(),
20
+ "version-file": zod_1.z.string().optional(),
21
+ "changelog-file": zod_1.z.string().optional(),
22
+ "release-branch": zod_1.z.string().optional(),
23
+ "baseline-file": zod_1.z.string().optional(),
24
+ "bootstrap-sha": zod_1.z.string().optional(),
25
+ "monorepo-mode": zod_1.z.enum(["independent", "fixed"]).optional(),
26
+ "bump-minor-pre-major": zod_1.z.boolean().optional(),
27
+ "include-commit-authors": zod_1.z.boolean().optional(),
28
+ "release-type": zod_1.z.string().optional(),
29
+ packages: zod_1.z.record(zod_1.z.string().min(1), packageSchema).optional(),
30
+ plugins: zod_1.z.array(zod_1.z.string().min(1)).optional(),
93
31
  });
package/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
- export type { VersionaryConfig, VersionaryPackage, VersionaryPluginRef, VersionaryArtifactRule, } from "./types/config.js";
1
+ export type { VersionaryConfig, VersionaryPackage, VersionaryArtifactRule, } from "./types/config.js";
2
+ export type { VersionaryPluginCapability, VersionaryPluginContext, VersionaryPluginRuntime, VersionaryScmReviewRequestInput, VersionaryScmReviewRequestResult, VersionaryScmReleaseMetadataInput, VersionaryScmReleaseMetadataResult, } from "./types/plugins.js";
2
3
  export { loadConfig } from "./config/load-config.js";
4
+ export { findPluginsByCapability, pluginHasCapability } from "./plugins/capabilities.js";
5
+ export { loadRuntimePlugins } from "./plugins/runtime.js";
3
6
  export { verifyProject } from "./verify/verify-project.js";
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.verifyProject = exports.loadConfig = void 0;
3
+ exports.verifyProject = exports.loadRuntimePlugins = exports.pluginHasCapability = exports.findPluginsByCapability = exports.loadConfig = void 0;
4
4
  var load_config_js_1 = require("./config/load-config.js");
5
5
  Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return load_config_js_1.loadConfig; } });
6
+ var capabilities_js_1 = require("./plugins/capabilities.js");
7
+ Object.defineProperty(exports, "findPluginsByCapability", { enumerable: true, get: function () { return capabilities_js_1.findPluginsByCapability; } });
8
+ Object.defineProperty(exports, "pluginHasCapability", { enumerable: true, get: function () { return capabilities_js_1.pluginHasCapability; } });
9
+ var runtime_js_1 = require("./plugins/runtime.js");
10
+ Object.defineProperty(exports, "loadRuntimePlugins", { enumerable: true, get: function () { return runtime_js_1.loadRuntimePlugins; } });
6
11
  var verify_project_js_1 = require("./verify/verify-project.js");
7
12
  Object.defineProperty(exports, "verifyProject", { enumerable: true, get: function () { return verify_project_js_1.verifyProject; } });
@@ -0,0 +1,3 @@
1
+ import type { VersionaryPluginCapability, VersionaryPluginRuntime } from "../types/plugins.js";
2
+ export declare function pluginHasCapability(plugin: VersionaryPluginRuntime, capability: VersionaryPluginCapability): boolean;
3
+ export declare function findPluginsByCapability(plugins: VersionaryPluginRuntime[], capability: VersionaryPluginCapability): VersionaryPluginRuntime[];
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pluginHasCapability = pluginHasCapability;
4
+ exports.findPluginsByCapability = findPluginsByCapability;
5
+ function pluginHasCapability(plugin, capability) {
6
+ return plugin.capabilities.includes(capability);
7
+ }
8
+ function findPluginsByCapability(plugins, capability) {
9
+ return plugins.filter((plugin) => pluginHasCapability(plugin, capability));
10
+ }
@@ -0,0 +1,2 @@
1
+ import type { VersionaryPluginRuntime } from "../types/plugins.js";
2
+ export declare function loadRuntimePlugins(cwd?: string): VersionaryPluginRuntime[];
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadRuntimePlugins = loadRuntimePlugins;
4
+ const load_config_js_1 = require("../config/load-config.js");
5
+ const github_plugin_js_1 = require("../scm/github-plugin.js");
6
+ const BUILTIN_PLUGIN_FACTORIES = {
7
+ github: github_plugin_js_1.createGitHubPlugin,
8
+ };
9
+ function loadRuntimePlugins(cwd = process.cwd()) {
10
+ const loaded = (0, load_config_js_1.loadConfig)(cwd);
11
+ const configured = loaded.config.plugins ?? [];
12
+ const plugins = [(0, github_plugin_js_1.createGitHubPlugin)()];
13
+ const seen = new Set(plugins.map((plugin) => plugin.name));
14
+ for (const name of configured) {
15
+ const factory = BUILTIN_PLUGIN_FACTORIES[name];
16
+ if (!factory || seen.has(name)) {
17
+ continue;
18
+ }
19
+ const plugin = factory();
20
+ plugins.push(plugin);
21
+ seen.add(plugin.name);
22
+ }
23
+ return plugins;
24
+ }
@@ -0,0 +1,2 @@
1
+ import type { VersionaryPluginRuntime } from "../types/plugins.js";
2
+ export declare function createGitHubPlugin(): VersionaryPluginRuntime;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGitHubPlugin = createGitHubPlugin;
4
+ const rest_1 = require("@octokit/rest");
5
+ function parseRepoSlug(input) {
6
+ const [owner, repo] = input.split("/");
7
+ if (!owner || !repo) {
8
+ throw new Error(`Invalid repo slug "${input}". Expected "owner/repo".`);
9
+ }
10
+ return { owner, repo };
11
+ }
12
+ function getRepoFromEnv() {
13
+ const slug = process.env.GITHUB_REPOSITORY;
14
+ if (!slug) {
15
+ throw new Error("Missing GITHUB_REPOSITORY environment variable.");
16
+ }
17
+ return parseRepoSlug(slug);
18
+ }
19
+ function getGitHubToken() {
20
+ const token = process.env.VERSIONARY_PR_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN;
21
+ if (!token) {
22
+ throw new Error("Missing GitHub token. Set VERSIONARY_PR_TOKEN, GH_TOKEN, or GITHUB_TOKEN.");
23
+ }
24
+ return token;
25
+ }
26
+ async function ensureLabels(octokit, repo, pullNumber, labels) {
27
+ if (labels.length === 0) {
28
+ return;
29
+ }
30
+ try {
31
+ await octokit.issues.addLabels({
32
+ owner: repo.owner,
33
+ repo: repo.repo,
34
+ issue_number: pullNumber,
35
+ labels,
36
+ });
37
+ }
38
+ catch {
39
+ // Best effort: label may not exist or permissions may be restricted.
40
+ }
41
+ }
42
+ function createGitHubPlugin() {
43
+ return {
44
+ name: "github",
45
+ capabilities: ["scm.reviewRequest", "scm.releaseMetadata"],
46
+ async createOrUpdateReviewRequest(input, _context) {
47
+ const repo = getRepoFromEnv();
48
+ const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
49
+ const { data: existing } = await octokit.pulls.list({
50
+ owner: repo.owner,
51
+ repo: repo.repo,
52
+ state: "open",
53
+ head: `${repo.owner}:${input.headBranch}`,
54
+ base: input.baseBranch,
55
+ per_page: 1,
56
+ });
57
+ if (existing.length > 0) {
58
+ const pr = existing[0];
59
+ const { data: updated } = await octokit.pulls.update({
60
+ owner: repo.owner,
61
+ repo: repo.repo,
62
+ pull_number: pr.number,
63
+ title: input.title,
64
+ body: input.body,
65
+ });
66
+ await ensureLabels(octokit, repo, updated.number, input.labels ?? []);
67
+ return {
68
+ id: String(updated.id),
69
+ number: updated.number,
70
+ url: updated.html_url,
71
+ state: updated.state === "open" ? "open" : "closed",
72
+ };
73
+ }
74
+ const { data: created } = await octokit.pulls.create({
75
+ owner: repo.owner,
76
+ repo: repo.repo,
77
+ title: input.title,
78
+ head: input.headBranch,
79
+ base: input.baseBranch,
80
+ body: input.body,
81
+ });
82
+ await ensureLabels(octokit, repo, created.number, input.labels ?? []);
83
+ return {
84
+ id: String(created.id),
85
+ number: created.number,
86
+ url: created.html_url,
87
+ state: created.state === "open" ? "open" : "closed",
88
+ };
89
+ },
90
+ async createReleaseMetadata(input, _context) {
91
+ const repo = getRepoFromEnv();
92
+ const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
93
+ try {
94
+ const existing = await octokit.repos.getReleaseByTag({
95
+ owner: repo.owner,
96
+ repo: repo.repo,
97
+ tag: input.tag,
98
+ });
99
+ return { url: existing.data.html_url };
100
+ }
101
+ catch {
102
+ // Continue with create flow when release does not exist.
103
+ }
104
+ const { data } = await octokit.repos.createRelease({
105
+ owner: repo.owner,
106
+ repo: repo.repo,
107
+ tag_name: input.tag,
108
+ name: input.tag,
109
+ body: input.notes,
110
+ });
111
+ return { url: data.html_url };
112
+ },
113
+ };
114
+ }
@@ -7,6 +7,8 @@ exports.renderSimpleChangelog = renderSimpleChangelog;
7
7
  exports.prependChangelog = prependChangelog;
8
8
  const node_fs_1 = __importDefault(require("node:fs"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
+ const git_js_1 = require("./git.js");
11
+ const repo_url_js_1 = require("./repo-url.js");
10
12
  function formatDate() {
11
13
  return new Date().toISOString().slice(0, 10);
12
14
  }
@@ -14,10 +16,22 @@ function renderSimpleChangelog(plan) {
14
16
  if (!plan.nextVersion) {
15
17
  return "";
16
18
  }
19
+ const repoUrl = (0, repo_url_js_1.resolveRepositoryWebBaseUrl)(process.cwd());
20
+ const versionHeading = repoUrl
21
+ ? `## [${plan.nextVersion}](${repoUrl}/compare/v${plan.currentVersion}...v${plan.nextVersion}) (${formatDate()})`
22
+ : `## ${plan.nextVersion} - ${formatDate()}`;
17
23
  const lines = [
18
- `## ${plan.nextVersion} - ${formatDate()}`,
24
+ versionHeading,
19
25
  "",
20
- ...plan.commits.map((commit) => `- ${commit.subject} (${commit.hash.slice(0, 7)})`),
26
+ ...plan.commits
27
+ .filter((commit) => (0, git_js_1.isReleasableCommit)(commit.subject))
28
+ .map((commit) => {
29
+ const short = commit.hash.slice(0, 7);
30
+ if (!repoUrl) {
31
+ return `- ${commit.subject} (\`${short}\`)`;
32
+ }
33
+ return `- ${commit.subject} ([\`${short}\`](${repoUrl}/commit/${commit.hash}))`;
34
+ }),
21
35
  "",
22
36
  ];
23
37
  return lines.join("\n");
@@ -3,5 +3,8 @@ export interface CommitInfo {
3
3
  hash: string;
4
4
  subject: string;
5
5
  }
6
- export declare function getCommitsSinceLastTag(cwd?: string): CommitInfo[];
6
+ export declare function getCommitsSinceLastTag(cwd?: string, baselineSha?: string | null): CommitInfo[];
7
+ export declare function getCommitsForPath(cwd?: string, baselineSha?: string | null, packagePath?: string, excludePaths?: string[]): CommitInfo[];
8
+ export declare function inferReleaseTypeFromSubject(subject: string): ReleaseType;
9
+ export declare function isReleasableCommit(subject: string): boolean;
7
10
  export declare function analyzeCommits(commits: CommitInfo[]): ReleaseType;