ci: update release script

This commit is contained in:
Elliot DeNolf
2024-03-03 13:01:59 -05:00
parent 48fbedd8ee
commit 399b799002
8 changed files with 326 additions and 188 deletions

View File

@@ -5,13 +5,24 @@ const packagesDir = path.resolve(__dirname, '../../packages')
export type PackageDetails = {
name: string
shortName: string
packagePath: string
shortName: string
version: string
}
const whitelist3_0 = [
'payload',
'translations',
'ui',
'next',
'graphql',
'db-mongodb',
'richtext-slate',
]
export const getPackageDetails = async (): Promise<PackageDetails[]> => {
const packageDirs = fse.readdirSync(packagesDir).filter((d) => d !== 'eslint-config-payload')
const packageDirs = fse.readdirSync(packagesDir).filter((d) => whitelist3_0.includes(d))
const packageDetails = await Promise.all(
packageDirs.map(async (dirName) => {
@@ -21,8 +32,8 @@ export const getPackageDetails = async (): Promise<PackageDetails[]> => {
return {
name: packageJson.name as string,
shortName: dirName,
packagePath: path.resolve(packagesDir, dirName),
shortName: dirName,
version: packageJson.version,
}
}),

View File

@@ -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`))
}

View File

@@ -1,24 +1,29 @@
import fse, { createWriteStream, createReadStream } from 'fs-extra'
import { ExecSyncOptions, execSync } from 'child_process'
import chalk from 'chalk'
import path from 'path'
import prompts from 'prompts'
import minimist from 'minimist'
import chalkTemplate from 'chalk-template'
import { PackageDetails, getPackageDetails, showPackageDetails } from './lib/getPackageDetails'
import semver from 'semver'
import type { ExecSyncOptions } from 'child_process'
import addStream from 'add-stream'
import tempfile from 'tempfile'
import chalk from 'chalk'
import chalkTemplate from 'chalk-template'
import { execSync } from 'child_process'
import concatSream from 'concat-stream'
import getStream from 'get-stream'
import conventionalChangelog, { Options as ChangelogOptions } from 'conventional-changelog'
import conventionalChangelogCore, {
Options,
Context,
GitRawCommitsOptions,
Options,
ParserOptions,
WriterOptions,
} from 'conventional-changelog-core'
import conventionalChangelog, { Options as ChangelogOptions } from 'conventional-changelog'
import fse, { createReadStream, createWriteStream } from 'fs-extra'
import getStream from 'get-stream'
import minimist from 'minimist'
import path from 'path'
import prompts from 'prompts'
import semver from 'semver'
import tempfile from 'tempfile'
import type { PackageDetails } from './lib/getPackageDetails'
import { getPackageDetails, showPackageDetails } from './lib/getPackageDetails'
const execOpts: ExecSyncOptions = { stdio: 'inherit' }
const args = minimist(process.argv.slice(2))
@@ -65,7 +70,7 @@ async function main() {
abort()
}
const nextReleaseVersion = semver.inc(pkg.version, bump) as string
const nextReleaseVersion = semver.inc(pkg.version, bump)
const changelogStream = conventionalChangelog(
{
preset: 'conventionalcommits',

View File

@@ -1,15 +1,18 @@
import type {
GitRawCommitsOptions,
ParserOptions,
WriterOptions,
} from 'conventional-changelog-core'
import { Octokit } from '@octokit/core'
import addStream from 'add-stream'
import conventionalChangelog from 'conventional-changelog'
import { default as getConventionalPreset } from 'conventional-changelog-conventionalcommits'
import { GitRawCommitsOptions, ParserOptions, WriterOptions } from 'conventional-changelog-core'
import { once } from 'events'
import fse, { createReadStream, createWriteStream } from 'fs-extra'
import minimist from 'minimist'
import semver, { ReleaseType } from 'semver'
import tempfile from 'tempfile'
import { Octokit } from '@octokit/core'
import simpleGit from 'simple-git'
import { once } from 'events'
import tempfile from 'tempfile'
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
const git = simpleGit()
@@ -36,11 +39,10 @@ export const updateChangelog = async ({ newVersion, dryRun }: Args) => {
// Load conventional commits preset and modify it
const conventionalPreset = (await getConventionalPreset()) as {
conventionalChangelog: unknown
gitRawCommitsOpts: GitRawCommitsOptions
parserOpts: ParserOptions
writerOpts: WriterOptions
recommmendBumpOpts: unknown
conventionalChangelog: unknown
}
// Unbold scope
@@ -106,7 +108,13 @@ export const updateChangelog = async ({ newVersion, dryRun }: Args) => {
// If file is executed directly, run the function
if (require.main === module) {
const { newVersion } = minimist(process.argv.slice(2))
updateChangelog({ newVersion, dryRun: true })
updateChangelog({ dryRun: true, newVersion })
.then(() => {
console.log('Done')
})
.catch((err) => {
console.error(err)
})
}
async function createContributorSection(lastTag: string): Promise<string> {
@@ -120,7 +128,7 @@ async function createContributorSection(lastTag: string): Promise<string> {
repo: 'payload',
ref: c.hash,
})
.then(({ data }) => data.author?.login as string),
.then(({ data }) => data.author?.login),
),
)