@entro314labs/release-kit 2.9.3 → 2.10.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.
package/train.mjs CHANGED
@@ -78,6 +78,11 @@ export function parseSemver(version) {
78
78
  return { major: +match[1], minor: +match[2], patch: +match[3], prerelease: match[4] ?? null }
79
79
  }
80
80
 
81
+ /**
82
+ * Precedence per semver §11, the same ordering release.mjs `compareVersions` applies. It
83
+ * has to be, because this decides which tag is a package's last release: ranking every
84
+ * prerelease equal left `rc.9` and `rc.10` in `git tag` order, which is alphabetical.
85
+ */
81
86
  export function compareSemver(a, b) {
82
87
  const pa = parseSemver(a)
83
88
  const pb = parseSemver(b)
@@ -85,18 +90,45 @@ export function compareSemver(a, b) {
85
90
  for (const key of ['major', 'minor', 'patch']) {
86
91
  if (pa[key] !== pb[key]) return pa[key] - pb[key]
87
92
  }
88
- if (pa.prerelease && !pb.prerelease) return -1
89
- if (!pa.prerelease && pb.prerelease) return 1
93
+ if (!pa.prerelease && !pb.prerelease) return 0
94
+ if (!pa.prerelease) return 1
95
+ if (!pb.prerelease) return -1
96
+ const x = pa.prerelease.split('.')
97
+ const y = pb.prerelease.split('.')
98
+ for (let i = 0; i < Math.max(x.length, y.length); i += 1) {
99
+ const left = x[i]
100
+ const right = y[i]
101
+ if (left === undefined) return -1
102
+ if (right === undefined) return 1
103
+ if (left === right) continue
104
+ const leftNumeric = /^\d+$/.test(left)
105
+ const rightNumeric = /^\d+$/.test(right)
106
+ if (leftNumeric && rightNumeric) return Number(left) - Number(right)
107
+ // Numeric identifiers always have lower precedence than alphanumeric ones.
108
+ if (leftNumeric !== rightNumeric) return leftNumeric ? -1 : 1
109
+ return left < right ? -1 : 1
110
+ }
90
111
  return 0
91
112
  }
92
113
 
114
+ /**
115
+ * The bumps a train plans, with release.mjs `incrementVersion`'s arithmetic for them: a
116
+ * major/minor/patch off a prerelease releases that prerelease's base when the base already
117
+ * satisfies the bump, so `2.0.0-beta.1` + `patch` is `2.0.0` — not the `2.0.1` a naive
118
+ * increment gives, which release-kit would then never produce.
119
+ */
93
120
  export function bumpSemver(version, bump) {
94
121
  const v = parseSemver(version)
95
122
  if (!v) return null
96
123
  if (bump === 'as-is') return version
97
- if (bump === 'major') return `${v.major + 1}.0.0`
98
- if (bump === 'minor') return `${v.major}.${v.minor + 1}.0`
99
- return `${v.major}.${v.minor}.${v.patch + 1}`
124
+ const pre = v.prerelease !== null
125
+ if (bump === 'major') {
126
+ return pre && v.minor === 0 && v.patch === 0 ? `${v.major}.0.0` : `${v.major + 1}.0.0`
127
+ }
128
+ if (bump === 'minor') {
129
+ return pre && v.patch === 0 ? `${v.major}.${v.minor}.0` : `${v.major}.${v.minor + 1}.0`
130
+ }
131
+ return pre ? `${v.major}.${v.minor}.${v.patch}` : `${v.major}.${v.minor}.${v.patch + 1}`
100
132
  }
101
133
 
102
134
  /**
@@ -339,6 +371,8 @@ export function discover(rootDir, config) {
339
371
  repoRelPath: repoDir ? relative(repoDir, dir) || '.' : null,
340
372
  publish: entry.publish !== false,
341
373
  branch: releaseConfig.branch === undefined ? 'main' : releaseConfig.branch,
374
+ // The prefix release-kit will tag with, and so the one its history is read under.
375
+ tagPrefix: releaseConfig.tagPrefix ?? 'v',
342
376
  ...manifest,
343
377
  })
344
378
  }
@@ -416,19 +450,24 @@ export function topoSort(orderEdges) {
416
450
  // ─────────────────────────────────────────────────────────────────────────────
417
451
 
418
452
  /**
419
- * Tag scheme: `v<version>` when the repo owns exactly one member, `<name>@<version>` when
420
- * it owns several — which keeps single-package repos identical to standalone release-kit.
453
+ * Tag scheme: the package's own `tagPrefix` (`v` unless its release.config.json says
454
+ * otherwise) when the repo owns exactly one member, `<name>@<version>` when it owns
455
+ * several — which keeps single-package repos identical to standalone release-kit.
421
456
  */
422
457
  export function tagPatternFor(member, repoMemberCount) {
423
- return repoMemberCount > 1
424
- ? { prefix: `${member.name}@`, glob: `${member.name}@*` }
425
- : { prefix: 'v', glob: 'v*' }
458
+ if (repoMemberCount > 1) return { prefix: `${member.name}@`, glob: `${member.name}@*` }
459
+ const prefix = member.tagPrefix ?? 'v'
460
+ return { prefix, glob: `${prefix}*` }
426
461
  }
427
462
 
428
463
  function lastReleaseTag(member, repoMemberCount) {
429
464
  if (!member.repoDir) return null
430
465
  const { prefix, glob } = tagPatternFor(member, repoMemberCount)
431
- const tags = git(member.repoDir, ['tag', '--list', glob], { allowFailure: true })
466
+ // `--merged HEAD`, as release.mjs reads it: a tag made on another branch is not this
467
+ // branch's last release, and the commits it would hide are still unreleased here.
468
+ const tags = git(member.repoDir, ['tag', '--list', glob, '--merged', 'HEAD'], {
469
+ allowFailure: true,
470
+ })
432
471
  if (!tags) return null
433
472
  const versions = tags
434
473
  .split('\n')