versionary 0.17.0 → 0.18.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/dist/cli/index.js +13 -5
- package/dist/git/commits.js +10 -1
- package/dist/release/changelog.js +2 -1
- package/dist/release/pr.d.ts +7 -0
- package/dist/release/pr.js +27 -0
- package/dist/scm/github-plugin.js +62 -0
- package/dist/scm/types.d.ts +11 -0
- package/package.json +3 -3
package/dist/cli/index.js
CHANGED
|
@@ -99,6 +99,9 @@ async function main() {
|
|
|
99
99
|
}
|
|
100
100
|
const plan = (0, plan_js_1.createReleasePlan)();
|
|
101
101
|
if (!plan.nextVersion) {
|
|
102
|
+
if (!flags["dry-run"]) {
|
|
103
|
+
await (0, pr_js_1.closeStaleReviewRequestIfExists)(process.cwd(), { logger });
|
|
104
|
+
}
|
|
102
105
|
const message = "No releasable commits found. Nothing to do.";
|
|
103
106
|
if (flags.json) {
|
|
104
107
|
emitJson({
|
|
@@ -203,12 +206,17 @@ async function main() {
|
|
|
203
206
|
return 0;
|
|
204
207
|
}
|
|
205
208
|
if (command === "pr") {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (!
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
const plan = (0, plan_js_1.createReleasePlan)();
|
|
210
|
+
if (!plan.nextVersion) {
|
|
211
|
+
if (!flags["dry-run"]) {
|
|
212
|
+
await (0, pr_js_1.closeStaleReviewRequestIfExists)(process.cwd(), {
|
|
213
|
+
logger: console,
|
|
214
|
+
});
|
|
211
215
|
}
|
|
216
|
+
console.log("No releasable commits found. Nothing to do.");
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
if (flags["dry-run"]) {
|
|
212
220
|
console.log(`Dry run: would prepare release PR branch ${plan.releaseBranchPrefix} for ${plan.nextVersion}`);
|
|
213
221
|
return 0;
|
|
214
222
|
}
|
package/dist/git/commits.js
CHANGED
|
@@ -437,17 +437,26 @@ function applyRevertSuppression(commits) {
|
|
|
437
437
|
}
|
|
438
438
|
const presentShas = new Set(commits.map((commit) => commit.hash.toLowerCase()));
|
|
439
439
|
const reverted = new Set();
|
|
440
|
+
const suppressedReverts = new Set();
|
|
440
441
|
for (const commit of commits) {
|
|
441
442
|
if (!commit.isRevert) {
|
|
442
443
|
continue;
|
|
443
444
|
}
|
|
445
|
+
const revertsOnlyInWindow = commit.revertedShas.length > 0 &&
|
|
446
|
+
commit.revertedShas.every((sha) => presentShas.has(sha));
|
|
447
|
+
if (revertsOnlyInWindow) {
|
|
448
|
+
suppressedReverts.add(commit.hash.toLowerCase());
|
|
449
|
+
}
|
|
444
450
|
for (const sha of commit.revertedShas) {
|
|
445
451
|
if (presentShas.has(sha)) {
|
|
446
452
|
reverted.add(sha);
|
|
447
453
|
}
|
|
448
454
|
}
|
|
449
455
|
}
|
|
450
|
-
return commits.filter((commit) =>
|
|
456
|
+
return commits.filter((commit) => {
|
|
457
|
+
const hash = commit.hash.toLowerCase();
|
|
458
|
+
return !reverted.has(hash) && !suppressedReverts.has(hash);
|
|
459
|
+
});
|
|
451
460
|
}
|
|
452
461
|
function analyzeParsedCommits(commits) {
|
|
453
462
|
let result = null;
|
|
@@ -77,6 +77,7 @@ function groupCommitLines(commits, repoUrl) {
|
|
|
77
77
|
const features = [];
|
|
78
78
|
const fixes = [];
|
|
79
79
|
const reverts = [];
|
|
80
|
+
const effectiveCommits = (0, commits_js_1.applyRevertSuppression)(commits);
|
|
80
81
|
const getRevertedSubject = (commit) => {
|
|
81
82
|
const normalizedDescription = (commit.description ?? "")
|
|
82
83
|
.trim()
|
|
@@ -99,7 +100,7 @@ function groupCommitLines(commits, repoUrl) {
|
|
|
99
100
|
const revertedCommit = (0, commits_js_1.parseConventionalCommitMessage)(revertedSubject);
|
|
100
101
|
return (0, commits_js_1.inferReleaseTypeFromParsedCommit)(revertedCommit) !== null;
|
|
101
102
|
};
|
|
102
|
-
for (const commit of
|
|
103
|
+
for (const commit of effectiveCommits) {
|
|
103
104
|
const type = (0, commits_js_1.inferReleaseTypeFromParsedCommit)(commit);
|
|
104
105
|
if (!type) {
|
|
105
106
|
continue;
|
package/dist/release/pr.d.ts
CHANGED
|
@@ -20,6 +20,13 @@ export declare function renderSimpleReviewRequestBody(version: string, previousV
|
|
|
20
20
|
export declare function openOrUpdateReviewRequest(cwd: string, branch: string, title: string, version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, options?: {
|
|
21
21
|
logger?: VersionaryPluginContext["logger"];
|
|
22
22
|
}): Promise<string>;
|
|
23
|
+
export declare function closeStaleReviewRequestIfExists(cwd?: string, options?: {
|
|
24
|
+
logger?: VersionaryPluginContext["logger"];
|
|
25
|
+
}): Promise<{
|
|
26
|
+
closed: boolean;
|
|
27
|
+
url?: string;
|
|
28
|
+
number?: number;
|
|
29
|
+
}>;
|
|
23
30
|
/** @deprecated Use prepareReleasePr. */
|
|
24
31
|
export declare function prepareSimpleReleasePr(cwd?: string, options?: {
|
|
25
32
|
logger?: VersionaryPluginContext["logger"];
|
package/dist/release/pr.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.splitSafeDirtyFiles = splitSafeDirtyFiles;
|
|
|
7
7
|
exports.prepareReleasePr = prepareReleasePr;
|
|
8
8
|
exports.renderSimpleReviewRequestBody = renderSimpleReviewRequestBody;
|
|
9
9
|
exports.openOrUpdateReviewRequest = openOrUpdateReviewRequest;
|
|
10
|
+
exports.closeStaleReviewRequestIfExists = closeStaleReviewRequestIfExists;
|
|
10
11
|
exports.prepareSimpleReleasePr = prepareSimpleReleasePr;
|
|
11
12
|
exports.openOrUpdateSimpleReviewRequest = openOrUpdateSimpleReviewRequest;
|
|
12
13
|
exports.pushReleaseBranch = pushReleaseBranch;
|
|
@@ -408,6 +409,32 @@ async function openOrUpdateReviewRequest(cwd, branch, title, version, previousVe
|
|
|
408
409
|
});
|
|
409
410
|
return result.url;
|
|
410
411
|
}
|
|
412
|
+
function hasScmRuntimeContext() {
|
|
413
|
+
const hasRepository = Boolean(process.env.GITHUB_REPOSITORY);
|
|
414
|
+
const hasToken = Boolean(process.env.VERSIONARY_PR_TOKEN ??
|
|
415
|
+
process.env.GH_TOKEN ??
|
|
416
|
+
process.env.GITHUB_TOKEN);
|
|
417
|
+
return hasRepository && hasToken;
|
|
418
|
+
}
|
|
419
|
+
async function closeStaleReviewRequestIfExists(cwd = process.cwd(), options = {}) {
|
|
420
|
+
if (!hasScmRuntimeContext()) {
|
|
421
|
+
return { closed: false };
|
|
422
|
+
}
|
|
423
|
+
const plan = (0, plan_js_1.createReleasePlan)(cwd);
|
|
424
|
+
const scmClient = (0, client_js_1.getScmClient)();
|
|
425
|
+
const result = await scmClient.closeReviewRequestIfExists({
|
|
426
|
+
baseBranch: process.env.VERSIONARY_BASE_BRANCH ?? "main",
|
|
427
|
+
headBranch: plan.releaseBranchPrefix,
|
|
428
|
+
reason: "Closing stale release PR because no releasable commits remain for the current baseline/tag state.",
|
|
429
|
+
}, {
|
|
430
|
+
cwd,
|
|
431
|
+
logger: options.logger,
|
|
432
|
+
});
|
|
433
|
+
if (result.closed && result.number) {
|
|
434
|
+
options.logger?.info(`Closed stale release review request #${result.number} for ${plan.releaseBranchPrefix}.`);
|
|
435
|
+
}
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
411
438
|
/** @deprecated Use prepareReleasePr. */
|
|
412
439
|
function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
|
|
413
440
|
return prepareReleasePr(cwd, options);
|
|
@@ -174,6 +174,68 @@ function createGitHubPlugin() {
|
|
|
174
174
|
state: toReviewRequestState(created, `pull request #${created.number} in ${repoRef(repo)}`),
|
|
175
175
|
};
|
|
176
176
|
},
|
|
177
|
+
async closeReviewRequestIfExists(input, _context) {
|
|
178
|
+
const repo = getRepoFromEnv();
|
|
179
|
+
const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
|
|
180
|
+
const listHead = resolveHeadForList(repo, input.headBranch);
|
|
181
|
+
let existing;
|
|
182
|
+
try {
|
|
183
|
+
const response = await octokit.pulls.list({
|
|
184
|
+
owner: repo.owner,
|
|
185
|
+
repo: repo.repo,
|
|
186
|
+
state: "open",
|
|
187
|
+
head: listHead,
|
|
188
|
+
base: input.baseBranch,
|
|
189
|
+
per_page: 100,
|
|
190
|
+
});
|
|
191
|
+
existing = response.data;
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
const { message } = parseGitHubError(error);
|
|
195
|
+
throw new Error(`Failed listing open pull requests for branch "${input.headBranch}" into "${input.baseBranch}": [${repoRef(repo)}] ${message}`);
|
|
196
|
+
}
|
|
197
|
+
if (existing.length > 1) {
|
|
198
|
+
const matches = existing.map((item) => `#${item.number}`).join(", ");
|
|
199
|
+
throw new Error(`Ambiguous open pull request matches for "${input.headBranch}" into "${input.baseBranch}": [${repoRef(repo)}] ${matches}`);
|
|
200
|
+
}
|
|
201
|
+
if (existing.length === 0) {
|
|
202
|
+
return { closed: false };
|
|
203
|
+
}
|
|
204
|
+
const pr = existing[0];
|
|
205
|
+
let updated;
|
|
206
|
+
try {
|
|
207
|
+
const response = await octokit.pulls.update({
|
|
208
|
+
owner: repo.owner,
|
|
209
|
+
repo: repo.repo,
|
|
210
|
+
pull_number: pr.number,
|
|
211
|
+
state: "closed",
|
|
212
|
+
});
|
|
213
|
+
updated = response.data;
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const { message } = parseGitHubError(error);
|
|
217
|
+
throw new Error(`Failed closing pull request #${pr.number}: [${repoRef(repo)} base=${input.baseBranch} head=${input.headBranch}] ${message}`);
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
await octokit.issues.createComment({
|
|
221
|
+
owner: repo.owner,
|
|
222
|
+
repo: repo.repo,
|
|
223
|
+
issue_number: pr.number,
|
|
224
|
+
body: input.reason,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
const { status, message } = parseGitHubError(error);
|
|
229
|
+
if (status !== 404 && status !== 410) {
|
|
230
|
+
throw new Error(`Failed commenting on closed pull request #${pr.number}: [${repoRef(repo)} base=${input.baseBranch} head=${input.headBranch}] ${message}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
closed: true,
|
|
235
|
+
number: updated.number,
|
|
236
|
+
url: updated.html_url,
|
|
237
|
+
};
|
|
238
|
+
},
|
|
177
239
|
async createReleaseMetadata(input, _context) {
|
|
178
240
|
const repo = getRepoFromEnv();
|
|
179
241
|
const octokit = new rest_1.Octokit({ auth: getGitHubToken() });
|
package/dist/scm/types.d.ts
CHANGED
|
@@ -20,6 +20,16 @@ export interface ScmReviewRequestResult {
|
|
|
20
20
|
url: string;
|
|
21
21
|
state: "open" | "closed" | "merged";
|
|
22
22
|
}
|
|
23
|
+
export interface ScmCloseReviewRequestInput {
|
|
24
|
+
baseBranch: string;
|
|
25
|
+
headBranch: string;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ScmCloseReviewRequestResult {
|
|
29
|
+
closed: boolean;
|
|
30
|
+
number?: number;
|
|
31
|
+
url?: string;
|
|
32
|
+
}
|
|
23
33
|
export interface ScmReleaseMetadataInput {
|
|
24
34
|
tag: string;
|
|
25
35
|
version: string;
|
|
@@ -43,6 +53,7 @@ export interface ScmReleaseReferenceCommentsResult {
|
|
|
43
53
|
export interface ScmClient {
|
|
44
54
|
provider: ScmProvider;
|
|
45
55
|
createOrUpdateReviewRequest: (input: ScmReviewRequestInput, context: ScmClientContext) => Promise<ScmReviewRequestResult>;
|
|
56
|
+
closeReviewRequestIfExists: (input: ScmCloseReviewRequestInput, context: ScmClientContext) => Promise<ScmCloseReviewRequestResult>;
|
|
46
57
|
createReleaseMetadata: (input: ScmReleaseMetadataInput, context: ScmClientContext) => Promise<ScmReleaseMetadataResult>;
|
|
47
58
|
createReleaseReferenceComments?: (input: ScmReleaseReferenceCommentsInput, context: ScmClientContext) => Promise<ScmReleaseReferenceCommentsResult>;
|
|
48
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "versionary",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Automatic release framework based on conventional commits and semantic versioning",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"releasing",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"zod": "^4.1.12"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^
|
|
37
|
+
"@types/node": "^25.6.0",
|
|
38
38
|
"tsx": "^4.20.6",
|
|
39
|
-
"typescript": "^
|
|
39
|
+
"typescript": "^6.0.3",
|
|
40
40
|
"vitest": "^4.1.4"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|