ci(scripts): create draft release with release script, cleanup [skip ci]
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
/**
|
||||
* Usage: GITHUB_TOKEN=$GITHUB_TOKEN pnpm release --bump <minor|patch>
|
||||
*
|
||||
* Ensure your GITHUB_TOKEN is set in your environment variables
|
||||
* and also has the ability to create releases in the repository.
|
||||
*/
|
||||
|
||||
import type { ExecSyncOptions } from 'child_process'
|
||||
|
||||
import chalk from 'chalk'
|
||||
@@ -14,8 +21,8 @@ import semver from 'semver'
|
||||
import type { PackageDetails } from './lib/getPackageDetails.js'
|
||||
|
||||
import { getPackageDetails } from './lib/getPackageDetails.js'
|
||||
import { getPackageRegistryVersions } from './lib/getPackageRegistryVersions.js'
|
||||
import { packagePublishList } from './lib/publishList.js'
|
||||
import { createDraftGitHubRelease } from './utils/createDraftGitHubRelease.js'
|
||||
import { generateReleaseNotes } from './utils/generateReleaseNotes.js'
|
||||
import { getRecommendedBump } from './utils/getRecommendedBump.js'
|
||||
|
||||
@@ -34,7 +41,6 @@ const {
|
||||
'dry-run': dryRun,
|
||||
'git-tag': gitTag = true, // Whether to run git tag and commit operations
|
||||
'git-commit': gitCommit = true, // Whether to run git commit operations
|
||||
versionOverride = undefined,
|
||||
tag, // Tag to publish to: latest, beta, canary
|
||||
} = args
|
||||
|
||||
@@ -108,14 +114,7 @@ async function main() {
|
||||
throw new Error('Could not find version in package.json')
|
||||
}
|
||||
|
||||
// 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 = versionOverride || semver.inc(monorepoVersion, bump, undefined, tag)
|
||||
const nextReleaseVersion = semver.inc(monorepoVersion, bump, undefined, tag)
|
||||
|
||||
if (!nextReleaseVersion) {
|
||||
abort(`Invalid nextReleaseVersion: ${nextReleaseVersion}`)
|
||||
@@ -126,7 +125,7 @@ async function main() {
|
||||
header(`${logPrefix}📝 Updating changelog...`)
|
||||
const {
|
||||
changelog: changelogContent,
|
||||
releaseUrl,
|
||||
releaseUrl: prefilledReleaseUrl,
|
||||
releaseNotes,
|
||||
} = await generateReleaseNotes({
|
||||
bump,
|
||||
@@ -138,7 +137,7 @@ async function main() {
|
||||
|
||||
console.log(chalk.green('\nFull Release Notes:\n\n'))
|
||||
console.log(chalk.gray(releaseNotes) + '\n\n')
|
||||
console.log(`\n\nRelease URL: ${chalk.dim(releaseUrl)}`)
|
||||
console.log(`\n\nRelease URL: ${chalk.dim(prefilledReleaseUrl)}`)
|
||||
|
||||
let packageDetails = await getPackageDetails(packagePublishList)
|
||||
|
||||
@@ -229,16 +228,36 @@ async function main() {
|
||||
.join('\n') + '\n',
|
||||
)
|
||||
|
||||
// TODO: Push commit and tag
|
||||
// const push = await confirm(`Push commits and tags?`)
|
||||
// if (push) {
|
||||
// header(`Pushing commits and tags...`)
|
||||
// execSync(`git push --follow-tags`, execOpts)
|
||||
// }
|
||||
header(`🚀 Publishing complete!`)
|
||||
|
||||
const pushTags = await confirm('Push commit and tags to remote?')
|
||||
if (pushTags) {
|
||||
runCmd(`git push --follow-tags`, execOpts)
|
||||
console.log(chalk.bold.green('Commit and tags pushed to remote'))
|
||||
}
|
||||
|
||||
const createDraftRelease = await confirm('Create draft release on GitHub?')
|
||||
if (createDraftRelease) {
|
||||
try {
|
||||
const { releaseUrl: draftReleaseUrl } = await createDraftGitHubRelease({
|
||||
branch: 'main',
|
||||
tag: `v${nextReleaseVersion}`,
|
||||
releaseNotes,
|
||||
})
|
||||
console.log(chalk.bold.green(`Draft release created on GitHub: ${draftReleaseUrl}`))
|
||||
} catch (error) {
|
||||
console.log(chalk.bold.red('\nFull Release Notes:\n\n'))
|
||||
console.log(chalk.gray(releaseNotes) + '\n\n')
|
||||
console.log(`\n\nRelease URL: ${chalk.dim(prefilledReleaseUrl)}`)
|
||||
console.log(chalk.bold.red(`Error creating draft release on GitHub: ${error.message}`))
|
||||
console.log(
|
||||
chalk.bold.red(
|
||||
`Use the above link to create the release manually and optionally add the release notes.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
header('🎉 Done!')
|
||||
|
||||
console.log(chalk.bold.green(`\n\nRelease URL: ${releaseUrl}`))
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
@@ -296,7 +315,7 @@ async function publishSinglePackage(pkg: PackageDetails, opts?: { dryRun?: boole
|
||||
details:
|
||||
err instanceof Error
|
||||
? `Error publishing ${pkg.name}: ${err.message}`
|
||||
: `Unexpected error publishing ${pkg.name}: ${String(err)}`,
|
||||
: `Unexpected error publishing ${pkg.name}: ${JSON.stringify(err)}`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
37
scripts/utils/createDraftGitHubRelease.ts
Normal file
37
scripts/utils/createDraftGitHubRelease.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
type Args = {
|
||||
branch: string
|
||||
tag: string
|
||||
releaseNotes: string
|
||||
}
|
||||
|
||||
export const createDraftGitHubRelease = async ({
|
||||
branch,
|
||||
tag,
|
||||
releaseNotes,
|
||||
}: Args): Promise<{ releaseUrl: string }> => {
|
||||
// https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release
|
||||
const res = await fetch(`https://api.github.com/repos/payloadcms/payload/releases`, {
|
||||
headers: {
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
Authorization: `token ${process.env.GITHUB_TOKEN}`,
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
tag_name: tag,
|
||||
target_commitish: branch,
|
||||
name: tag,
|
||||
body: releaseNotes,
|
||||
draft: true,
|
||||
prerelease: false,
|
||||
generate_release_notes: false,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to create release: ${await res.text()}`)
|
||||
}
|
||||
|
||||
const resBody = await res.json()
|
||||
|
||||
return { releaseUrl: resBody.html_url }
|
||||
}
|
||||
@@ -30,6 +30,10 @@ type ChangelogResult = {
|
||||
* The release notes, includes contributors. This is the content used for the releaseUrl
|
||||
*/
|
||||
releaseNotes: string
|
||||
/**
|
||||
* The release tag, includes prefix 'v'
|
||||
*/
|
||||
releaseTag: string
|
||||
}
|
||||
|
||||
export const generateReleaseNotes = async (args: Args = {}): Promise<ChangelogResult> => {
|
||||
@@ -49,6 +53,10 @@ export const generateReleaseNotes = async (args: Args = {}): Promise<ChangelogRe
|
||||
|
||||
const calculatedBump = bump || recommendedBump
|
||||
|
||||
if (!calculatedBump) {
|
||||
throw new Error('Could not determine bump type')
|
||||
}
|
||||
|
||||
const proposedReleaseVersion = 'v' + semver.inc(fromVersion, calculatedBump, undefined, tag)
|
||||
|
||||
console.log(`Generating release notes for ${fromVersion} to ${toVersion}...`)
|
||||
@@ -155,6 +163,7 @@ export const generateReleaseNotes = async (args: Args = {}): Promise<ChangelogRe
|
||||
releaseUrl,
|
||||
changelog,
|
||||
releaseNotes,
|
||||
releaseTag: proposedReleaseVersion,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +225,7 @@ async function getContributors(commits: GitCommit[]): Promise<Contributor[]> {
|
||||
const coAuthors = Array.from(
|
||||
commit.body.matchAll(coAuthorPattern),
|
||||
(match) => match.groups,
|
||||
).filter((e) => !e.email.includes('[bot]'))
|
||||
).filter((e) => !e?.email.includes('[bot]')) as { name: string; email: string }[]
|
||||
|
||||
if (!coAuthors.length) {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user