Selaa lähdekoodia

meta: improve release process for beta branch (#3809)

Antoine du Hamel 2 vuotta sitten
vanhempi
commit
2be8cec2af

+ 2 - 2
.github/workflows/release.yml

@@ -84,8 +84,8 @@ jobs:
         run: gh api -X DELETE repos/${{ github.repository }}/git/refs/heads/release-candidate || echo "Already deleted"
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      - name: Remove release branch
-        run: gh api -X DELETE repos/${{ github.repository }}/git/refs/heads/release
+      - name: Remove release-beta branch
+        run: gh api -X DELETE repos/${{ github.repository }}/git/refs/heads/release-beta
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
       - name: Disable Release workflow

+ 56 - 0
private/release/autoFixConflicts.js

@@ -0,0 +1,56 @@
+#!/usr/bin/env node
+
+// Usage: autoFixConflicts.js | sh
+
+import { createInterface as readLines } from 'node:readline'
+import { spawn } from 'node:child_process'
+
+const VERSION_URL = /(?<=https:\/\/\S+\/v)\d+\.\d+\.\d+(?:-(?:alpha|beta)(?:[.-]\d+)?)?(?=\/)/
+
+const gitStatus = spawn('git', ['status', '--porcelain'])
+
+for await (const line of readLines(gitStatus.stdout)) {
+  // eslint-disable-next-line no-continue
+  if (!line.startsWith('UU ')) continue
+
+  const file = line.slice(3)
+  if (file === 'yarn.lock') {
+    console.log('corepack yarn install')
+    console.log('git add yarn.lock')
+    // eslint-disable-next-line no-continue
+    continue
+  }
+
+  if (file.endsWith('/package.json')) {
+    console.log(`git checkout --ours ${file}`)
+    console.log(`git add ${file}`)
+    // eslint-disable-next-line no-continue
+    continue
+  }
+
+  const gitDiff = spawn('git', ['--no-pager', 'diff', '--', file])
+  let conflictHasStarted = false
+  let containsCDNChanges = true
+  let currentConflictContainsCDNChanges = false
+
+  // eslint-disable-next-line no-shadow
+  for await (const line of readLines(gitDiff.stdout)) {
+    if (conflictHasStarted) {
+      if (line.startsWith('++>>>>>>>')) {
+        conflictHasStarted = false
+        containsCDNChanges &&= currentConflictContainsCDNChanges
+        currentConflictContainsCDNChanges = false
+      } else {
+        currentConflictContainsCDNChanges ||= VERSION_URL.test(line)
+      }
+    } else if (line === '++<<<<<<< HEAD') {
+      conflictHasStarted = true
+    }
+  }
+  if (containsCDNChanges) {
+    console.log(`git checkout --ours ${file}`)
+    console.log(`git add ${file}`)
+    // eslint-disable-next-line no-continue
+    continue
+  }
+}

+ 23 - 1
private/release/choose-semverness.js

@@ -26,6 +26,7 @@ function maxSemverness (a, b) {
 export default async function pickSemverness (
   spawnOptions,
   LAST_RELEASE_COMMIT,
+  STABLE_BRANCH_MERGE_BASE_RANGE,
   releaseFileUrl,
   packagesList,
 ) {
@@ -55,7 +56,28 @@ export default async function pickSemverness (
       spawnOptions,
     )
     if (stdout.length === 0) {
-      console.log(`No commits since last release for ${name}, skipping.`)
+      const { stdout } = spawnSync(
+        'git',
+        [
+          '--no-pager',
+          'log',
+          '--format=- %s',
+          STABLE_BRANCH_MERGE_BASE_RANGE,
+          '--',
+          location,
+        ],
+        spawnOptions,
+      )
+      if (stdout.length === 0) {
+        console.log(`No commits since last release for ${name}, skipping.`)
+      } else {
+        console.log(`Some commits have landed on the stable branch since last release for ${name}.`)
+        releaseFile.write(`  ${JSON.stringify(name)}: prerelease\n`)
+        uppySemverness = maxSemverness(uppySemverness, 'prerelease')
+        if (robodogDeps.includes(name)) {
+          robodogSemverness = maxSemverness(robodogSemverness, 'prerelease')
+        }
+      }
       continue
     }
     console.log('\n')

+ 53 - 2
private/release/commit-and-open-pr.js

@@ -1,9 +1,9 @@
 import { spawnSync } from 'node:child_process'
 import { fileURLToPath } from 'node:url'
 import prompts from 'prompts'
-import { REPO_OWNER, REPO_NAME } from './config.js'
+import { REPO_NAME, REPO_OWNER } from './config.js'
 
-export default async function commit (spawnOptions, ...files) {
+export default async function commit (spawnOptions, STABLE_HEAD, ...files) {
   console.log(`Now is the time to do manual edits to ${files.join(',')}.`)
   await prompts({
     type: 'toggle',
@@ -15,8 +15,59 @@ export default async function commit (spawnOptions, ...files) {
   })
 
   spawnSync('git', ['add', ...files.map(url => fileURLToPath(url))], spawnOptions)
+  const remoteHeadSha = spawnSync('git', ['rev-parse', 'HEAD'], spawnOptions).stdout.toString().trim()
   spawnSync('git', ['commit', '-n', '-m', 'Prepare next release'], { ...spawnOptions, stdio: 'inherit' })
+  const releaseSha = spawnSync('git', ['rev-parse', 'HEAD'], spawnOptions).stdout.toString().trim()
+
+  console.log('Attempting to merge changes from stable branch...')
+  {
+    // eslint-disable-next-line no-shadow
+    const { status, stdout, stderr } = spawnSync(
+      'git',
+      [
+        'merge',
+        '--no-edit',
+        '-m',
+        'Merge stable branch',
+        STABLE_HEAD,
+      ],
+      spawnOptions,
+    )
+    if (status) {
+      console.log(stdout.toString())
+      console.error(stderr.toString())
+
+      await prompts({
+        type: 'toggle',
+        name: 'value',
+        message: 'Fix the conflict before continuing. Ready?',
+        initial: true,
+        active: 'yes',
+        inactive: 'yes',
+      })
+
+      // eslint-disable-next-line no-shadow
+      const { status } = spawnSync(
+        'git',
+        [
+          'merge',
+          '--continue',
+        ],
+        { ...spawnOptions, stdio: 'inherit' },
+      )
+
+      if (status) {
+        throw new Error('Merge has failed')
+      }
+    }
+  }
+  const mergeSha = spawnSync('git', ['rev-parse', 'HEAD'], spawnOptions).stdout.toString().trim()
+
+  spawnSync('git', ['reset', remoteHeadSha, '--hard'], spawnOptions)
+  spawnSync('git', ['cherry-pick', mergeSha, '--hard'], spawnOptions)
+  spawnSync('git', ['cherry-pick', releaseSha, '--hard'], spawnOptions)
   const sha = spawnSync('git', ['rev-parse', 'HEAD'], spawnOptions).stdout.toString().trim()
+
   const getRemoteCommamnd = `git remote -v | grep '${REPO_OWNER}/${REPO_NAME}' | awk '($3 == "(push)") { print $1; exit }'`
   const remote = spawnSync('/bin/sh', ['-c', getRemoteCommamnd]).stdout.toString().trim()
                  || `git@github.com:${REPO_OWNER}/${REPO_NAME}.git`

+ 1 - 0
private/release/config.js

@@ -1,3 +1,4 @@
 export const REPO_OWNER = 'transloadit'
 export const REPO_NAME = 'uppy'
 export const TARGET_BRANCH = '3.x'
+export const STABLE_BRANCH = 'main'

+ 15 - 6
private/release/getUpToDateRefsFromGitHub.js

@@ -2,7 +2,7 @@ import fetch from 'node-fetch'
 
 import { spawnSync } from 'node:child_process'
 import prompts from 'prompts'
-import { TARGET_BRANCH, REPO_NAME, REPO_OWNER } from './config.js'
+import { TARGET_BRANCH, REPO_NAME, REPO_OWNER, STABLE_BRANCH } from './config.js'
 
 async function apiCall (endpoint, errorMessage) {
   const response = await fetch(
@@ -25,10 +25,10 @@ export async function getRemoteHEAD () {
 }
 
 async function getLatestReleaseSHA () {
-  const { tag_name } = await apiCall(
-    `/releases/latest`,
-    'Cannot get latest release from GitHub, check your internet connection.',
-  )
+  const response = await fetch(`https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/${TARGET_BRANCH}/packages/uppy/package.json`)
+  if (!response.ok) throw new Error(`Network call failed: ${response.status} ${response.statusText}`)
+  const { version } = await response.json()
+  const tag_name = `uppy@${version}`
   console.log(`Last release was ${tag_name}.`)
   return (
     await apiCall(
@@ -38,6 +38,15 @@ async function getLatestReleaseSHA () {
   ).object.sha
 }
 
+function getStableBranchMergeBase (REMOTE_HEAD) {
+  spawnSync('git', ['fetch', `https://github.com/${REPO_OWNER}/${REPO_NAME}.git`, STABLE_BRANCH])
+  const STABLE_HEAD = spawnSync('git', ['rev-parse', 'FETCH_HEAD']).stdout.toString().trim()
+  return [[
+    spawnSync('git', ['merge-base', REMOTE_HEAD, 'FETCH_HEAD']).stdout.toString().trim(),
+    STABLE_HEAD,
+  ].join('..'), STABLE_HEAD]
+}
+
 async function getLocalHEAD () {
   return spawnSync('git', ['rev-parse', 'HEAD']).stdout.toString().trim()
 }
@@ -104,5 +113,5 @@ export async function validateGitStatus (spawnOptions) {
     }
   }
 
-  return [await latestRelease, LOCAL_HEAD]
+  return [await latestRelease, LOCAL_HEAD, ...getStableBranchMergeBase(REMOTE_HEAD)]
 }

+ 3 - 3
private/release/interactive.js

@@ -14,14 +14,14 @@ const deferredReleaseFile = new URL('./.yarn/versions/next.yml', ROOT)
 const temporaryChangeLog = new URL('./CHANGELOG.next.md', ROOT)
 
 console.log('Validating local repo status and get previous release info...')
-const [LAST_RELEASE_COMMIT, LOCAL_HEAD] = await validateGitStatus(spawnOptions)
+const [LAST_RELEASE_COMMIT, LOCAL_HEAD, MERGE_BASE, STABLE_HEAD] = await validateGitStatus(spawnOptions)
 try {
   console.log('Local git repository is ready, starting release process...')
-  await pickSemverness(spawnOptions, LAST_RELEASE_COMMIT, deferredReleaseFile, process.env.PACKAGES.split(' '))
+  await pickSemverness(spawnOptions, LAST_RELEASE_COMMIT, MERGE_BASE, deferredReleaseFile, process.env.PACKAGES.split(' '))
   console.log('Working on the changelog...')
   await formatChangeLog(spawnOptions, LAST_RELEASE_COMMIT, temporaryChangeLog)
   console.log('Final step...')
-  await commit(spawnOptions, deferredReleaseFile, temporaryChangeLog)
+  await commit(spawnOptions, STABLE_HEAD, deferredReleaseFile, temporaryChangeLog)
 } finally {
   console.log('Rewinding git history...')
   await rewindGitHistory(spawnOptions, LOCAL_HEAD)