diff --git a/package.json b/package.json index 56129200f..a0fcb358e 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "reinstall": "pnpm clean:all && pnpm install", "release:alpha": "tsx ./scripts/release.ts --bump prerelease --tag alpha", "release:beta": "tsx ./scripts/release.ts --bump prerelease --tag beta", + "script:list-published": "tsx scripts/lib/getPackageRegistryVersions.ts", "script:pack": "tsx scripts/pack-all-to-dest.ts", "pretest": "pnpm build", "test": "pnpm test:int && pnpm test:components && pnpm test:e2e", diff --git a/scripts/lib/getPackageRegistryVersions.ts b/scripts/lib/getPackageRegistryVersions.ts index 7f0667707..faea119ea 100644 --- a/scripts/lib/getPackageRegistryVersions.ts +++ b/scripts/lib/getPackageRegistryVersions.ts @@ -2,43 +2,12 @@ import chalk from 'chalk' import pLimit from 'p-limit' import { getPackageDetails } from './getPackageDetails.js' +import { packageWhitelist } from './whitelist.js' const npmRequestLimit = pLimit(40) -const packages = [ - 'payload', - 'translations', - 'ui', - 'next', - 'graphql', - 'db-mongodb', - 'db-postgres', - 'richtext-slate', - 'richtext-lexical', - - 'create-payload-app', - - 'email-nodemailer', - - 'storage-s3', - 'storage-azure', - 'storage-gcs', - 'storage-vercel-blob', - - // Plugins - 'plugin-cloud', - 'plugin-cloud-storage', - 'plugin-form-builder', - 'plugin-nested-docs', - 'plugin-redirects', - 'plugin-search', - 'plugin-seo', - // 'plugin-stripe', - // 'plugin-sentry', -] - export const getPackageRegistryVersions = async (): Promise => { - const packageDetails = await getPackageDetails(packages) + const packageDetails = await getPackageDetails(packageWhitelist) const results = await Promise.all( packageDetails.map(async (pkg) => diff --git a/scripts/lib/whitelist.ts b/scripts/lib/whitelist.ts new file mode 100644 index 000000000..dd45aeff7 --- /dev/null +++ b/scripts/lib/whitelist.ts @@ -0,0 +1,34 @@ +// Update this list with any packages to publish +export const packageWhitelist = [ + 'payload', + 'translations', + 'ui', + 'next', + 'graphql', + 'db-mongodb', + 'db-postgres', + 'richtext-slate', + 'richtext-lexical', + + 'create-payload-app', + + // Adapters + 'email-nodemailer', + 'email-resend', + + 'storage-s3', + 'storage-azure', + 'storage-gcs', + 'storage-vercel-blob', + + // Plugins + 'plugin-cloud', + 'plugin-cloud-storage', + 'plugin-form-builder', + 'plugin-nested-docs', + 'plugin-redirects', + 'plugin-search', + 'plugin-seo', + 'plugin-stripe', + // 'plugin-sentry', +] diff --git a/scripts/release.ts b/scripts/release.ts index 457571748..2625bbb6b 100755 --- a/scripts/release.ts +++ b/scripts/release.ts @@ -16,46 +16,12 @@ import { simpleGit } from 'simple-git' import type { PackageDetails } from './lib/getPackageDetails.js' import { getPackageDetails } from './lib/getPackageDetails.js' +import { packageWhitelist } from './lib/whitelist.js' import { getRecommendedBump } from './utils/getRecommendedBump.js' import { updateChangelog } from './utils/updateChangelog.js' const npmPublishLimit = pLimit(6) -// Update this list with any packages to publish -const packageWhitelist = [ - 'payload', - 'translations', - 'ui', - 'next', - 'graphql', - 'db-mongodb', - 'db-postgres', - 'richtext-slate', - 'richtext-lexical', - - 'create-payload-app', - - // Adapters - 'email-nodemailer', - 'email-resend', - - 'storage-s3', - 'storage-azure', - 'storage-gcs', - 'storage-vercel-blob', - - // Plugins - 'plugin-cloud', - 'plugin-cloud-storage', - 'plugin-form-builder', - 'plugin-nested-docs', - 'plugin-redirects', - 'plugin-search', - 'plugin-seo', - 'plugin-stripe', - // 'plugin-sentry', -] - const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) const cwd = path.resolve(dirname, '..') @@ -162,7 +128,11 @@ async function main() { // Preview/Update changelog header(`${logPrefix}šŸ“ Updating changelog...`) - await updateChangelog({ + const { + changelog: changelogContent, + releaseNotes, + releaseUrl, + } = await updateChangelog({ bump, dryRun, toVersion: 'HEAD', @@ -171,6 +141,10 @@ async function main() { writeChangelog: changelog, }) + console.log(chalk.green('\nChangelog Preview:\n')) + console.log(chalk.gray(changelogContent) + '\n\n') + console.log(`Release URL: ${chalk.dim(releaseUrl)}`) + let packageDetails = await getPackageDetails(packageWhitelist) console.log(chalk.bold(`\n Version: ${monorepoVersion} => ${chalk.green(nextReleaseVersion)}\n`)) @@ -238,7 +212,7 @@ async function main() { packageDetails = packageDetails.filter((p) => p.name !== 'payload') runCmd(`pnpm publish -C packages/payload --no-git-checks --json --tag ${tag}`, execOpts) - const results = await Promise.all( + const results = await Promise.allSettled( packageDetails.map((pkg) => publishPackageThrottled(pkg, { dryRun })), ) @@ -247,7 +221,12 @@ async function main() { // New results format console.log( results - .map(({ name, success, details }) => { + .map((result) => { + if (result.status === 'rejected') { + console.error(result.reason) + return ` āŒ ${String(result.reason)}` + } + const { name, success, details } = result.value let summary = ` ${success ? 'āœ…' : 'āŒ'} ${name}` if (details) { summary += `\n ${details}\n` @@ -265,6 +244,8 @@ async function main() { // } header('šŸŽ‰ Done!') + + console.log(chalk.bold.green(`\n\nRelease URL: ${releaseUrl}`)) } main().catch((error) => { @@ -281,23 +262,36 @@ async function publishPackageThrottled(pkg: PackageDetails, opts?: { dryRun?: bo async function publishSinglePackage(pkg: PackageDetails, opts?: { dryRun?: boolean }) { const { dryRun = false } = opts ?? {} console.log(chalk.bold(`šŸš€ ${pkg.name} publishing...`)) - const cmdArgs = ['publish', '-C', pkg.packagePath, '--no-git-checks', '--json', '--tag', tag] - if (dryRun) { - cmdArgs.push('--dry-run') - } - const { exitCode, stderr } = await execa('pnpm', cmdArgs, { - cwd, - stdio: ['ignore', 'ignore', 'pipe'], - // stdio: 'inherit', - }) - if (exitCode !== 0) { - console.log(chalk.bold.red(`\n\nāŒ ${pkg.name} ERROR: pnpm publish failed\n\n${stderr}`)) - return { name: pkg.name, success: false, details: stderr } - } + try { + const cmdArgs = ['publish', '-C', pkg.packagePath, '--no-git-checks', '--json', '--tag', tag] + if (dryRun) { + cmdArgs.push('--dry-run') + } + const { exitCode, stderr } = await execa('pnpm', cmdArgs, { + cwd, + stdio: ['ignore', 'ignore', 'pipe'], + // stdio: 'inherit', + }) - console.log(`${logPrefix} ${chalk.green(`āœ… ${pkg.name} published`)}`) - return { name: pkg.name, success: true } + if (exitCode !== 0) { + console.log(chalk.bold.red(`\n\nāŒ ${pkg.name} ERROR: pnpm publish failed\n\n${stderr}`)) + return { name: pkg.name, success: false, details: stderr } + } + + console.log(`${logPrefix} ${chalk.green(`āœ… ${pkg.name} published`)}`) + return { name: pkg.name, success: true } + } catch (err: unknown) { + console.error(err) + return { + name: pkg.name, + success: false, + details: + err instanceof Error + ? `Error publishing ${pkg.name}: ${err.message}` + : `Unexpected error publishing ${pkg.name}: ${String(err)}`, + } + } } function abort(message = 'Abort', exitCode = 1) { diff --git a/scripts/utils/updateChangelog.ts b/scripts/utils/updateChangelog.ts index 038f5e203..4ad2d2032 100755 --- a/scripts/utils/updateChangelog.ts +++ b/scripts/utils/updateChangelog.ts @@ -19,7 +19,22 @@ type Args = { writeChangelog?: boolean } -export const updateChangelog = async (args: Args = {}) => { +type ChangelogResult = { + /** + * URL to open releases/new with the changelog pre-filled + */ + releaseUrl: string + /** + * The changelog content, does not include contributors + */ + changelog: string + /** + * The release notes, includes contributors. This is the content used for the releaseUrl + */ + releaseNotes: string +} + +export const updateChangelog = async (args: Args = {}): Promise => { const { toVersion = 'HEAD', dryRun, bump, openReleaseUrl, writeChangelog } = args const fromVersion = @@ -31,7 +46,7 @@ export const updateChangelog = async (args: Args = {}) => { tag !== 'latest' ? 'prerelease' : await getRecommendedBump(fromVersion, toVersion) if (bump && bump !== recommendedBump) { - console.log(`WARNING: Recommended bump is ${recommendedBump}, but you specified ${bump}`) + console.log(`WARNING: Recommended bump is '${recommendedBump}', but you specified '${bump}'`) } const calculatedBump = bump || recommendedBump @@ -70,38 +85,40 @@ export const updateChangelog = async (args: Args = {}) => { const yyyyMMdd = new Date().toISOString().split('T')[0] // Might need to swap out HEAD for the new proposed version - let output = `## [${proposedReleaseVersion}](https://github.com/payloadcms/payload/compare/${fromVersion}...${proposedReleaseVersion}) (${yyyyMMdd})\n\n\n` + let changelog = `## [${proposedReleaseVersion}](https://github.com/payloadcms/payload/compare/${fromVersion}...${proposedReleaseVersion}) (${yyyyMMdd})\n\n\n` if (sections.feat.length) { - output += `### Features\n\n${sections.feat.join('\n')}\n\n` + changelog += `### Features\n\n${sections.feat.join('\n')}\n\n` } if (sections.fix.length) { - output += `### Bug Fixes\n\n${sections.fix.join('\n')}\n\n` + changelog += `### Bug Fixes\n\n${sections.fix.join('\n')}\n\n` } if (sections.breaking.length) { - output += `### BREAKING CHANGES\n\n${sections.breaking.join('\n')}\n\n` + changelog += `### BREAKING CHANGES\n\n${sections.breaking.join('\n')}\n\n` } - console.log(chalk.green('\nChangelog Preview:\n')) - console.log(chalk.gray(output)) - if (writeChangelog) { const changelogPath = 'CHANGELOG.md' const changelog = await fse.readFile(changelogPath, 'utf8') - const newChangelog = output + '\n\n' + changelog + const newChangelog = changelog + '\n\n' + changelog await fse.writeFile(changelogPath, newChangelog) console.log(`Changelog updated at ${changelogPath}`) } // Add contributors after writing to file - output += contributors + const releaseNotes = changelog + contributors - let url = `https://github.com/payloadcms/payload/releases/new?tag=${proposedReleaseVersion}&title=${proposedReleaseVersion}&body=${encodeURIComponent(output)}` + let releaseUrl = `https://github.com/payloadcms/payload/releases/new?tag=${proposedReleaseVersion}&title=${proposedReleaseVersion}&body=${encodeURIComponent(releaseNotes)}` if (tag !== 'latest') { - url += `&prerelease=1` + releaseUrl += `&prerelease=1` } - console.log(`Release URL: ${chalk.dim(url)}`) if (!openReleaseUrl) { - await open(url) + await open(releaseUrl) + } + + return { + releaseUrl, + changelog, + releaseNotes, } } @@ -180,11 +197,10 @@ function formatCommitForChangelog(commit: GitCommit, includeBreakingNotes = fals const [rawNotes, _] = commit.body.split('\n\n') const notes = rawNotes .split('\n') - .filter((l) => !l.toUpperCase().startsWith('BREAKING')) .map((l) => `> ${l}`) .join('\n') .trim() - formatted += `\n\n${notes}` + formatted += `\n\n${notes}\n\n` } return formatted