From ff5e1744979c60363943de417ecbd0b8a6f9910d Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Sun, 15 Oct 2023 20:53:16 -0400 Subject: [PATCH] chore(script): multiselect publishing prompt --- scripts/lib/getPackageDetails.ts | 79 ++++++++++++++++++++++++++++++++ scripts/list-packages.ts | 70 ++-------------------------- scripts/release.ts | 76 +++++++++++++++++++----------- 3 files changed, 131 insertions(+), 94 deletions(-) create mode 100644 scripts/lib/getPackageDetails.ts diff --git a/scripts/lib/getPackageDetails.ts b/scripts/lib/getPackageDetails.ts new file mode 100644 index 0000000000..cdb8a242a6 --- /dev/null +++ b/scripts/lib/getPackageDetails.ts @@ -0,0 +1,79 @@ +import path from 'path' +import fse from 'fs-extra' +import chalk from 'chalk' +import chalkTemplate from 'chalk-template' +import simpleGit from 'simple-git' + +const git = simpleGit() +const packagesDir = path.resolve(__dirname, '../../packages') + +export type PackageDetails = { + name: string + newCommits: number + shortName: string + packagePath: string + publishedVersion: string + publishDate: string + version: string +} + +export const getPackageDetails = async (): Promise => { + const packageDirs = fse.readdirSync(packagesDir).filter((d) => d !== 'eslint-config-payload') + const packageDetails = await Promise.all( + packageDirs.map(async (dirName) => { + const packageJson = await fse.readJson(`${packagesDir}/${dirName}/package.json`) + const isPublic = packageJson.private !== true + if (!isPublic) return null + + // Get published version from npm + const json = await fetch(`https://registry.npmjs.org/${packageJson.name}`).then((res) => + res.json(), + ) + + const publishedVersion = json?.['dist-tags']?.latest + const publishDate = json?.time?.[publishedVersion] + + const prevGitTag = `${dirName}/${packageJson.version}` + const prevGitTagHash = await git.revparse(prevGitTag) + + const newCommits = await git.log({ + from: prevGitTagHash, + file: `packages/${dirName}`, + }) + + return { + name: packageJson.name as string, + newCommits: newCommits.total, + shortName: dirName, + packagePath: `packages/${dirName}`, + publishedVersion, + publishDate, + version: packageJson.version, + } + }), + ) + + return packageDetails.filter((p): p is Exclude => p !== null) +} + +export const showPackageDetails = (details: PackageDetails[]) => { + console.log(chalkTemplate` + + {bold Packages:} + +${details + .map((p) => { + const name = p?.newCommits ? chalk.bold.green(p?.shortName.padEnd(28)) : p?.shortName.padEnd(28) + const publishData = `${p?.publishedVersion} at ${p?.publishDate + .split(':') + .slice(0, 2) + .join(':') + .replace('T', ' ')}` + const newCommits = `${p?.newCommits ? `${chalk.bold.green(p?.newCommits)} new commits` : ''}` + + return ` ${name}${publishData} ${newCommits}` + }) + .join('\n')} + +`) +} diff --git a/scripts/list-packages.ts b/scripts/list-packages.ts index 607a7005b1..a5773fcc88 100755 --- a/scripts/list-packages.ts +++ b/scripts/list-packages.ts @@ -1,74 +1,10 @@ -import path from 'path' -import fse from 'fs-extra' -import chalk from 'chalk' -import chalkTemplate from 'chalk-template' -import simpleGit from 'simple-git' - -const git = simpleGit() - -const packagesDir = path.resolve(__dirname, '../packages') +import { getPackageDetails, showPackageDetails } from './lib/getPackageDetails' async function main() { - // List all public packages excluding eslint-config-payload - const packageDirs = fse.readdirSync(packagesDir).filter((d) => d !== 'eslint-config-payload') - const packageDetails = await Promise.all( - packageDirs.map(async (dirName) => { - const packageJson = await fse.readJson(`${packagesDir}/${dirName}/package.json`) - const isPublic = packageJson.private !== true - if (!isPublic) return null - - // Get published version from npm - const json = await fetch(`https://registry.npmjs.org/${packageJson.name}`).then((res) => - res.json(), - ) - - const publishedVersion = json?.['dist-tags']?.latest - const publishDate = json?.time?.[publishedVersion] - - const prevGitTag = `${dirName}/${packageJson.version}` - const prevGitTagHash = await git.revparse(prevGitTag) - - const newCommits = await git.log({ - from: prevGitTagHash, - file: `packages/${dirName}`, - }) - - return { - name: packageJson.name, - newCommits: newCommits.total, - packageDir: dirName, - packagePath: `packages/${dirName}`, - publishedVersion, - publishDate, - version: packageJson.version, - } - }), - ) - console.log(chalkTemplate` - - {bold Packages:} - -${packageDetails - .map((p) => { - const name = p?.newCommits - ? chalk.bold.green(p?.packageDir.padEnd(28)) - : p?.packageDir.padEnd(28) - const publishData = `${p?.publishedVersion} at ${p?.publishDate - .split(':') - .slice(0, 2) - .join(':') - .replace('T', ' ')}` - const newCommits = `${p?.newCommits ? `${chalk.bold.green(p?.newCommits)} new commits` : ''}` - - return ` ${name}${publishData} ${newCommits}` - }) - .join('\n')} - -`) + const packageDetails = await getPackageDetails() + showPackageDetails(packageDetails) } -// console.log(packageNames) - main().catch((error) => { console.error(error) process.exit(1) diff --git a/scripts/release.ts b/scripts/release.ts index 2416781507..e07a5fee9d 100755 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -4,32 +4,49 @@ import chalk from 'chalk' import prompts from 'prompts' import minimist from 'minimist' import chalkTemplate from 'chalk-template' +import { PackageDetails, getPackageDetails, showPackageDetails } from './lib/getPackageDetails' const execOpts: ExecSyncOptions = { stdio: 'inherit' } const args = minimist(process.argv.slice(2)) async function main() { - const { _: packageNames, tag = 'latest', bump = 'patch' } = args + const { tag = 'latest', bump = 'patch' } = args - if (packageNames.length === 0) { + const packageDetails = await getPackageDetails() + showPackageDetails(packageDetails) + + const { packagesToRelease } = (await prompts({ + type: 'multiselect', + name: 'packagesToRelease', + message: 'Select packages to release', + instructions: 'Space to select. Enter to submit.', + choices: packageDetails.map((p) => { + const title = p?.newCommits ? chalk.bold.green(p?.shortName) : p?.shortName + return { + title, + value: p.shortName, + } + }), + })) as { packagesToRelease: string[] } + + if (!packagesToRelease) { + abort() + } + + if (packagesToRelease.length === 0) { abort('Please specify a package to publish') } - if (packageNames.find((p) => p === 'payload' && packageNames.length > 1)) { - abort('Cannot publish payload with other packages') + if (packagesToRelease.find((p) => p === 'payload' && packagesToRelease.length > 1)) { + abort('Cannot publish payload with other packages. Release Payload first.') } - // Get current version of each package from package.json - const packageDetails = await Promise.all( - packageNames.map(async (packageName) => { - const packageDir = `packages/${packageName}` - if (!(await fse.pathExists(packageDir))) { - abort(`Package path ${packageDir} does not exist`) - } - const packageObj = await fse.readJson(`${packageDir}/package.json`) - - return { name: packageName, version: packageObj.version, dir: packageDir } - }), + const packageMap = packageDetails.reduce( + (acc, p) => { + acc[p.shortName] = p + return acc + }, + {} as Record, ) console.log(chalkTemplate` @@ -38,10 +55,15 @@ async function main() { {bold.yellow Bump: ${bump}} {bold.yellow Tag: ${tag}} -${packageDetails.map((p) => ` ${p.name} - current: ${p.version}`).join('\n')} +${packagesToRelease + .map((p) => { + const { shortName, version } = packageMap[p] + return ` ${shortName.padEnd(24)} ${version}` + }) + .join('\n')} `) - const confirmPublish = await confirm(`Publish ${packageNames.length} package(s)?`) + const confirmPublish = await confirm(`Publish ${packagesToRelease.length} package(s)?`) if (!confirmPublish) { abort() @@ -49,26 +71,26 @@ ${packageDetails.map((p) => ` ${p.name} - current: ${p.version}`).join('\n')} const results: { name: string; success: boolean }[] = [] - for (const pkg of packageDetails) { - const { dir, name } = pkg + for (const pkg of packagesToRelease) { + const { packagePath, shortName } = packageMap[pkg] try { - console.log(chalk.bold(`\n\nPublishing ${name}...\n\n`)) + console.log(chalk.bold(`\n\nPublishing ${shortName}...\n\n`)) - execSync(`npm --no-git-tag-version --prefix ${dir} version ${bump}`, execOpts) - execSync(`git add ${dir}/package.json`, execOpts) + execSync(`npm --no-git-tag-version --prefix ${packagePath} version ${bump}`, execOpts) + execSync(`git add ${packagePath}/package.json`, execOpts) - const packageObj = await fse.readJson(`${dir}/package.json`) + const packageObj = await fse.readJson(`${packagePath}/package.json`) const newVersion = packageObj.version - const tagName = `${name}/${newVersion}` + const tagName = `${shortName}/${newVersion}` execSync(`git commit -m "chore(release): ${tagName}"`, execOpts) execSync(`git tag -a ${tagName} -m "${tagName}"`, execOpts) - execSync(`pnpm publish -C ${dir} --no-git-checks`, execOpts) - results.push({ name, success: true }) + execSync(`pnpm publish -C ${packagePath} --no-git-checks`, execOpts) + results.push({ name: shortName, success: true }) } catch (error) { console.error(chalk.bold.red(`ERROR: ${error.message}`)) - results.push({ name, success: false }) + results.push({ name: shortName, success: false }) } }