Skip to content

Commit

Permalink
ci(scripts): create draft release with release script, cleanup [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
denolfe committed Jan 3, 2025
1 parent 766b67f commit 6dcf817
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 22 deletions.
61 changes: 40 additions & 21 deletions scripts/release.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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'

Expand All @@ -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

Expand Down Expand Up @@ -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}`)
Expand All @@ -126,7 +125,7 @@ async function main() {
header(`${logPrefix}📝 Updating changelog...`)
const {
changelog: changelogContent,
releaseUrl,
releaseUrl: prefilledReleaseUrl,
releaseNotes,
} = await generateReleaseNotes({
bump,
Expand All @@ -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)

Expand Down Expand Up @@ -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!`)

header('🎉 Done!')
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'))
}

console.log(chalk.bold.green(`\n\nRelease URL: ${releaseUrl}`))
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!')
}

main().catch((error) => {
Expand Down Expand Up @@ -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)}`,
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions scripts/utils/createDraftGitHubRelease.ts
Original file line number Diff line number Diff line change
@@ -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 }
}
11 changes: 10 additions & 1 deletion scripts/utils/generateReleaseNotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> => {
Expand All @@ -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}...`)
Expand Down Expand Up @@ -155,6 +163,7 @@ export const generateReleaseNotes = async (args: Args = {}): Promise<ChangelogRe
releaseUrl,
changelog,
releaseNotes,
releaseTag: proposedReleaseVersion,
}
}

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 6dcf817

Please sign in to comment.