-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
134 changed files
with
3,376 additions
and
1,062 deletions.
There are no files selected for viewing
Binary file added
BIN
+11.8 KB
.yarn/cache/@aashutoshrathi-word-wrap-npm-1.2.6-5b1d95e487-53c2b231a6.zip
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+372 KB
.yarn/cache/@eslint-community-eslint-utils-npm-4.4.0-d1791bd5a3-7e559c4ce5.zip
Binary file not shown.
Binary file added
BIN
+423 KB
.yarn/cache/@eslint-community-regexpp-npm-4.10.0-6bfb984c81-c5f60ef1f1.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+55.4 KB
.yarn/cache/@humanwhocodes-config-array-npm-0.11.14-94a02fcc87-66f725b4ee.zip
Binary file not shown.
Binary file added
BIN
+23.4 KB
.yarn/cache/@humanwhocodes-module-importer-npm-1.0.1-9d07ed2e4a-909b69c3b8.zip
Binary file not shown.
Binary file added
BIN
+55.9 KB
.yarn/cache/@humanwhocodes-object-schema-npm-2.0.2-77b42018f9-6fd83dc320.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+2.6 MB
.yarn/cache/@typescript-eslint-eslint-plugin-npm-7.1.0-31bd50d8f4-e5644a9879.zip
Binary file not shown.
Binary file added
BIN
+19.9 KB
.yarn/cache/@typescript-eslint-parser-npm-7.1.0-4862dd3703-8fcbfc8c0c.zip
Binary file not shown.
Binary file added
BIN
+730 KB
.yarn/cache/@typescript-eslint-scope-manager-npm-7.1.0-4aebe598fa-2fd167730b.zip
Binary file not shown.
Binary file added
BIN
+123 KB
.yarn/cache/@typescript-eslint-type-utils-npm-7.1.0-9322895735-3e3eea6c03.zip
Binary file not shown.
Binary file added
BIN
+157 KB
.yarn/cache/@typescript-eslint-types-npm-7.1.0-9208d73d58-095cde3e77.zip
Binary file not shown.
Binary file added
BIN
+591 KB
.yarn/cache/@typescript-eslint-typescript-estree-npm-7.1.0-ebb2e5440a-063845dc85.zip
Binary file not shown.
Binary file added
BIN
+338 KB
.yarn/cache/@typescript-eslint-utils-npm-7.1.0-379a7d3140-3fefd51307.zip
Binary file not shown.
Binary file added
BIN
+22.4 KB
.yarn/cache/@typescript-eslint-visitor-keys-npm-7.1.0-402d386e44-9015a10e6e.zip
Binary file not shown.
Binary file added
BIN
+29.4 KB
.yarn/cache/@ungap-structured-clone-npm-1.2.0-648f0b82e0-8209c937cb.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+20.7 KB
.yarn/cache/fast-json-stable-stringify-npm-2.1.0-02e8905fda-7f081eb0b8.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+17.8 KB
.yarn/cache/json-stable-stringify-without-jsonify-npm-1.0.1-b65772b28b-cb168b61fd.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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,19 @@ | ||
{ | ||
"typescript": { | ||
}, | ||
"json": { | ||
}, | ||
"markdown": { | ||
}, | ||
"excludes": [ | ||
"**/node_modules", | ||
"**/*-lock.json", | ||
".yarn/*", | ||
".pnp.*" | ||
], | ||
"plugins": [ | ||
"https://plugins.dprint.dev/typescript-0.89.3.wasm", | ||
"https://plugins.dprint.dev/json-0.19.2.wasm", | ||
"https://plugins.dprint.dev/markdown-0.16.4.wasm" | ||
] | ||
} |
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,21 @@ | ||
// @ts-check | ||
|
||
import eslint from "@eslint/js"; | ||
import tseslint from "typescript-eslint"; | ||
|
||
export default tseslint.config( | ||
eslint.configs.recommended, | ||
...tseslint.configs.recommended, | ||
{ | ||
ignores: [ | ||
".yarn/*", | ||
".pnp.loader.mjs", | ||
], | ||
}, | ||
{ | ||
rules: { | ||
"no-constant-condition": "off", | ||
"@typescript-eslint/no-explicit-any": "off", | ||
}, | ||
}, | ||
); |
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 |
---|---|---|
@@ -1,18 +1,18 @@ | ||
import { describe, expect, it } from 'vitest' | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import { assertGitHubToken } from './assert_github_token.js' | ||
import { assertGitHubToken } from "./assert_github_token.js"; | ||
|
||
describe('assertGitHubToken', () => { | ||
it('works', () => { | ||
expect(process.env.REDWOOD_GITHUB_TOKEN).toBeDefined() | ||
expect(assertGitHubToken).not.toThrow() | ||
}) | ||
describe("assertGitHubToken", () => { | ||
it("works", () => { | ||
expect(process.env.REDWOOD_GITHUB_TOKEN).toBeDefined(); | ||
expect(assertGitHubToken).not.toThrow(); | ||
}); | ||
|
||
it("throws if `REDWOOD_GITHUB_TOKEN` isn't defined", () => { | ||
const originalToken = process.env.REDWOOD_GITHUB_TOKEN | ||
delete process.env.REDWOOD_GITHUB_TOKEN | ||
expect(process.env.REDWOOD_GITHUB_TOKEN).toBeUndefined() | ||
expect(assertGitHubToken).toThrow() | ||
process.env.REDWOOD_GITHUB_TOKEN = originalToken | ||
}) | ||
}) | ||
const originalToken = process.env.REDWOOD_GITHUB_TOKEN; | ||
delete process.env.REDWOOD_GITHUB_TOKEN; | ||
expect(process.env.REDWOOD_GITHUB_TOKEN).toBeUndefined(); | ||
expect(assertGitHubToken).toThrow(); | ||
process.env.REDWOOD_GITHUB_TOKEN = originalToken; | ||
}); | ||
}); |
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 |
---|---|---|
@@ -1,21 +1,23 @@ | ||
import { chalk } from 'zx' | ||
import { chalk } from "zx"; | ||
|
||
import { CustomError } from './custom_error.js' | ||
import { CustomError } from "./custom_error.js"; | ||
|
||
export function assertGitHubToken() { | ||
if (process.env.REDWOOD_GITHUB_TOKEN) { | ||
return | ||
return; | ||
} | ||
|
||
throw new CustomError([ | ||
`The ${chalk.magenta('REDWOOD_GITHUB_TOKEN')} environment variable isn't set. Set it to a GitHub personal access token:`, | ||
'', | ||
`The ${ | ||
chalk.magenta("REDWOOD_GITHUB_TOKEN") | ||
} environment variable isn't set. Set it to a GitHub personal access token:`, | ||
"", | ||
` ${chalk.green("export REDWOOD_GITHUB_TOKEN='...'")}`, | ||
'', | ||
`in one of your shell start-up files (e.g. ${chalk.magenta('~/.bashrc')} or ${chalk.magenta('~/.zshrc')})`, | ||
'or in a .env file in this directory that you create.', | ||
'', | ||
"", | ||
`in one of your shell start-up files (e.g. ${chalk.magenta("~/.bashrc")} or ${chalk.magenta("~/.zshrc")})`, | ||
"or in a .env file in this directory that you create.", | ||
"", | ||
`You can create a new personal access token at https://github.com/settings/tokens`, | ||
`All it needs is the ${chalk.magenta('public_repo')} scope` | ||
].join('\n')) | ||
`All it needs is the ${chalk.magenta("public_repo")} scope`, | ||
].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 |
---|---|---|
@@ -1,32 +1,34 @@ | ||
import { describe, expect, it } from 'vitest' | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import { branchExists, getReleaseBranchesFromStdout } from './branches.js' | ||
import { branchExists, getReleaseBranchesFromStdout } from "./branches.js"; | ||
|
||
describe('branchExists', () => { | ||
it('works', async () => { | ||
const exists = await branchExists('main') | ||
expect(exists).toBe(true) | ||
}) | ||
describe("branchExists", () => { | ||
it("works", async () => { | ||
const exists = await branchExists("main"); | ||
expect(exists).toBe(true); | ||
}); | ||
|
||
it("returns false if the branch doesn't exist", async () => { | ||
const exists = await branchExists('nonexistent-branch') | ||
expect(exists).toBe(false) | ||
}) | ||
}) | ||
const exists = await branchExists("nonexistent-branch"); | ||
expect(exists).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('getReleaseBranches', () => { | ||
it('works', () => { | ||
const releaseBranches = getReleaseBranchesFromStdout('release/patch/v7.0.4\nrelease/minor/v7.1.0\nrelease/patch/v7.0.5') | ||
expect(releaseBranches).toEqual(["release/minor/v7.1.0", "release/patch/v7.0.5", "release/patch/v7.0.4"]) | ||
}) | ||
describe("getReleaseBranches", () => { | ||
it("works", () => { | ||
const releaseBranches = getReleaseBranchesFromStdout( | ||
"release/patch/v7.0.4\nrelease/minor/v7.1.0\nrelease/patch/v7.0.5", | ||
); | ||
expect(releaseBranches).toEqual(["release/minor/v7.1.0", "release/patch/v7.0.5", "release/patch/v7.0.4"]); | ||
}); | ||
|
||
it("returns an empty array if there's no release branches", () => { | ||
const releaseBranches = getReleaseBranchesFromStdout('') | ||
expect(releaseBranches).toEqual([]) | ||
}) | ||
const releaseBranches = getReleaseBranchesFromStdout(""); | ||
expect(releaseBranches).toEqual([]); | ||
}); | ||
|
||
it('handles a release branch currently being checked out', () => { | ||
const releaseBranches = getReleaseBranchesFromStdout('release/patch/v7.0.4\n* release/patch/v7.0.5') | ||
expect(releaseBranches).toEqual(["release/patch/v7.0.5", "release/patch/v7.0.4"]) | ||
}) | ||
}) | ||
it("handles a release branch currently being checked out", () => { | ||
const releaseBranches = getReleaseBranchesFromStdout("release/patch/v7.0.4\n* release/patch/v7.0.5"); | ||
expect(releaseBranches).toEqual(["release/patch/v7.0.5", "release/patch/v7.0.4"]); | ||
}); | ||
}); |
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 |
---|---|---|
@@ -1,76 +1,76 @@ | ||
import semver from 'semver' | ||
import { chalk, $ } from 'zx' | ||
import semver from "semver"; | ||
import { $, chalk } from "zx"; | ||
|
||
import { CustomError } from './custom_error.js' | ||
import { unwrap } from './zx_helpers.js' | ||
import { CustomError } from "./custom_error.js"; | ||
import { unwrap } from "./zx_helpers.js"; | ||
|
||
export async function assertWorkTreeIsClean() { | ||
const workTreeIsClean = unwrap(await $`git status -s`) === '' | ||
const workTreeIsClean = unwrap(await $`git status -s`) === ""; | ||
if (!workTreeIsClean) { | ||
throw new CustomError( | ||
`The working tree at ${chalk.magenta(process.cwd())} isn't clean. Commit or stash your changes` | ||
`The working tree at ${chalk.magenta(process.cwd())} isn't clean. Commit or stash your changes`, | ||
); | ||
} | ||
console.log('✨ The working tree is clean') | ||
console.log("✨ The working tree is clean"); | ||
} | ||
|
||
export async function branchExists(branch: string) { | ||
return !!unwrap(await $`git branch --list ${branch}`) | ||
return !!unwrap(await $`git branch --list ${branch}`); | ||
} | ||
|
||
export async function assertBranchExistsAndTracksRemote(branch: string, remote: string) { | ||
if (!(await branchExists(branch))) { | ||
throw new CustomError([ | ||
`The ${chalk.magenta(branch)} branch doesn't exist locally. Check it out from the Redwood remote:`, | ||
'', | ||
"", | ||
chalk.green(` git checkout -b ${branch} ${remote}/${branch}`), | ||
].join('\n')) | ||
].join("\n")); | ||
} | ||
console.log(`🏠 The ${chalk.magenta(branch)} branch exists locally`) | ||
console.log(`🏠 The ${chalk.magenta(branch)} branch exists locally`); | ||
|
||
const trackingBranch = unwrap(await $`git rev-parse --abbrev-ref ${branch}@{upstream}`) | ||
const trackingBranch = unwrap(await $`git rev-parse --abbrev-ref ${branch}@{upstream}`); | ||
if (trackingBranch === `${remote}/${branch}`) { | ||
console.log(`🆗 The ${chalk.magenta(branch)} branch tracks ${chalk.magenta(`${remote}/${branch}`)}`) | ||
return | ||
console.log(`🆗 The ${chalk.magenta(branch)} branch tracks ${chalk.magenta(`${remote}/${branch}`)}`); | ||
return; | ||
} | ||
|
||
throw new CustomError([ | ||
`The ${chalk.magenta(branch)} branch doesn't track ${chalk.magenta(`${remote}/${branch}`)}`, | ||
`It's currently tracking ${chalk.magenta(trackingBranch)}`, | ||
'', | ||
"", | ||
`Make it track the remote with:`, | ||
'', | ||
"", | ||
chalk.green(` git branch -u ${remote}/${branch}`), | ||
].join('\n')) | ||
].join("\n")); | ||
} | ||
|
||
export async function pullBranch(branch: string, remote: string) { | ||
await $`git switch ${branch}` | ||
await $`git pull ${remote} ${branch}` | ||
await $`git switch ${branch}`; | ||
await $`git pull ${remote} ${branch}`; | ||
} | ||
|
||
export async function pushBranch(branch: string, remote: string) { | ||
await $`git push ${remote} ${branch}` | ||
await $`git push ${remote} ${branch}`; | ||
} | ||
|
||
export async function getReleaseBranches() { | ||
const stdout = unwrap(await $`git branch --list release/*`) | ||
return getReleaseBranchesFromStdout(stdout) | ||
const stdout = unwrap(await $`git branch --list release/*`); | ||
return getReleaseBranchesFromStdout(stdout); | ||
} | ||
|
||
export function getReleaseBranchesFromStdout(stdout: string) { | ||
if (stdout === '') { | ||
return [] | ||
if (stdout === "") { | ||
return []; | ||
} | ||
|
||
const releaseBranches = stdout | ||
.split('\n') | ||
.map((branch) => branch.trim().replace('* ', '')) | ||
.split("\n") | ||
.map((branch) => branch.trim().replace("* ", "")) | ||
.sort((releaseBranchA, releaseBranchB) => { | ||
const [, , versionA] = releaseBranchA.split('/') | ||
const [, , versionB] = releaseBranchB.split('/') | ||
return semver.compare(versionA, versionB) | ||
}) | ||
const [, , versionA] = releaseBranchA.split("/"); | ||
const [, , versionB] = releaseBranchB.split("/"); | ||
return semver.compare(versionA, versionB); | ||
}); | ||
|
||
return releaseBranches.reverse() | ||
return releaseBranches.reverse(); | ||
} |
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 |
---|---|---|
@@ -1,68 +1,68 @@ | ||
import { chalk, question, $ } from 'zx' | ||
import { $, chalk, question } from "zx"; | ||
|
||
import { consoleBoxen } from './console_helpers.js' | ||
import { getUserLogin } from './github.js' | ||
import { resolveRes } from './prompts.js' | ||
import type { Commit, Range } from './types.js' | ||
import { consoleBoxen } from "./console_helpers.js"; | ||
import { getUserLogin } from "./github.js"; | ||
import { resolveRes } from "./prompts.js"; | ||
import type { Commit, Range } from "./types.js"; | ||
|
||
export async function cherryPickCommits(commits: Commit[], { | ||
range, | ||
afterCherryPick, | ||
}: { | ||
range: Range | ||
afterCherryPick?: (commit: Commit) => Promise<void> | ||
range: Range; | ||
afterCherryPick?: (commit: Commit) => Promise<void>; | ||
}) { | ||
const login = await getUserLogin() | ||
const login = await getUserLogin(); | ||
|
||
for (let i = 0; i < commits.length; i++) { | ||
const commit = commits[i] | ||
consoleBoxen(`🧮 Triaging ${i + 1} of ${commits.length}`, commit.line) | ||
const commit = commits[i]; | ||
consoleBoxen(`🧮 Triaging ${i + 1} of ${commits.length}`, commit.line); | ||
|
||
while (true) { | ||
const res = resolveRes(await question('Ok to cherry pick? [Y/n/o(pen)] > ')) | ||
const res = resolveRes(await question("Ok to cherry pick? [Y/n/o(pen)] > ")); | ||
|
||
if (res === 'open') { | ||
if (res === "open") { | ||
if (commit.url) { | ||
await $`open ${commit.url}` | ||
await $`open ${commit.url}`; | ||
} else { | ||
console.log("There's no PR associated with this commit") | ||
console.log("There's no PR associated with this commit"); | ||
} | ||
continue | ||
} else if (res === 'no') { | ||
let res = await question('Add a note explaining why not > ') | ||
res = `(${login}) ${res}` | ||
await $`git notes add -m ${res} ${commit.hash}` | ||
await $`git notes show ${commit.hash}` | ||
console.log(`You can edit the note with \`git notes edit ${commit.hash}\``) | ||
break | ||
continue; | ||
} else if (res === "no") { | ||
let res = await question("Add a note explaining why not > "); | ||
res = `(${login}) ${res}`; | ||
await $`git notes add -m ${res} ${commit.hash}`; | ||
await $`git notes show ${commit.hash}`; | ||
console.log(`You can edit the note with \`git notes edit ${commit.hash}\``); | ||
break; | ||
} | ||
|
||
try { | ||
await $`git switch ${range.to}` | ||
await $`git cherry-pick ${commit.hash}` | ||
console.log() | ||
console.log(chalk.green('🌸 Successfully cherry picked')) | ||
await afterCherryPick?.(commit) | ||
break | ||
await $`git switch ${range.to}`; | ||
await $`git cherry-pick ${commit.hash}`; | ||
console.log(); | ||
console.log(chalk.green("🌸 Successfully cherry picked")); | ||
await afterCherryPick?.(commit); | ||
break; | ||
} catch (_error) { | ||
console.log() | ||
console.log(); | ||
console.log(chalk.yellow([ | ||
"✋ Couldn't cleanly cherry pick. Resolve the conflicts and run `git cherry-pick --continue`", | ||
"🙅 Don't edit the commit message!" | ||
].join('\n'))) | ||
console.log() | ||
await question('Press anything to continue > ') | ||
await afterCherryPick?.(commit) | ||
break | ||
"🙅 Don't edit the commit message!", | ||
].join("\n"))); | ||
console.log(); | ||
await question("Press anything to continue > "); | ||
await afterCherryPick?.(commit); | ||
break; | ||
} | ||
} | ||
|
||
console.log() | ||
console.log(); | ||
} | ||
|
||
consoleBoxen('🏁 Finish!', `All ${commits.length} commits have been triaged`) | ||
consoleBoxen("🏁 Finish!", `All ${commits.length} commits have been triaged`); | ||
} | ||
|
||
export function reportCommitsEligibleForCherryPick(commits: Commit[]) { | ||
consoleBoxen(`🧮 ${commits.length} commits to triage`, commits.map(commit => commit.line).join('\n')) | ||
consoleBoxen(`🧮 ${commits.length} commits to triage`, commits.map(commit => commit.line).join("\n")); | ||
} |
Oops, something went wrong.