lechnerio-git-hooks 1.3.1 → 1.3.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/README.md CHANGED
@@ -34,9 +34,13 @@ On install, the package automatically:
34
34
  | `commit-msg` | Enforces conventional commits via commitlint |
35
35
  | `post-commit` | Interactive version bump (subtle/patch/minor/major), changelog generation with optional skip flag, push/merge prompt |
36
36
 
37
+ ## Changelog versioning model
38
+
39
+ Commits belong to the version that was current while they were made. A Minor or Major bump closes the running version — all work since the previous bump stays under it, including the commit that triggered the bump — and opens the new version with a `Version Upgrade to <version>` marker entry. Future commits then accumulate under the new version.
40
+
37
41
  ## AI release summaries
38
42
 
39
- On Minor and Major version bumps, the `post-commit` hook sends the version's non-skipped commit messages to the Gemini API and stores the result as `summary_en` and `summary_ger` fields (English and German) on the version entry in `data/changelog.json`. This requires `GITHOOKS_GEMINI_API_KEY` in the project's `.env` file or environment; without it the summary step is skipped silently.
43
+ On Minor and Major version bumps, the `post-commit` hook sends the non-skipped commit messages of the version being closed to the Gemini API and stores the result as `summary_en` and `summary_ger` fields (English and German) on that version entry in `data/changelog.json`. This requires `GITHOOKS_GEMINI_API_KEY` in the project's `.env` file or environment; without it the summary step is skipped silently. Run `node scripts/generate-changelog.mjs --summarize-version <version>` to generate the summary for any version by hand (accepts the version with or without the `v` prefix, subtle suffixes are normalized). Summaries are only generated when `summary_en` or `summary_ger` is missing on the entry — to regenerate one, delete both fields from `data/changelog.json` first.
40
44
 
41
45
  ## Peer dependencies (auto-installed)
42
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lechnerio-git-hooks",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "Shared git hooks for lechnerio projects",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -58,23 +58,13 @@ if (process.argv.includes("--skip-last")) {
58
58
 
59
59
  const changelog = []
60
60
 
61
- const currentVersion = tags[0].replace(/^v/, "")
62
- const unreleasedRaw = run(`git log --format="%s" ${tags[0]}..HEAD`)
63
- if (unreleasedRaw) {
64
- const commits = [...new Set(unreleasedRaw.split("\n").filter(Boolean))].map((message) => ({ message, skip: skipFlags.get(message) ?? false }))
65
- if (commits.length > 0) {
66
- const date = run(`git log --format=%ai HEAD -1`).split(" ")[0]
67
- changelog.push({ version: currentVersion, tag: null, date, commits })
68
- }
69
- }
70
-
71
61
  for (let i = 0; i < tags.length; i++) {
72
62
  const tag = tags[i]
63
+ const upper = i === 0 ? "HEAD" : tags[i - 1]
64
+ const eraRaw = run(`git log --format="%s" ${tag}..${upper}`)
65
+ const initialRaw = i === tags.length - 1 ? run(`git log --format="%s" ${tag}`) : ""
66
+ const raw = [eraRaw, initialRaw].filter(Boolean).join("\n")
73
67
  const date = run(`git log --format=%ai ${tag} -1`).split(" ")[0]
74
- const prevTag = tags[i + 1]
75
-
76
- const range = prevTag ? `${prevTag}..${tag}` : tag
77
- const raw = run(`git log --format="%s" ${range}`)
78
68
 
79
69
  const commits = raw
80
70
  ? [...new Set(raw.split("\n").filter(Boolean))].map((message) => ({ message, skip: skipFlags.get(message) ?? false }))
@@ -112,6 +102,14 @@ for (const entry of grouped) {
112
102
  })
113
103
  }
114
104
 
105
+ for (let i = 0; i < grouped.length - 1; i++) {
106
+ const entry = grouped[i]
107
+ const marker = `Version Upgrade to ${entry.version}`
108
+ if (!entry.commits.some((c) => c.message === marker)) {
109
+ entry.commits.push({ message: marker, skip: skipFlags.get(marker) ?? false })
110
+ }
111
+ }
112
+
115
113
  for (const entry of grouped) {
116
114
  const saved = summaries.get(entry.version)
117
115
  if (saved) {
@@ -120,13 +118,22 @@ for (const entry of grouped) {
120
118
  }
121
119
  }
122
120
 
123
- if (process.argv.includes("--summarize")) {
121
+ const svIndex = process.argv.indexOf("--summarize-version")
122
+ const requestedVersion = svIndex === -1 ? null : process.argv[svIndex + 1] ?? null
123
+
124
+ if (process.argv.includes("--summarize") || svIndex !== -1) {
124
125
  const apiKey = loadEnvKey("GITHOOKS_GEMINI_API_KEY")
125
- if (!apiKey) {
126
+ if (svIndex !== -1 && !requestedVersion) {
127
+ console.warn("--summarize-version requires a version argument")
128
+ } else if (!apiKey) {
126
129
  console.warn("GITHOOKS_GEMINI_API_KEY not set, skipping AI summary")
127
130
  } else {
128
- const target = grouped.find((e) => e.version === baseVersion(tags[0].replace(/^v/, "")))
129
- if (target && !(target.summary_en && target.summary_ger)) {
131
+ const closedTag = tags.length > 1 ? tags[1] : tags[0]
132
+ const targetVersion = requestedVersion ? baseVersion(requestedVersion.replace(/^v/, "")) : baseVersion(closedTag.replace(/^v/, ""))
133
+ const target = grouped.find((e) => e.version === targetVersion)
134
+ if (!target) {
135
+ console.warn(`version ${targetVersion} not found in changelog`)
136
+ } else if (!(target.summary_en && target.summary_ger)) {
130
137
  const messages = target.commits.filter((c) => !c.skip).map((c) => c.message)
131
138
  if (messages.length === 0) {
132
139
  console.warn("no commits to summarize")