Repository: vercel/next.js
Pull request: [release] use changesets/action for release-candidate

Best to be reviewed with "Hide whitespace".

This PR added release candidate handling and used a switch case to ensure the correct release type was given.

Changed files:
filename: .github/workflows/build_and_deploy.yml
patch: @@ -20,7 +20,7 @@ env:
   # See https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#os-version for more details
   MACOSX_DEPLOYMENT_TARGET: 11.0
   # This will become "true" if the latest commit is
-  # "Version Packages" or "Version Pacakges (canary)"
+  # "Version Packages" or "Version Pacakges \((canary|rc)\)"
   # set from scripts/check-is-release.js
   __NEW_RELEASE: 'false'
 
@@ -50,7 +50,7 @@ jobs:
         run: |
           # TODO: Remove the new release check once the new release workflow is fully replaced.
           RELEASE_CHECK=$(node ./scripts/check-is-release.js 2> /dev/null || :)
-          if [[ $RELEASE_CHECK =~ ^Version\ Packages(\ \(canary\))?$ ]];
+          if [[ $RELEASE_CHECK =~ ^Version\ Packages(\ \((canary|rc)\))?$ ]];
           then
             echo "__NEW_RELEASE=true" >> $GITHUB_ENV
           elif [[ $RELEASE_CHECK = v* ]];

filename: .github/workflows/trigger_release_new.yml
patch: @@ -18,8 +18,7 @@ on:
         options:
           - canary
           - stable
-          # TODO: Follow up enable the case.
-          # - release-candidate
+          - release-candidate
 
       force:
         description: Forced Release

filename: scripts/check-is-release.js
patch: @@ -13,7 +13,7 @@ const checkIsRelease = async () => {
 
   const versionString = commitMsg.split(' ').pop().trim()
   const publishMsgRegex = /^v\d{1,}\.\d{1,}\.\d{1,}(-\w{1,}\.\d{1,})?$/
-  const newPublishMsgRegex = /^Version Packages( \(canary\))?$/
+  const newPublishMsgRegex = /^Version Packages( \((canary|rc)\))?$/
 
   if (publishMsgRegex.test(versionString)) {
     console.log(versionString)

filename: scripts/release/version-packages.ts
patch: @@ -36,53 +36,73 @@ async function versionPackages() {
     }
   }
 
+  // For prereleases, we need to set the "mode" on `pre.json`, which
+  // can be done by running `changeset pre enter <mode>`.
   const releaseType = process.env.RELEASE_TYPE
+  switch (releaseType) {
+    case 'canary': {
+      // Enter pre mode as "canary" tag.
+      await execa('pnpm', ['changeset', 'pre', 'enter', 'canary'], {
+        stdio: 'inherit',
+      })
 
-  if (releaseType === 'canary') {
-    // Enter pre mode as "canary" tag.
-    await execa('pnpm', ['changeset', 'pre', 'enter', 'canary'], {
-      stdio: 'inherit',
-    })
+      console.log(
+        '▲   Preparing to bump the canary version, checking if there are any changesets.'
+      )
 
-    console.log(
-      '▲   Preparing to bump the canary version, checking if there are any changesets.'
-    )
+      // Create an empty changeset for `next` to bump the canary version
+      // even if there are no changesets for `next`.
+      await execa('pnpm', [
+        'changeset',
+        'status',
+        '--output',
+        './changeset-status.json',
+      ])
 
-    // Create an empty changeset for `next` to bump the canary version
-    // even if there are no changesets for `next`.
-    await execa('pnpm', [
-      'changeset',
-      'status',
-      '--output',
-      './changeset-status.json',
-    ])
+      let hasNextChangeset = false
+      if (existsSync('./changeset-status.json')) {
+        const changesetStatus: ChangesetStatusJson = JSON.parse(
+          await readFile('./changeset-status.json', 'utf8')
+        )
 
-    let hasNextChangeset = false
-    if (existsSync('./changeset-status.json')) {
-      const changesetStatus: ChangesetStatusJson = JSON.parse(
-        await readFile('./changeset-status.json', 'utf8')
-      )
+        console.log('▲   Changeset Status:')
+        console.log(changesetStatus)
 
-      console.log('▲   Changeset Status:')
-      console.log(changesetStatus)
+        hasNextChangeset =
+          changesetStatus.releases.find(
+            (release) => release.name === 'next'
+          ) !== undefined
 
-      hasNextChangeset =
-        changesetStatus.releases.find((release) => release.name === 'next') !==
-        undefined
+        await unlink('./changeset-status.json')
+      }
 
-      await unlink('./changeset-status.json')
+      if (!hasNextChangeset) {
+        console.log(
+          '▲   No changesets found for `next`, creating an empty changeset.'
+        )
+        // TODO: Since this is temporary until we hard-require a changeset, we will
+        // need to remove this in the future to prevent publishing empty releases.
+        await writeFile(
+          join(process.cwd(), '.changeset', `next-canary-${Date.now()}.md`),
+          `---\n'next': patch\n---`
+        )
+      }
+      break
     }
-
-    if (!hasNextChangeset) {
-      console.log(
-        '▲   No changesets found for `next`, creating an empty changeset.'
-      )
-      // TODO: Since this is temporary until we hard-require a changeset, we will
-      // need to remove this in the future to prevent publishing empty releases.
-      await writeFile(
-        join(process.cwd(), '.changeset', `next-canary-${Date.now()}.md`),
-        `---\n'next': patch\n---`
-      )
+    case 'release-candidate': {
+      // Enter pre mode as "rc" tag.
+      await execa('pnpm', ['changeset', 'pre', 'enter', 'rc'], {
+        stdio: 'inherit',
+      })
+      break
+    }
+    case 'stable': {
+      // No additional steps needed for 'stable' releases since we've already
+      // exited any pre-release mode. Only need to run `changeset version` after.
+      break
+    }
+    default: {
+      throw new Error(`Invalid release type: ${releaseType}`)
     }
   }