@tertium/gith 0.1.1 → 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.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/bin/gith.js +104 -9
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -93,6 +93,8 @@ gith stat --last-week -limit 50 # the same for last week, looking at no more
93
93
  gith sync # fetch all remotes and tags, prune stale refs
94
94
  gith open # open origin in the browser
95
95
  gith doctor # check the repo is ready for the branch flow
96
+ gith changelog --dry-run # preview the CHANGELOG.md entry for package.json's version
97
+ gith changelog # write it as its own commit (docs(changelog): <version>); --no-commit only writes
96
98
 
97
99
  # Help
98
100
  gith help # full usage
package/bin/gith.js CHANGED
@@ -31,6 +31,7 @@ var SPECS = new Map([
31
31
  ["pockets", "switch"],
32
32
  ["detached", "switch"],
33
33
  ["all", "switch"],
34
+ ["no-commit", "switch"],
34
35
  ["overall", "switch"],
35
36
  ["current-year", "switch"],
36
37
  ["current-month", "switch"],
@@ -119,6 +120,10 @@ function onlyOne(flags, names) {
119
120
  return present[0];
120
121
  }
121
122
 
123
+ // src/components/changelog/changelog.ts
124
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
125
+ import path from "node:path";
126
+
122
127
  // src/components/git/git.ts
123
128
  import { execFileSync } from "node:child_process";
124
129
  var verbose = false;
@@ -216,6 +221,92 @@ function act(args, options = {}) {
216
221
  execFileSync("git", args, { stdio: "inherit", ...options });
217
222
  }
218
223
 
224
+ // src/components/changelog/changelog.constants.ts
225
+ var CHANGELOG_FILE_NAME = "CHANGELOG.md";
226
+ var CHANGELOG_TITLE = "# Changelog";
227
+ var SEMVER_ONLY_PATTERN = /^\d+\.\d+\.\d+$/;
228
+
229
+ // src/components/changelog/changelog.utils.ts
230
+ function filterCommitSubjects(rawLog) {
231
+ if (!rawLog.trim())
232
+ return [];
233
+ return rawLog.split(`
234
+ `).map((line) => line.trim()).filter((line) => line.length > 0 && !SEMVER_ONLY_PATTERN.test(line));
235
+ }
236
+ function buildEntry(notes) {
237
+ const bullets = notes.subjects.length > 0 ? notes.subjects.map((subject) => `- ${subject}`).join(`
238
+ `) : "- (no changes recorded)";
239
+ return `## ${notes.version} - ${notes.date}
240
+
241
+ ${bullets}
242
+ `;
243
+ }
244
+ function mergeChangelogContent(existing, entry) {
245
+ const heading = `${CHANGELOG_TITLE}
246
+ `;
247
+ if (existing === null)
248
+ return `${heading}
249
+ ${entry}
250
+ `;
251
+ const body = existing.startsWith(heading) ? existing.slice(heading.length).replace(/^\n+/, "") : existing;
252
+ return `${heading}
253
+ ${entry}
254
+ ${body}`;
255
+ }
256
+ function parsePackageVersion(rawPackageJson) {
257
+ const pkg = JSON.parse(rawPackageJson);
258
+ if (!pkg.version)
259
+ throw new Error('No "version" field found in package.json');
260
+ return pkg.version;
261
+ }
262
+ function todayIso(date = new Date) {
263
+ return date.toISOString().slice(0, 10);
264
+ }
265
+
266
+ // src/components/changelog/changelog.ts
267
+ function previousTag() {
268
+ try {
269
+ return captureArgs(["describe", "--tags", "--abbrev=0"]);
270
+ } catch {
271
+ return null;
272
+ }
273
+ }
274
+ function commitSubjectsSince(tag) {
275
+ const range = tag ? `${tag}..HEAD` : "HEAD";
276
+ return filterCommitSubjects(captureArgs(["log", range, "--no-merges", "--pretty=format:%s"]));
277
+ }
278
+ function changelog(flags) {
279
+ const noCommit = flags.has("no-commit");
280
+ if (flags.size > (noCommit ? 1 : 0))
281
+ throw new Error("usage: gith changelog [--dry-run] [--no-commit]");
282
+ const root = process.cwd();
283
+ const version = parsePackageVersion(readFileSync(path.join(root, "package.json"), "utf8"));
284
+ const entry = buildEntry({
285
+ version,
286
+ date: todayIso(),
287
+ subjects: commitSubjectsSince(previousTag())
288
+ });
289
+ if (isDryRun()) {
290
+ console.log(entry);
291
+ console.log(`dry run: would update ${CHANGELOG_FILE_NAME}`);
292
+ return;
293
+ }
294
+ const file = path.join(root, CHANGELOG_FILE_NAME);
295
+ const existing = existsSync(file) ? readFileSync(file, "utf8") : null;
296
+ writeFileSync(file, mergeChangelogContent(existing, entry));
297
+ console.log(`✅ ${CHANGELOG_FILE_NAME} updated for ${version}`);
298
+ if (noCommit)
299
+ return;
300
+ gitArgs(["add", "--", CHANGELOG_FILE_NAME]);
301
+ gitArgs([
302
+ "commit",
303
+ "-m",
304
+ `docs(changelog): ${version}`,
305
+ "--",
306
+ CHANGELOG_FILE_NAME
307
+ ]);
308
+ }
309
+
219
310
  // src/components/doctor/doctor.utils.ts
220
311
  function check(name, test, failure = "fail") {
221
312
  return {
@@ -1569,7 +1660,7 @@ function tree(flags) {
1569
1660
  }
1570
1661
 
1571
1662
  // src/components/version/version.ts
1572
- import { readFileSync as readFileSync2 } from "node:fs";
1663
+ import { readFileSync as readFileSync3 } from "node:fs";
1573
1664
 
1574
1665
  // src/components/version/version.constants.ts
1575
1666
  var MASCOT = [
@@ -1584,19 +1675,19 @@ var MASCOT = [
1584
1675
  ];
1585
1676
 
1586
1677
  // src/components/version/version.utils.ts
1587
- import { existsSync, readFileSync } from "node:fs";
1588
- import path from "node:path";
1678
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
1679
+ import path2 from "node:path";
1589
1680
  import { fileURLToPath } from "node:url";
1590
1681
  function packageJsonPath() {
1591
- let dir = path.dirname(fileURLToPath(import.meta.url));
1682
+ let dir = path2.dirname(fileURLToPath(import.meta.url));
1592
1683
  for (;; ) {
1593
- const candidate = path.join(dir, "package.json");
1594
- if (existsSync(candidate)) {
1595
- const pkg = JSON.parse(readFileSync(candidate, "utf8"));
1684
+ const candidate = path2.join(dir, "package.json");
1685
+ if (existsSync2(candidate)) {
1686
+ const pkg = JSON.parse(readFileSync2(candidate, "utf8"));
1596
1687
  if (pkg.name === "@tertium/gith")
1597
1688
  return candidate;
1598
1689
  }
1599
- const parent = path.dirname(dir);
1690
+ const parent = path2.dirname(dir);
1600
1691
  if (parent === dir)
1601
1692
  throw new Error("gith package.json not found");
1602
1693
  dir = parent;
@@ -1605,7 +1696,7 @@ function packageJsonPath() {
1605
1696
 
1606
1697
  // src/components/version/version.ts
1607
1698
  function readVersion() {
1608
- const pkg = JSON.parse(readFileSync2(packageJsonPath(), "utf8"));
1699
+ const pkg = JSON.parse(readFileSync3(packageJsonPath(), "utf8"));
1609
1700
  return pkg.version ?? "unknown";
1610
1701
  }
1611
1702
  function versionText() {
@@ -1670,6 +1761,7 @@ Remote and health
1670
1761
  gith sync fetch all remotes and tags, prune stale refs
1671
1762
  gith open open origin in the browser
1672
1763
  gith doctor check the repo is ready for the branch flow (exit 1 on failures)
1764
+ gith changelog [--dry-run] add an entry for package.json's version to CHANGELOG.md (commits since the last tag)
1673
1765
 
1674
1766
  Help
1675
1767
  gith help show this help
@@ -1753,6 +1845,9 @@ try {
1753
1845
  case "open":
1754
1846
  open2(parseFlags(rest));
1755
1847
  break;
1848
+ case "changelog":
1849
+ changelog(parseFlags(rest));
1850
+ break;
1756
1851
  case "doctor":
1757
1852
  if (!doctor(parseFlags(rest)))
1758
1853
  exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tertium/gith",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Git helper: branch flow with goal branches, release and hotfix containers, and everyday git commands.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,8 @@
18
18
  "typecheck": "tsc --noEmit",
19
19
  "release:patch": "node bin/gith.js release --patch",
20
20
  "release:minor": "node bin/gith.js release --minor",
21
- "release:major": "node bin/gith.js release --major"
21
+ "release:major": "node bin/gith.js release --major",
22
+ "version": "bun run build && node bin/gith.js changelog"
22
23
  },
23
24
  "license": "ISC",
24
25
  "author": "Vitalii Balabanov",