-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add branch strategy tooling (#6857)
- Loading branch information
Showing
9 changed files
with
859 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#!/usr/bin/env node | ||
/* eslint-env node, es2022 */ | ||
|
||
import yargs from 'yargs' | ||
import { hideBin } from 'yargs/helpers' | ||
|
||
import * as findPRCommand from './findPRCommand.mjs' | ||
import * as triageMainCommand from './triageMainCommand.mjs' | ||
import * as triageNextCommand from './triageNextCommand.mjs' | ||
import * as validateMilestonesCommand from './validateMilestonesCommand.mjs' | ||
|
||
yargs(hideBin(process.argv)) | ||
// Config | ||
.scriptName('branch-strategy') | ||
.demandCommand() | ||
.strict() | ||
// Commands | ||
.command(triageMainCommand) | ||
.command(triageNextCommand) | ||
.command(findPRCommand) | ||
.command(validateMilestonesCommand) | ||
// Run | ||
.parse() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* eslint-env node, es2022 */ | ||
|
||
import boxen from 'boxen' | ||
import { $, fs, question, chalk } from 'zx' | ||
|
||
export function setupCache(file) { | ||
let cache | ||
|
||
try { | ||
cache = JSON.parse(fs.readFileSync(file, 'utf-8')) | ||
cache = new Map(Object.entries(cache)) | ||
} catch { | ||
cache = new Map() | ||
} | ||
|
||
process.on('exit', () => { | ||
fs.writeFileSync(file, JSON.stringify(Object.fromEntries(cache), null, 2)) | ||
}) | ||
|
||
return cache | ||
} | ||
|
||
export const GIT_LOG_OPTIONS = [ | ||
'--graph', | ||
'--oneline', | ||
'--boundary', | ||
'--cherry-pick', | ||
'--left-only', | ||
] | ||
|
||
export const HASH = /\w{9}/ | ||
export const PR = /#(?<pr>\d*)/ | ||
|
||
export function parseCommit(commit) { | ||
const match = commit.match(HASH) | ||
const [hash] = match | ||
|
||
const message = commit.slice(match.index + 10) | ||
|
||
const prMatch = message.match(PR) | ||
const pr = prMatch?.groups.pr | ||
|
||
return { | ||
hash, | ||
message, | ||
pr, | ||
} | ||
} | ||
|
||
export async function isCommitInBranch(branch, message) { | ||
const { stdout } = await $`git log ${branch} --oneline --grep ${message}` | ||
return Boolean(stdout) | ||
} | ||
|
||
export function reportNewCommits(commits) { | ||
console.log( | ||
[ | ||
`There's ${commits.length} commits in the main branch that aren't in the next branch:`, | ||
'', | ||
commits | ||
.map((commit) => { | ||
const { hash, message } = parseCommit(commit) | ||
return `${chalk.bold(chalk.yellow(hash))} ${message}` | ||
}) | ||
.join('\n'), | ||
'', | ||
].join('\n') | ||
) | ||
} | ||
|
||
export async function triageCommits(commits) { | ||
for (let commit of commits) { | ||
const { hash, message, pr } = parseCommit(commit) | ||
|
||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
const answer = await question( | ||
`Does ${chalk.bold(chalk.yellow(hash))} ${chalk.cyan( | ||
message | ||
)} need to be cherry picked into ${this.branch}? (Y/n/o(pen)) > ` | ||
) | ||
|
||
commit = this.cache.get(hash) | ||
|
||
if (answer === 'o' || answer === 'open') { | ||
await $`open https://github.com/redwoodjs/redwood/pull/${pr}` | ||
continue | ||
} | ||
|
||
this.cache.set(hash, { | ||
message: message, | ||
needsCherryPick: answer === '' || answer === 'y' || answer === 'Y', | ||
}) | ||
|
||
break | ||
} | ||
} | ||
} | ||
|
||
export const GIT_LOG_UI = ['o', ' /', '|\\', '| o'] | ||
|
||
export async function getReleaseBranch() { | ||
const { stdout: gitBranchStdout } = await $`git branch --list release/*` | ||
|
||
if (gitBranchStdout.trim().split('\n').length > 1) { | ||
console.log() | ||
console.log("There's more than one release branch") | ||
process.exit(1) | ||
} | ||
|
||
return gitBranchStdout.trim() | ||
} | ||
|
||
export async function purgeCache(cache, commits, branch) { | ||
const commitHashes = commits.map((commit) => parseCommit(commit).hash) | ||
|
||
for (const cachedHash of cache.keys()) { | ||
if (!commitHashes.includes(cachedHash)) { | ||
cache.delete(cachedHash) | ||
} | ||
} | ||
|
||
const needsCherryPick = [...cache.entries()].filter( | ||
([, { needsCherryPick }]) => needsCherryPick | ||
) | ||
|
||
for (const [hash, { message }] of needsCherryPick) { | ||
if (await isCommitInBranch(branch, message)) { | ||
cache.delete(hash) | ||
} | ||
} | ||
} | ||
|
||
export async function updateRemotes() { | ||
await $`git remote update` | ||
console.log() | ||
|
||
const { stdout: main } = await $`git rev-list main...origin/main --count` | ||
console.log() | ||
|
||
if (parseInt(main.trim())) { | ||
await $`git fetch origin main:main` | ||
console.log() | ||
} | ||
|
||
const { stdout: next } = await $`git rev-list next...origin/next --count` | ||
console.log() | ||
|
||
if (parseInt(next.trim())) { | ||
await $`git fetch origin next:next` | ||
console.log() | ||
} | ||
} | ||
|
||
export function colorKeyBox(colorKey) { | ||
return boxen(colorKey, { | ||
title: 'Key', | ||
padding: 1, | ||
margin: 1, | ||
borderStyle: 'round', | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* eslint-env node, es2022 */ | ||
|
||
import { Octokit } from 'octokit' | ||
|
||
import { | ||
updateRemotes, | ||
isCommitInBranch, | ||
getReleaseBranch, | ||
} from './branchStrategyLib.mjs' | ||
|
||
export const command = 'find-pr <uri>' | ||
export const description = 'Find which branches a PR is in' | ||
|
||
export function builder(yargs) { | ||
yargs.positional('pr', { | ||
description: 'The PR URL', | ||
type: 'string', | ||
}) | ||
} | ||
|
||
export async function handler({ uri }) { | ||
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }) | ||
|
||
await updateRemotes() | ||
|
||
const { | ||
resource: { | ||
mergeCommit: { messageHeadline }, | ||
}, | ||
} = await octokit.graphql( | ||
` | ||
query GetPR($uri: URI!) { | ||
resource(url: $uri) { | ||
... on PullRequest { | ||
mergeCommit { | ||
messageHeadline | ||
} | ||
} | ||
} | ||
} | ||
`, | ||
{ uri } | ||
) | ||
|
||
const isInNext = await isCommitInBranch('next', messageHeadline) | ||
console.log() | ||
const releaseBranch = await getReleaseBranch() | ||
console.log() | ||
const isInRelease = await isCommitInBranch(releaseBranch, messageHeadline) | ||
console.log() | ||
|
||
console.log( | ||
[ | ||
isInNext | ||
? '✅ This PR is in the next branch' | ||
: `❌ This PR isn't the next branch`, | ||
isInRelease | ||
? `✅ This PR is in the ${releaseBranch} branch` | ||
: `❌ This PR isn't the ${releaseBranch} branch`, | ||
].join('\n') | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
{ | ||
"544374612": { | ||
"message": "Fix dbauth webauthn template (redundant type import) (#6769)", | ||
"needsCherryPick": false | ||
}, | ||
"97f6f622e": { | ||
"message": "Fix auth0 decoder import (#6764)", | ||
"needsCherryPick": false | ||
}, | ||
"e2ec41f31": { | ||
"message": "Update netlify auth docs (#6748)", | ||
"needsCherryPick": false | ||
}, | ||
"ce3426b38": { | ||
"message": "Update auth setup warning message (#6746)", | ||
"needsCherryPick": false | ||
}, | ||
"be4c01c1a": { | ||
"message": "Okta: Add packages to setup script (#6732)", | ||
"needsCherryPick": false | ||
}, | ||
"810f1fecf": { | ||
"message": "Azure setup auth: Install and import all needed packages (#6736)", | ||
"needsCherryPick": false | ||
}, | ||
"c7cb9d975": { | ||
"message": "Setup auth: Update goTrue (#6733)", | ||
"needsCherryPick": false | ||
}, | ||
"e05e08071": { | ||
"message": "Auth0 setup: Install correct packages (#6734)", | ||
"needsCherryPick": false | ||
}, | ||
"d0be5e823": { | ||
"message": "nhost auth: Add missing packages (#6742)", | ||
"needsCherryPick": false | ||
}, | ||
"50586ea0b": { | ||
"message": "Add missing packages to magicLink setup (#6741)", | ||
"needsCherryPick": false | ||
}, | ||
"9a2609355": { | ||
"message": "supertokens setup auth: Add missing RW packages (#6744)", | ||
"needsCherryPick": false | ||
}, | ||
"af8970fd5": { | ||
"message": "Missing packages for Ethereum auth setup (#6740)", | ||
"needsCherryPick": false | ||
}, | ||
"570d7b49d": { | ||
"message": "supabase auth setup: Add missing rw packages (#6743)", | ||
"needsCherryPick": false | ||
}, | ||
"968ad3a3c": { | ||
"message": "Update Clerk docs (#6712)", | ||
"needsCherryPick": false | ||
}, | ||
"801894efc": { | ||
"message": "Update firebase auth docs (#6717)", | ||
"needsCherryPick": false | ||
}, | ||
"443506daf": { | ||
"message": "Clerk: Simplify web implementation (#6713)", | ||
"needsCherryPick": false | ||
}, | ||
"60e075f4d": { | ||
"message": "Add auth decoder to clerk auth setup (#6718)", | ||
"needsCherryPick": false | ||
}, | ||
"7fbd6ba32": { | ||
"message": "Update the Clerk setup script and templates (#6710)", | ||
"needsCherryPick": false | ||
}, | ||
"e01750d96": { | ||
"message": "Fix decouple auth related type errors (#6709)", | ||
"needsCherryPick": false | ||
}, | ||
"fa6546440": { | ||
"message": "fix(dbAuth): add required packages to setup command (#6698)", | ||
"needsCherryPick": false | ||
}, | ||
"79adb685e": { | ||
"message": "chore: make misc change to trigger canary publishing (#6695)", | ||
"needsCherryPick": false | ||
}, | ||
"18eaf3007": { | ||
"message": "chore: run lint fix (#6691)", | ||
"needsCherryPick": false | ||
}, | ||
"0942fba9f": { | ||
"message": "Decouple auth (#5985)", | ||
"needsCherryPick": false | ||
}, | ||
"c7ce6d6ac": { | ||
"message": "Custom auth: Fix comment in template (#6804)", | ||
"needsCherryPick": false | ||
}, | ||
"ca4f2bdb5": { | ||
"message": "fix(deps): update jest monorepo (#6818)", | ||
"needsCherryPick": true | ||
}, | ||
"a0b262d0b": { | ||
"message": "fix(deps): update storybook monorepo to v6.5.13 (#6819)", | ||
"needsCherryPick": true | ||
}, | ||
"49d829fb5": { | ||
"message": "Handle multiple set-cookie headers (#6812)", | ||
"needsCherryPick": true | ||
}, | ||
"64a6dce21": { | ||
"message": "fix(deps): update dependency core-js to v3.26.0 (#6822)", | ||
"needsCherryPick": true | ||
}, | ||
"cadb28725": { | ||
"message": "chore(deps): update dependency cypress to v10.11.0 (#6820)", | ||
"needsCherryPick": true | ||
}, | ||
"af3716763": { | ||
"message": "fix(deps): update jest monorepo to v29.3.1 (#6848)", | ||
"needsCherryPick": true | ||
}, | ||
"7cd1204a5": { | ||
"message": "fix(deps): update prisma monorepo to v4.6.0 (#6851)", | ||
"needsCherryPick": true | ||
}, | ||
"1d4b2c4a0": { | ||
"message": "Change to use @iarna/toml instead of toml (#6839)", | ||
"needsCherryPick": true | ||
} | ||
} |
Oops, something went wrong.