@logickernel/agileflow 0.2.0 → 0.2.2
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/package.json +2 -2
- package/src/git-push.js +22 -8
- package/src/index.js +22 -17
- package/src/utils.js +37 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logickernel/agileflow",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Automatic semantic versioning and changelog generation based on conventional commits",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,6 @@
|
|
|
34
34
|
"type": "git",
|
|
35
35
|
"url": "git@code.logickernel.com:kernel/agileflow.git"
|
|
36
36
|
},
|
|
37
|
-
"author": "",
|
|
37
|
+
"author": "Víctor H. Valle <victor.valle@logickernel.com>",
|
|
38
38
|
"license": "ISC"
|
|
39
39
|
}
|
package/src/git-push.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Creates an annotated tag and pushes it to the remote repository.
|
|
@@ -11,16 +14,27 @@ const { run } = require('./utils');
|
|
|
11
14
|
*/
|
|
12
15
|
async function pushTag(tagName, message) {
|
|
13
16
|
const safeTag = String(tagName).replace(/"/g, '\\"');
|
|
14
|
-
const safeMsg = String(message).replace(/"/g, '\\"');
|
|
15
17
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// Write message to a temp file to avoid shell escaping issues with special characters
|
|
19
|
+
const tempFile = path.join(os.tmpdir(), `agileflow-tag-${Date.now()}.txt`);
|
|
20
|
+
try {
|
|
21
|
+
fs.writeFileSync(tempFile, message, 'utf8');
|
|
22
|
+
|
|
23
|
+
// Create annotated tag using -F to read message from file
|
|
24
|
+
execSync(`git tag -a "${safeTag}" -F "${tempFile}"`, { stdio: 'pipe' });
|
|
25
|
+
|
|
26
|
+
// Push to origin
|
|
27
|
+
execSync(`git push origin "${safeTag}"`, { stdio: 'pipe' });
|
|
28
|
+
} finally {
|
|
29
|
+
// Clean up temp file
|
|
30
|
+
try {
|
|
31
|
+
fs.unlinkSync(tempFile);
|
|
32
|
+
} catch {
|
|
33
|
+
// Ignore cleanup errors
|
|
34
|
+
}
|
|
35
|
+
}
|
|
21
36
|
}
|
|
22
37
|
|
|
23
38
|
module.exports = {
|
|
24
39
|
pushTag,
|
|
25
40
|
};
|
|
26
|
-
|
package/src/index.js
CHANGED
|
@@ -38,24 +38,32 @@ function parseArgs(args) {
|
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Displays version info to the console.
|
|
41
|
-
* @param {{currentVersion: string|null,
|
|
42
|
-
* @param {boolean} quiet - Only output the
|
|
41
|
+
* @param {{currentVersion: string|null, newVersion: string|null, commits: Array, changelog: string}} info
|
|
42
|
+
* @param {boolean} quiet - Only output the new version
|
|
43
43
|
*/
|
|
44
44
|
function displayVersionInfo(info, quiet) {
|
|
45
|
-
const { currentVersion,
|
|
45
|
+
const { currentVersion, newVersion, commits, changelog } = info;
|
|
46
46
|
|
|
47
47
|
if (quiet) {
|
|
48
|
-
if (
|
|
49
|
-
console.log(
|
|
48
|
+
if (newVersion) {
|
|
49
|
+
console.log(newVersion);
|
|
50
50
|
}
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
console.log(`Commits since current version
|
|
54
|
+
|
|
55
|
+
// List commits
|
|
56
|
+
console.log(`Commits since current version (${commits.length}):`);
|
|
57
|
+
for (const commit of commits) {
|
|
58
|
+
const subject = commit.message.split('\n')[0].trim();
|
|
59
|
+
const shortHash = commit.hash.substring(0, 7);
|
|
60
|
+
console.log(` ${shortHash} ${subject}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log(`\nCurrent version: ${currentVersion || 'none'}`);
|
|
64
|
+
console.log(`New version: ${newVersion || 'no bump needed'}`);
|
|
57
65
|
if (changelog) {
|
|
58
|
-
console.log(`\nChangelog:\n${changelog}`);
|
|
66
|
+
console.log(`\nChangelog:\n\n${changelog}`);
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
|
|
@@ -71,10 +79,7 @@ async function handlePushCommand(pushType, options) {
|
|
|
71
79
|
displayVersionInfo(info, options.quiet);
|
|
72
80
|
|
|
73
81
|
// Skip push if no version bump needed
|
|
74
|
-
if (!info.
|
|
75
|
-
if (!options.quiet) {
|
|
76
|
-
console.log('\nNo version bump needed. Skipping tag creation.');
|
|
77
|
-
}
|
|
82
|
+
if (!info.newVersion) {
|
|
78
83
|
return;
|
|
79
84
|
}
|
|
80
85
|
|
|
@@ -93,16 +98,16 @@ async function handlePushCommand(pushType, options) {
|
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
// Create tag message from changelog
|
|
96
|
-
const tagMessage = info.changelog || info.
|
|
101
|
+
const tagMessage = info.changelog || info.newVersion;
|
|
97
102
|
|
|
98
103
|
if (!options.quiet) {
|
|
99
|
-
console.log(`\nCreating tag ${info.
|
|
104
|
+
console.log(`\nCreating tag ${info.newVersion}...`);
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
await pushModule.pushTag(info.
|
|
107
|
+
await pushModule.pushTag(info.newVersion, tagMessage);
|
|
103
108
|
|
|
104
109
|
if (!options.quiet) {
|
|
105
|
-
console.log(`Tag ${info.
|
|
110
|
+
console.log(`Tag ${info.newVersion} created and pushed successfully.`);
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
113
|
|
package/src/utils.js
CHANGED
|
@@ -67,6 +67,21 @@ const TYPE_ORDER = ['feat', 'fix', 'perf', 'refactor', 'style', 'test', 'docs',
|
|
|
67
67
|
const PATCH_TYPES = ['fix', 'perf', 'refactor', 'test', 'build', 'ci', 'revert'];
|
|
68
68
|
const SEMVER_PATTERN = /^v(\d+)\.(\d+)\.(\d+)(-[a-zA-Z0-9.-]+)?$/;
|
|
69
69
|
|
|
70
|
+
// Friendly header names for changelog
|
|
71
|
+
const TYPE_HEADERS = {
|
|
72
|
+
feat: 'Features:',
|
|
73
|
+
fix: 'Fixes:',
|
|
74
|
+
perf: 'Performance:',
|
|
75
|
+
refactor: 'Refactors:',
|
|
76
|
+
style: 'Style:',
|
|
77
|
+
test: 'Tests:',
|
|
78
|
+
docs: 'Documentation:',
|
|
79
|
+
build: 'Build:',
|
|
80
|
+
ci: 'CI:',
|
|
81
|
+
chore: 'Chores:',
|
|
82
|
+
revert: 'Reverts:',
|
|
83
|
+
};
|
|
84
|
+
|
|
70
85
|
/**
|
|
71
86
|
* Fetches tags from remote (non-destructive) if a remote is configured.
|
|
72
87
|
* @returns {boolean} True if tags were fetched, false if using local tags only
|
|
@@ -242,6 +257,16 @@ function analyzeCommitsForVersioning(commits) {
|
|
|
242
257
|
return { hasBreaking, hasFeat, hasPatchTypes, commitsByType };
|
|
243
258
|
}
|
|
244
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Capitalizes the first letter of a string.
|
|
262
|
+
* @param {string} str - The string to capitalize
|
|
263
|
+
* @returns {string} Capitalized string
|
|
264
|
+
*/
|
|
265
|
+
function capitalize(str) {
|
|
266
|
+
if (!str) return str;
|
|
267
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
268
|
+
}
|
|
269
|
+
|
|
245
270
|
/**
|
|
246
271
|
* Generates changelog entries for a commit type section.
|
|
247
272
|
* @param {Array} commits - Commits of this type
|
|
@@ -271,20 +296,22 @@ function generateTypeChangelog(commits) {
|
|
|
271
296
|
|
|
272
297
|
const lines = [];
|
|
273
298
|
for (const entry of noScope) {
|
|
274
|
-
|
|
299
|
+
const ref = entry.issueRef ? ` ${entry.issueRef}` : '';
|
|
300
|
+
lines.push(`- ${capitalize(entry.description)}${ref}`);
|
|
275
301
|
}
|
|
276
302
|
for (const scope of Object.keys(byScope).sort()) {
|
|
277
303
|
for (const entry of byScope[scope]) {
|
|
278
|
-
|
|
304
|
+
const ref = entry.issueRef ? ` ${entry.issueRef}` : '';
|
|
305
|
+
lines.push(`- ${scope}: ${capitalize(entry.description)}${ref}`);
|
|
279
306
|
}
|
|
280
307
|
}
|
|
281
308
|
return lines;
|
|
282
309
|
}
|
|
283
310
|
|
|
284
311
|
/**
|
|
285
|
-
* Calculates the
|
|
312
|
+
* Calculates the new version and generates a changelog.
|
|
286
313
|
* @param {{latestVersion: string|null, commits: Array}} expandedInfo
|
|
287
|
-
* @returns {{
|
|
314
|
+
* @returns {{newVersion: string|null, changelog: string}}
|
|
288
315
|
*/
|
|
289
316
|
function calculateNextVersionAndChangelog(expandedInfo) {
|
|
290
317
|
const { latestVersion, commits } = expandedInfo;
|
|
@@ -292,7 +319,7 @@ function calculateNextVersionAndChangelog(expandedInfo) {
|
|
|
292
319
|
const analysis = analyzeCommitsForVersioning(commits);
|
|
293
320
|
|
|
294
321
|
const bump = determineVersionBumpType(analysis, current.major === 0);
|
|
295
|
-
const
|
|
322
|
+
const newVersion = applyVersionBump(current, bump);
|
|
296
323
|
|
|
297
324
|
// Generate changelog
|
|
298
325
|
const changelogLines = [];
|
|
@@ -300,7 +327,7 @@ function calculateNextVersionAndChangelog(expandedInfo) {
|
|
|
300
327
|
const typeCommits = analysis.commitsByType[type];
|
|
301
328
|
if (!typeCommits?.length) continue;
|
|
302
329
|
|
|
303
|
-
changelogLines.push(
|
|
330
|
+
changelogLines.push(TYPE_HEADERS[type] || `${capitalize(type)}:`);
|
|
304
331
|
changelogLines.push(...generateTypeChangelog(typeCommits));
|
|
305
332
|
changelogLines.push('');
|
|
306
333
|
}
|
|
@@ -309,7 +336,7 @@ function calculateNextVersionAndChangelog(expandedInfo) {
|
|
|
309
336
|
changelogLines.pop();
|
|
310
337
|
}
|
|
311
338
|
|
|
312
|
-
return {
|
|
339
|
+
return { newVersion, changelog: changelogLines.join('\n') };
|
|
313
340
|
}
|
|
314
341
|
|
|
315
342
|
/**
|
|
@@ -355,7 +382,7 @@ function getAllBranchCommits(branch) {
|
|
|
355
382
|
|
|
356
383
|
/**
|
|
357
384
|
* Processes version information for the current branch.
|
|
358
|
-
* @returns {Promise<{currentVersion: string|null,
|
|
385
|
+
* @returns {Promise<{currentVersion: string|null, newVersion: string|null, commits: Array, changelog: string}>}
|
|
359
386
|
*/
|
|
360
387
|
async function processVersionInfo() {
|
|
361
388
|
ensureGitRepo();
|
|
@@ -365,11 +392,11 @@ async function processVersionInfo() {
|
|
|
365
392
|
const allCommits = getAllBranchCommits(branch);
|
|
366
393
|
const expandedInfo = expandCommitInfo(allCommits);
|
|
367
394
|
const { latestVersion, commits } = expandedInfo;
|
|
368
|
-
const {
|
|
395
|
+
const { newVersion, changelog } = calculateNextVersionAndChangelog(expandedInfo);
|
|
369
396
|
|
|
370
397
|
return {
|
|
371
398
|
currentVersion: latestVersion,
|
|
372
|
-
|
|
399
|
+
newVersion,
|
|
373
400
|
commits,
|
|
374
401
|
changelog,
|
|
375
402
|
};
|