Skip to content

Commit

Permalink
add eslint and dprint
Browse files Browse the repository at this point in the history
  • Loading branch information
jtoar committed Feb 29, 2024
1 parent 7ae7aa9 commit 08afe50
Show file tree
Hide file tree
Showing 134 changed files with 3,376 additions and 1,062 deletions.
1,249 changes: 1,234 additions & 15 deletions .pnp.cjs

Large diffs are not rendered by default.

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.
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.
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
19 changes: 19 additions & 0 deletions dprint.json
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"
]
}
21 changes: 21 additions & 0 deletions eslint.config.js
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",
},
},
);
28 changes: 14 additions & 14 deletions lib/assert_github_token.test.ts
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;
});
});
24 changes: 13 additions & 11 deletions lib/assert_github_token.ts
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"));
}
50 changes: 26 additions & 24 deletions lib/branches.test.ts
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"]);
});
});
62 changes: 31 additions & 31 deletions lib/branches.ts
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();
}
76 changes: 38 additions & 38 deletions lib/cherry_pick_commits.ts
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"));
}
Loading

0 comments on commit 08afe50

Please sign in to comment.