Add release model CLI
This commit is contained in:
@@ -51,3 +51,7 @@ androidVersionCode: 1110
|
||||
`sourceTag` must equal `releaseVersion`, `sourceSha` must be a full Git object ID, and both build numbers must be positive integers.
|
||||
|
||||
Each successful OTA adds exactly one contiguous section (`OTA 1`, `OTA 2`, and so on) inside the public changelog delimiters. GitHub Release text is extracted only from those delimiters; operational frontmatter is never published.
|
||||
|
||||
## Command line usage
|
||||
|
||||
The release model can be exercised locally with `node scripts/release/cli.mjs`. Run it without arguments to see the available commands for creating, validating, finalizing, and updating a release document.
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import {readFileSync, writeFileSync} from 'node:fs'
|
||||
|
||||
import {
|
||||
ReleaseError,
|
||||
appendOtaChangelog,
|
||||
createReleaseDocument,
|
||||
deriveReleaseIdentity,
|
||||
extractPublicChangelog,
|
||||
finalizeReleaseDocument,
|
||||
parseReleaseDocument,
|
||||
} from './model.mjs'
|
||||
|
||||
function usage() {
|
||||
return `Usage:
|
||||
node scripts/release/cli.mjs identity <version>
|
||||
node scripts/release/cli.mjs create <version> <changelog-file> <output-file>
|
||||
node scripts/release/cli.mjs validate <release-file> [prepared|final]
|
||||
node scripts/release/cli.mjs extract-public <release-file>
|
||||
node scripts/release/cli.mjs append-ota <release-file> <sequence> <changelog-file>
|
||||
node scripts/release/cli.mjs finalize <release-file> <source-tag> <source-sha> <ios-build-number> <android-version-code>`
|
||||
}
|
||||
|
||||
function requireArgs(args, count) {
|
||||
if (args.length !== count) throw new ReleaseError(usage())
|
||||
}
|
||||
|
||||
function read(path) {
|
||||
return readFileSync(path, 'utf8')
|
||||
}
|
||||
|
||||
function write(path, content) {
|
||||
writeFileSync(path, content, 'utf8')
|
||||
}
|
||||
|
||||
function run([command, ...args]) {
|
||||
switch (command) {
|
||||
case 'identity': {
|
||||
requireArgs(args, 1)
|
||||
console.log(JSON.stringify(deriveReleaseIdentity(args[0]), null, 2))
|
||||
return
|
||||
}
|
||||
case 'create': {
|
||||
requireArgs(args, 3)
|
||||
const [version, changelogFile, outputFile] = args
|
||||
write(outputFile, createReleaseDocument(version, read(changelogFile)))
|
||||
console.log(`Created ${outputFile}`)
|
||||
return
|
||||
}
|
||||
case 'validate': {
|
||||
if (args.length < 1 || args.length > 2) throw new ReleaseError(usage())
|
||||
const [releaseFile, stage = 'prepared'] = args
|
||||
const parsed = parseReleaseDocument(read(releaseFile), {
|
||||
filename: releaseFile.split('/').at(-1),
|
||||
stage,
|
||||
})
|
||||
console.log(
|
||||
`Validated ${releaseFile} as a ${stage} release for ${parsed.metadata.releaseVersion}`,
|
||||
)
|
||||
return
|
||||
}
|
||||
case 'extract-public': {
|
||||
requireArgs(args, 1)
|
||||
process.stdout.write(`${extractPublicChangelog(read(args[0]))}\n`)
|
||||
return
|
||||
}
|
||||
case 'append-ota': {
|
||||
requireArgs(args, 3)
|
||||
const [releaseFile, rawSequence, changelogFile] = args
|
||||
const sequence = Number(rawSequence)
|
||||
write(
|
||||
releaseFile,
|
||||
appendOtaChangelog(read(releaseFile), sequence, read(changelogFile)),
|
||||
)
|
||||
console.log(`Added OTA ${rawSequence} to ${releaseFile}`)
|
||||
return
|
||||
}
|
||||
case 'finalize': {
|
||||
requireArgs(args, 5)
|
||||
const [
|
||||
releaseFile,
|
||||
sourceTag,
|
||||
sourceSha,
|
||||
iosBuildNumber,
|
||||
androidVersionCode,
|
||||
] = args
|
||||
write(
|
||||
releaseFile,
|
||||
finalizeReleaseDocument(read(releaseFile), {
|
||||
sourceTag,
|
||||
sourceSha,
|
||||
iosBuildNumber,
|
||||
androidVersionCode,
|
||||
}),
|
||||
)
|
||||
console.log(`Finalized ${releaseFile}`)
|
||||
return
|
||||
}
|
||||
default:
|
||||
throw new ReleaseError(usage())
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
run(process.argv.slice(2))
|
||||
} catch (error) {
|
||||
if (error instanceof ReleaseError) {
|
||||
console.error(`Error: ${error.message}`)
|
||||
process.exitCode = 1
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user