@entro314labs/release-kit 2.4.0 → 2.5.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 +12 -0
  2. package/package.json +1 -1
  3. package/release.mjs +39 -6
package/README.md CHANGED
@@ -621,6 +621,18 @@ jobs:
621
621
  - run: goreleaser release --clean --release-notes dist-notes.md
622
622
  ```
623
623
 
624
+ A Go project has no version file at all — the tag is the version — so it is
625
+ `"versionFile": null` and the version is passed explicitly, or inferred with `auto`:
626
+
627
+ ```sh
628
+ release-kit auto # bump from the commits, tag, push; goreleaser takes it from there
629
+ ```
630
+
631
+ **Only one of the two should write release notes.** goreleaser groups conventional commits
632
+ itself; `--release-notes` makes it use yours instead and skip its own generation. Leaving
633
+ both on means the notes in the GitHub release and the notes in your `CHANGELOG.md` are
634
+ generated by different code from the same commits, and they drift.
635
+
624
636
  **Do not leave `release` in `steps` here.** goreleaser creates the GitHub release itself; if
625
637
  release-kit has already created one for that tag, goreleaser fails. Exactly one of them
626
638
  should own it, and it should be the one attaching the binaries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entro314labs/release-kit",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Single-file, zero-dependency release mechanism for JS/TS/Node projects: version bump, changelog roll, commit, annotated tag, push, publish, GitHub release",
5
5
  "keywords": [
6
6
  "changelog",
package/release.mjs CHANGED
@@ -419,6 +419,7 @@ const CHANGELOG_SECTIONS = [
419
419
  { type: 'fix', section: 'Bug Fixes' },
420
420
  { type: 'perf', section: 'Performance Improvements' },
421
421
  { type: 'revert', section: 'Reverts' },
422
+ { type: 'deps', section: 'Dependencies' },
422
423
  { type: 'docs', section: 'Documentation', hidden: true },
423
424
  { type: 'style', section: 'Styles', hidden: true },
424
425
  { type: 'refactor', section: 'Code Refactoring', hidden: true },
@@ -435,7 +436,9 @@ const CHANGELOG_SECTIONS = [
435
436
  * releaseAs: string|null} | null} null when the subject is not Conventional Commits
436
437
  */
437
438
  function parseCommit(subject, body = '', hash = '') {
438
- const match = /^([a-z]+)(?:\(([^)]+)\))?(!)?: (.+)$/i.exec(subject)
439
+ // Conventional Commits does not restrict the type to letters — `i18n:` and `a11y:` are
440
+ // types people really use, and `[a-z]+` silently failed to parse them at all.
441
+ const match = /^([a-z][a-z0-9-]*)(?:\(([^)]+)\))?(!)?: (.+)$/i.exec(subject)
439
442
  if (!match) return null
440
443
  const [, type, scope, bang, text] = match
441
444
  const shortHash = hash.slice(0, 7)
@@ -519,6 +522,7 @@ function changelogFromCommits(commits, links = null) {
519
522
  lines.push('')
520
523
  }
521
524
 
525
+ const known = new Set(CHANGELOG_SECTIONS.map((s) => s.type))
522
526
  for (const { type, section, hidden } of CHANGELOG_SECTIONS) {
523
527
  if (hidden) continue
524
528
  const inSection = parsed.filter(
@@ -530,6 +534,16 @@ function changelogFromCommits(commits, links = null) {
530
534
  lines.push('')
531
535
  }
532
536
 
537
+ // A conventional type nobody anticipated — `security:`, `i18n:` — is still a change
538
+ // someone made deliberately. Dropping it silently is how a security fix goes unmentioned.
539
+ // The hidden types are excluded because hiding them is the point.
540
+ const other = parsed.filter((c) => !known.has(c.type) && c.type !== 'feature')
541
+ if (other.length) {
542
+ lines.push('### Other Changes', '')
543
+ for (const c of other) lines.push(bullet(c, c.subject))
544
+ lines.push('')
545
+ }
546
+
533
547
  return lines.length ? lines.join('\n').trim() : null
534
548
  }
535
549
 
@@ -1188,7 +1202,20 @@ if (versionFile && !existsSync(versionFile.path)) {
1188
1202
  abort(`versionFile ${versionFile.path} does not exist`)
1189
1203
  }
1190
1204
 
1191
- const currentVersion = versionFile ? readVersionFrom(versionFile) : null
1205
+ /**
1206
+ * Where a repository versions by tag alone — a Go module, a docs site — the latest tag is
1207
+ * the current version. Without this, `auto` and every bump have nothing to work from and
1208
+ * the version has to be typed out in full every time.
1209
+ */
1210
+ function versionFromLastTag() {
1211
+ const tag = tryRead('git', ['describe', '--tags', '--abbrev=0'])
1212
+ if (!tag) return null
1213
+ const bare =
1214
+ config.tagPrefix && tag.startsWith(config.tagPrefix) ? tag.slice(config.tagPrefix.length) : tag
1215
+ return parseVersion(bare) ? bare : null
1216
+ }
1217
+
1218
+ const currentVersion = versionFile ? readVersionFrom(versionFile) : versionFromLastTag()
1192
1219
  if (versionFile && !currentVersion) {
1193
1220
  abort(`could not read a version from ${versionFile.path}`)
1194
1221
  }
@@ -1280,14 +1307,17 @@ let version
1280
1307
  if (!target) {
1281
1308
  if (!currentVersion) {
1282
1309
  abort(
1283
- 'this repository has no versionFile, so there is no version to default to.\n' +
1284
- ' Pass one explicitly: release-kit 1.2.3',
1310
+ 'nothing to default to: this repository has no versionFile and no tag to read a ' +
1311
+ 'version from.\n Pass one explicitly: release-kit 1.2.3',
1285
1312
  )
1286
1313
  }
1287
1314
  version = currentVersion
1288
1315
  } else if (target === 'auto') {
1289
1316
  if (!currentVersion) {
1290
- abort('auto needs a versionFile to bump from. Pass a version explicitly instead.')
1317
+ abort(
1318
+ 'auto has nothing to bump from: there is no versionFile and no tag to read a version ' +
1319
+ 'from.\n Pass the first version explicitly: release-kit 0.1.0',
1320
+ )
1291
1321
  }
1292
1322
  const { commits, lastTag } = commitsSinceLastTag()
1293
1323
  if (!commits.length) {
@@ -1310,7 +1340,10 @@ if (!target) {
1310
1340
  }
1311
1341
  } else if (BUMPS.has(target)) {
1312
1342
  if (!currentVersion) {
1313
- abort(`a ${target} bump needs a versionFile to bump from. Pass a version explicitly instead.`)
1343
+ abort(
1344
+ `a ${target} bump has nothing to bump from: no versionFile, and no tag to read a ` +
1345
+ 'version from.\n Pass the version explicitly instead.',
1346
+ )
1314
1347
  }
1315
1348
  const preid = requestedPreid ?? preidOf(currentVersion)
1316
1349
  if (target.startsWith('pre') && !preid) {