|
/* |
|
* Publish a pre-release ("dev") version to NPM. |
|
* |
|
* Computes the next dev version as `<next-target>-dev.<n>`, where: |
|
* - <next-target> is the latest published stable version with either the |
|
* minor or the major bumped by one and lower components reset to 0 |
|
* (e.g. `14.0.4` → `14.1.0` for a minor bump, or `14.0.4` → `15.0.0` |
|
* for a major bump). The bump kind is controlled by the `--major` flag; |
|
* the default is a minor bump. |
|
* - <n> is one greater than the highest existing dev counter for |
|
* <next-target>, or 1 if no dev release exists yet for it. |
|
* |
|
* The version is written to package.json, `npm publish --tag dev` is run, |
|
* and package.json is restored afterwards. |
|
*/ |
|
|
|
import { execSync } from 'node:child_process' |
|
import { readFileSync, writeFileSync } from 'node:fs' |
|
import path, { dirname } from 'node:path' |
|
import { fileURLToPath } from 'node:url' |
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url)) |
|
const packageJsonPath = path.join(__dirname, 'package.json') |
|
|
|
const parseStable = ( |
|
v: string, |
|
): { major: number; minor: number; patch: number } | null => { |
|
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(v) |
|
if (m === null) return null |
|
return { |
|
major: parseInt(m[1]!, 10), |
|
minor: parseInt(m[2]!, 10), |
|
patch: parseInt(m[3]!, 10), |
|
} |
|
} |
|
|
|
const cmp = ( |
|
a: { major: number; minor: number; patch: number }, |
|
b: { major: number; minor: number; patch: number }, |
|
): number => a.major - b.major || a.minor - b.minor || a.patch - b.patch |
|
|
|
const packageName = '@nrfcloud/billing-service-proto' |
|
|
|
const versionsOutput = execSync(`npm view ${packageName} versions --json`, { |
|
encoding: 'utf8', |
|
}) |
|
const versions = JSON.parse(versionsOutput) as string[] |
|
|
|
const stableVersions = versions |
|
.map(parseStable) |
|
.filter( |
|
(v): v is { major: number; minor: number; patch: number } => v !== null, |
|
) |
|
|
|
if (stableVersions.length === 0) { |
|
throw new Error(`No stable versions found for ${packageName}`) |
|
} |
|
|
|
const latestStable = stableVersions.reduce((a, b) => (cmp(a, b) >= 0 ? a : b)) |
|
|
|
const bumpMajor = process.argv.slice(2).includes('--major') |
|
|
|
const nextTarget = bumpMajor |
|
? `${latestStable.major + 1}.0.0` |
|
: `${latestStable.major}.${latestStable.minor + 1}.0` |
|
|
|
const devPattern = new RegExp( |
|
`^${nextTarget.replace(/\./g, '\\.')}-dev\\.(\\d+)$`, |
|
) |
|
const devCounters = versions |
|
.map((v) => devPattern.exec(v)) |
|
.filter((m): m is RegExpExecArray => m !== null) |
|
.map((m) => parseInt(m[1]!, 10)) |
|
|
|
const nextDev = (devCounters.length === 0 ? 0 : Math.max(...devCounters)) + 1 |
|
const nextVersion = `${nextTarget}-dev.${nextDev}` |
|
|
|
console.log( |
|
`Latest stable: ${latestStable.major}.${latestStable.minor}.${latestStable.patch}`, |
|
) |
|
console.log(`Publishing pre-release: ${nextVersion}`) |
|
|
|
const originalPackageJson = readFileSync(packageJsonPath, 'utf8') |
|
const pkg = JSON.parse(originalPackageJson) as { version: string } |
|
pkg.version = nextVersion |
|
|
|
const trailingNewline = originalPackageJson.endsWith('\n') ? '\n' : '' |
|
writeFileSync( |
|
packageJsonPath, |
|
JSON.stringify(pkg, null, 2) + trailingNewline, |
|
'utf8', |
|
) |
|
|
|
try { |
|
execSync(`npm publish --tag dev`, { |
|
stdio: 'inherit', |
|
cwd: __dirname, |
|
}) |
|
} finally { |
|
writeFileSync(packageJsonPath, originalPackageJson, 'utf8') |
|
} |