dsh-advisor 0.1.2 → 0.1.3-alpha.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-advisor",
3
- "version": "0.1.2",
3
+ "version": "0.1.3-alpha.2",
4
4
  "type": "module",
5
5
  "description": "dsh plugin bundle porting the omp advisor subsystem: a per-session reviewer model that observes the primary transcript and injects severity-ranked advice (nit/concern/blocker).",
6
6
  "repository": {
@@ -3,11 +3,13 @@
3
3
  * Release-prep version helper (plan dsh-advisor-ci-release, task 2 + 4):
4
4
  * resolves the target version — explicit `X.Y.Z` / `X.Y.Z-prerelease`
5
5
  * argument (e.g. 0.1.1-alpha.1) or an auto patch bump from package.json —
6
- * refuses already-released versions (existing `v<version>` git tag), bumps
7
- * package.json, inserts the `## [<version>]` section into CHANGELOG.md (from
8
- * the same git-log notes the workflows use; keeps existing sections, no-op
9
- * when already present), and prints `VERSION=<version>` for the workflow to
10
- * capture. Node built-ins only, no new dependencies.
6
+ * refuses already-released versions (existing `v<version>` git tag), aborts
7
+ * when there are no commits since the previous release tag (nothing to
8
+ * release), bumps package.json, inserts the `## [<version>]` section into
9
+ * CHANGELOG.md (from the same git-log notes the workflows use; keeps
10
+ * existing sections, no-op when already present), and prints
11
+ * `VERSION=<version>` for the workflow to capture. Node built-ins only, no
12
+ * new dependencies.
11
13
  *
12
14
  * Usage:
13
15
  * node scripts/prepare-release.mjs # auto patch bump
@@ -15,8 +17,8 @@
15
17
  * node scripts/prepare-release.mjs 0.1.1-alpha.1 # explicit prerelease
16
18
  *
17
19
  * Exit codes: 0 = bumped and printed VERSION; 1 = rejected (unparseable
18
- * version, existing tag, unparseable current package.json version, or
19
- * CHANGELOG.md write error).
20
+ * version, existing tag, no commits since the previous release tag,
21
+ * unparseable current package.json version, or CHANGELOG.md write error).
20
22
  */
21
23
 
22
24
  import { execFileSync } from 'node:child_process'
@@ -89,6 +91,19 @@ try {
89
91
  // Tag absent → OK to prepare.
90
92
  }
91
93
 
94
+ // Fail fast on a no-change release: an empty notes range means no commits
95
+ // since the previous release tag — prepare-release must not bump the version
96
+ // or touch CHANGELOG.md for an empty release (no empty release PR). First-
97
+ // ever releases (no ancestor tag yet) span the full first-parent history,
98
+ // which is non-empty by construction, so they still pass.
99
+ const { prevTag, notes } = resolveReleaseNotes()
100
+ if (notes === '') {
101
+ console.error(
102
+ `Error: no commits since previous release tag ${prevTag === '' ? '<first commit>' : prevTag} — nothing to release. Aborting.`,
103
+ )
104
+ process.exit(1)
105
+ }
106
+
92
107
  pkg.version = version
93
108
  // Preserve repo formatting: 2-space indent + trailing newline. The version
94
109
  // line must be the only diff (verified by `git diff package.json` in the
@@ -96,14 +111,14 @@ pkg.version = version
96
111
  writeFileSync(PKG_PATH, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8')
97
112
 
98
113
  /**
99
- * Release notes since the previous release: first-parent commit subjects
100
- * between the nearest ancestor tag of HEAD and HEAD. Same snippet the
101
- * workflows use for the PR body the CHANGELOG section and the PR body
102
- * stay consistent. When no ancestor tag exists yet, the notes span the full
103
- * first-parent history. Do NOT use "v$VERSION^": the version string is not a
114
+ * Resolve the previous release tag (nearest ancestor tag of HEAD; empty
115
+ * when none exists yet) and the first-parent commit subjects since that
116
+ * tag (full first-parent history when no tag exists). Same git-log snippet
117
+ * the workflows use for the PR body the CHANGELOG section and the PR body
118
+ * stay consistent. Do NOT use "v$VERSION^": the version string is not a
104
119
  * git ref — the bump commit is not tagged yet.
105
120
  */
106
- function collectReleaseNotes() {
121
+ function resolveReleaseNotes() {
107
122
  let prevTag = ''
108
123
  try {
109
124
  prevTag = execFileSync('git', ['describe', '--tags', '--abbrev=0', 'HEAD'], {
@@ -115,21 +130,23 @@ function collectReleaseNotes() {
115
130
  // No ancestor tag yet → full history.
116
131
  }
117
132
  const range = prevTag === '' ? 'HEAD' : `${prevTag}..HEAD`
118
- return execFileSync('git', ['log', '--oneline', '--first-parent', range], {
133
+ const notes = execFileSync('git', ['log', '--oneline', '--first-parent', range], {
119
134
  cwd: REPO_ROOT,
120
135
  encoding: 'utf8',
121
136
  stdio: ['ignore', 'pipe', 'ignore'],
122
137
  }).trim()
138
+ return { prevTag, notes }
123
139
  }
124
140
 
125
141
  /**
126
142
  * Insert a `## [<version>] - <YYYY-MM-DD>` section under the `# Changelog`
127
- * header, above the existing sections, from the same git-log notes the
128
- * workflows use. Preserves all existing sections; no-op when the section for
129
- * this version already exists (idempotent re-runs must not duplicate it);
130
- * write failures exit 1.
143
+ * header, above the existing sections, from the pre-collected git-log notes
144
+ * (guaranteed non-empty by the no-change guard, so no fallback text is ever
145
+ * written). Preserves all existing sections; no-op when the section for this
146
+ * version already exists (idempotent re-runs must not duplicate it); write
147
+ * failures exit 1.
131
148
  */
132
- function updateChangelog(version) {
149
+ function updateChangelog(version, notes) {
133
150
  let content
134
151
  try {
135
152
  content = readFileSync(CHANGELOG_PATH, 'utf8')
@@ -148,15 +165,11 @@ function updateChangelog(version) {
148
165
  return
149
166
  }
150
167
 
151
- const notes = collectReleaseNotes()
152
168
  const sectionHeader = `## [${version}] - ${new Date().toISOString().slice(0, 10)}`
153
- const sectionBody =
154
- notes === ''
155
- ? 'No commits between releases.'
156
- : notes
157
- .split('\n')
158
- .map((line) => `- ${line}`)
159
- .join('\n')
169
+ const sectionBody = notes
170
+ .split('\n')
171
+ .map((line) => `- ${line}`)
172
+ .join('\n')
160
173
  // The section's own trailing `\n` plus the inserted `\n` below form the
161
174
  // blank-line separator between the new section and the existing sections.
162
175
  const section = `${sectionHeader}\n\n${sectionBody}\n`
@@ -185,6 +198,6 @@ function updateChangelog(version) {
185
198
  }
186
199
  }
187
200
 
188
- updateChangelog(version)
201
+ updateChangelog(version, notes)
189
202
 
190
203
  console.log(`VERSION=${version}`)