ci: update release script
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { ExecSyncOptions } from 'child_process'
|
||||
|
||||
import chalk from 'chalk'
|
||||
import chalkTemplate from 'chalk-template'
|
||||
import { ExecSyncOptions, execSync } from 'child_process'
|
||||
import { execSync } from 'child_process'
|
||||
import execa from 'execa'
|
||||
import fse from 'fs-extra'
|
||||
import minimist from 'minimist'
|
||||
@@ -8,6 +9,7 @@ import path from 'path'
|
||||
import prompts from 'prompts'
|
||||
import semver from 'semver'
|
||||
import simpleGit from 'simple-git'
|
||||
|
||||
import { getPackageDetails } from './lib/getPackageDetails'
|
||||
import { updateChangelog } from './utils/updateChangelog'
|
||||
|
||||
@@ -18,17 +20,25 @@ const git = simpleGit(rootPath)
|
||||
const execOpts: ExecSyncOptions = { stdio: 'inherit' }
|
||||
const args = minimist(process.argv.slice(2))
|
||||
|
||||
const { tag = 'latest', bump = 'patch', 'dry-run': dryRun = true, changelog = false } = args
|
||||
const {
|
||||
bump = 'patch',
|
||||
changelog = false,
|
||||
'dry-run': dryRun,
|
||||
'git-tag': gitTag = false,
|
||||
tag = 'latest',
|
||||
} = args
|
||||
|
||||
const logPrefix = dryRun ? chalk.bold.magenta('[dry-run] >') : ''
|
||||
|
||||
const cmdRunner = (dryRun: boolean) => (cmd: string, execOpts: ExecSyncOptions) => {
|
||||
if (dryRun) {
|
||||
console.log(logPrefix, cmd)
|
||||
} else {
|
||||
execSync(cmd, execOpts)
|
||||
const cmdRunner =
|
||||
(dryRun: boolean, gitTag: boolean) => (cmd: string, execOpts: ExecSyncOptions) => {
|
||||
const isGitCommand = cmd.startsWith('git')
|
||||
if (dryRun || (isGitCommand && !gitTag)) {
|
||||
console.log(logPrefix, cmd)
|
||||
} else {
|
||||
execSync(cmd, execOpts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cmdRunnerAsync =
|
||||
(dryRun: boolean) => async (cmd: string, args: string[], options?: execa.Options) => {
|
||||
@@ -45,7 +55,9 @@ async function main() {
|
||||
console.log(chalk.bold.yellow(chalk.bold.magenta('\n 👀 Dry run mode enabled')))
|
||||
}
|
||||
|
||||
const runCmd = cmdRunner(dryRun)
|
||||
console.log({ args })
|
||||
|
||||
const runCmd = cmdRunner(dryRun, gitTag)
|
||||
const runCmdAsync = cmdRunnerAsync(dryRun)
|
||||
|
||||
if (!semver.RELEASE_TYPES.includes(bump)) {
|
||||
@@ -64,29 +76,29 @@ async function main() {
|
||||
|
||||
const lastTag = (await git.tags()).all.reverse().filter((t) => t.startsWith('v'))?.[0]
|
||||
|
||||
if (monorepoVersion !== lastTag.replace('v', '')) {
|
||||
throw new Error(
|
||||
`Version in package.json (${monorepoVersion}) does not match last tag (${lastTag})`,
|
||||
)
|
||||
// TODO: Re-enable this check once we start tagging releases again
|
||||
// if (monorepoVersion !== lastTag.replace('v', '')) {
|
||||
// throw new Error(
|
||||
// `Version in package.json (${monorepoVersion}) does not match last tag (${lastTag})`,
|
||||
// )
|
||||
// }
|
||||
|
||||
const nextReleaseVersion = semver.inc(monorepoVersion, bump, undefined, tag)
|
||||
|
||||
if (!nextReleaseVersion) {
|
||||
abort(`Invalid nextReleaseVersion: ${nextReleaseVersion}`)
|
||||
return // For TS type checking
|
||||
}
|
||||
const nextReleaseVersion = semver.inc(monorepoVersion, bump, undefined, tag) as string
|
||||
|
||||
const packageDetails = await getPackageDetails()
|
||||
|
||||
console.log(chalkTemplate`
|
||||
{bold Version: ${monorepoVersion} => {green ${nextReleaseVersion}}}
|
||||
|
||||
{bold.yellow Bump: ${bump}}
|
||||
{bold.yellow Tag: ${tag}}
|
||||
|
||||
{bold.green Changes (${packageDetails.length} packages):}
|
||||
|
||||
${packageDetails
|
||||
.map((p) => {
|
||||
return ` - ${p.name.padEnd(32)} ${p.version} => ${chalk.green(nextReleaseVersion)}`
|
||||
})
|
||||
.join('\n')}
|
||||
`)
|
||||
console.log(chalk.bold(`\n Version: ${monorepoVersion} => ${chalk.green(nextReleaseVersion)}\n`))
|
||||
console.log(chalk.bold.yellow(` Bump: ${bump}`))
|
||||
console.log(chalk.bold.yellow(` Tag: ${tag}\n`))
|
||||
console.log(chalk.bold.green(` Changes (${packageDetails.length} packages):\n`))
|
||||
console.log(
|
||||
`${packageDetails.map((p) => ` - ${p.name.padEnd(32)} ${p.version} => ${chalk.green(nextReleaseVersion)}`).join('\n')}\n`,
|
||||
)
|
||||
|
||||
const confirmPublish = await confirm('Are you sure your want to create these versions?')
|
||||
|
||||
@@ -96,7 +108,12 @@ ${packageDetails
|
||||
|
||||
// Prebuild all packages
|
||||
header(`\n🔨 Prebuilding all packages...`)
|
||||
const buildResult = await execa('pnpm', ['build:all'], {
|
||||
await execa('pnpm', ['install'], {
|
||||
cwd: rootPath,
|
||||
stdio: 'inherit',
|
||||
})
|
||||
|
||||
const buildResult = await execa('pnpm', ['build:core', '--output-logs=errors-only'], {
|
||||
cwd: rootPath,
|
||||
// stdio: ['ignore', 'ignore', 'pipe'],
|
||||
stdio: 'inherit',
|
||||
@@ -145,53 +162,44 @@ ${packageDetails
|
||||
runCmd(`git commit -m "chore(release): v${nextReleaseVersion} [skip ci]"`, execOpts)
|
||||
|
||||
// Tag
|
||||
header(`🏷️ Tagging release v${nextReleaseVersion}`)
|
||||
header(`🏷️ Tagging release v${nextReleaseVersion}`, { enable: gitTag })
|
||||
runCmd(`git tag -a v${nextReleaseVersion} -m "v${nextReleaseVersion}"`, execOpts)
|
||||
|
||||
const otp = dryRun ? undefined : await question('Enter your 2FA code')
|
||||
|
||||
// Publish
|
||||
const results: { name: string; success: boolean }[] = await Promise.all(
|
||||
packageDetails.map(async (pkg) => {
|
||||
try {
|
||||
console.log(logPrefix, chalk.bold(`🚀 ${pkg.name} publishing...`))
|
||||
const cmdArgs = [
|
||||
'publish',
|
||||
'-C',
|
||||
pkg.packagePath,
|
||||
'--no-git-checks',
|
||||
'--tag',
|
||||
tag,
|
||||
'--dry-run', // TODO: Use dryRun var
|
||||
]
|
||||
const cmdArgs = ['publish', '-C', pkg.packagePath, '--no-git-checks', '--tag', tag]
|
||||
if (dryRun) {
|
||||
cmdArgs.push('--dry-run')
|
||||
} else {
|
||||
cmdArgs.push('--otp', otp)
|
||||
}
|
||||
const { exitCode } = await execa('pnpm', cmdArgs, {
|
||||
cwd: rootPath,
|
||||
// stdio: ['ignore', 'ignore', 'pipe'],
|
||||
stdio: 'inherit',
|
||||
stdio: ['ignore', 'ignore', 'pipe'],
|
||||
// stdio: 'inherit',
|
||||
})
|
||||
|
||||
if (exitCode !== 0) {
|
||||
console.log(chalk.bold.red(`\n\np❌ ${pkg.name} ERROR: pnpm publish failed\n\n`))
|
||||
console.log(chalk.bold.red(`\n\n❌ ${pkg.name} ERROR: pnpm publish failed\n\n`))
|
||||
return { name: pkg.name, success: false }
|
||||
}
|
||||
|
||||
console.log(chalk.green(`✅ ${pkg.name} published`))
|
||||
console.log(`${logPrefix} ${chalk.green(`✅ ${pkg.name} published`)}`)
|
||||
return { name: pkg.name, success: true }
|
||||
} catch (error) {
|
||||
console.error(chalk.bold.red(`\n\np❌ ${pkg.name} ERROR: ${error.message}\n\n`))
|
||||
console.error(chalk.bold.red(`\n\n❌ ${pkg.name} ERROR: ${error.message}\n\n`))
|
||||
return { name: pkg.name, success: false }
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
console.log(chalkTemplate`
|
||||
|
||||
{bold.green Results:}
|
||||
|
||||
${results
|
||||
.map(
|
||||
({ name, success }) => ` ${success ? chalk.bold.green('✅') : chalk.bold.red('❌')} ${name}`,
|
||||
)
|
||||
.join('\n')}
|
||||
`)
|
||||
console.log(chalk.bold.green(`\n\nResults:\n\n`))
|
||||
console.log(results.map(({ name, success }) => ` ${success ? '✅' : '❌'} ${name}`).join('\n'))
|
||||
|
||||
// TODO: Push commit and tag
|
||||
// const push = await confirm(`Push commits and tags?`)
|
||||
@@ -208,7 +216,7 @@ main().catch((error) => {
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
async function abort(message = 'Abort', exitCode = 1) {
|
||||
function abort(message = 'Abort', exitCode = 1) {
|
||||
console.error(chalk.bold.red(`\n${message}\n`))
|
||||
process.exit(exitCode)
|
||||
}
|
||||
@@ -217,9 +225,9 @@ async function confirm(message: string): Promise<boolean> {
|
||||
const { confirm } = await prompts(
|
||||
{
|
||||
name: 'confirm',
|
||||
type: 'confirm',
|
||||
initial: false,
|
||||
message,
|
||||
type: 'confirm',
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
@@ -231,6 +239,26 @@ async function confirm(message: string): Promise<boolean> {
|
||||
return confirm
|
||||
}
|
||||
|
||||
async function header(message: string) {
|
||||
async function question(message: string): Promise<string> {
|
||||
const { value } = await prompts(
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
message,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
abort()
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
function header(message: string, opts?: { enable?: boolean }) {
|
||||
const { enable } = opts ?? {}
|
||||
if (!enable) return
|
||||
|
||||
console.log(chalk.bold.green(`${message}\n`))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user