Skip to content

Commit 06db74f

Browse files
feat: reuse GitHub auth in all-contributors-cli (#788)
## PR Checklist - [x] Addresses an existing open issue: fixes #787 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/create-typescript-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/create-typescript-app/blob/main/.github/CONTRIBUTING.md) were taken ## Overview Wraps the existing `octokit` property in one that also has `auth`.
1 parent f9d63fc commit 06db74f

10 files changed

+120
-82
lines changed

src/create/createWithOptions.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@ import { $ } from "execa";
33

44
import { withSpinner, withSpinners } from "../shared/cli/spinners.js";
55
import { doesRepositoryExist } from "../shared/doesRepositoryExist.js";
6-
import { OctokitAndOptions } from "../shared/options/readOptions.js";
6+
import { GitHubAndOptions } from "../shared/options/readOptions.js";
77
import { addToolAllContributors } from "../steps/addToolAllContributors.js";
88
import { finalizeDependencies } from "../steps/finalizeDependencies.js";
99
import { initializeGitHubRepository } from "../steps/initializeGitHubRepository/index.js";
1010
import { runCommands } from "../steps/runCommands.js";
1111
import { writeReadme } from "../steps/writeReadme.js";
1212
import { writeStructure } from "../steps/writing/writeStructure.js";
1313

14-
export async function createWithOptions({
15-
octokit,
16-
options,
17-
}: OctokitAndOptions) {
14+
export async function createWithOptions({ github, options }: GitHubAndOptions) {
1815
await withSpinners("Creating repository structure", [
1916
[
2017
"Writing structure",
@@ -49,8 +46,8 @@ export async function createWithOptions({
4946
]);
5047

5148
const sendToGitHub =
52-
octokit &&
53-
(await doesRepositoryExist(octokit, options)) &&
49+
github &&
50+
(await doesRepositoryExist(github.octokit, options)) &&
5451
(options.createRepository ??
5552
(await prompts.confirm({
5653
message:
@@ -63,7 +60,7 @@ export async function createWithOptions({
6360
await $`git add -A`;
6461
await $`git commit --message ${"feat: initialized repo ✨"}`;
6562
await $`git push -u origin main --force`;
66-
await initializeGitHubRepository(octokit, options);
63+
await initializeGitHubRepository(github.octokit, options);
6764
});
6865
}
6966

src/initialize/initializeWithOptions.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { withSpinner, withSpinners } from "../shared/cli/spinners.js";
2-
import { OctokitAndOptions } from "../shared/options/readOptions.js";
2+
import { GitHubAndOptions } from "../shared/options/readOptions.js";
33
import { addOwnerAsAllContributor } from "../steps/addOwnerAsAllContributor.js";
44
import { clearChangelog } from "../steps/clearChangelog.js";
55
import { initializeGitHubRepository } from "../steps/initializeGitHubRepository/index.js";
@@ -12,9 +12,9 @@ import { updateLocalFiles } from "../steps/updateLocalFiles.js";
1212
import { updateReadme } from "../steps/updateReadme.js";
1313

1414
export async function initializeWithOptions({
15-
octokit,
15+
github,
1616
options,
17-
}: OctokitAndOptions) {
17+
}: GitHubAndOptions) {
1818
await withSpinners("Initializing local files", [
1919
[
2020
"Updating local files",
@@ -39,9 +39,9 @@ export async function initializeWithOptions({
3939
});
4040
}
4141

42-
if (octokit) {
42+
if (github) {
4343
await withSpinner("Initializing GitHub repository", async () => {
44-
await initializeGitHubRepository(octokit, options);
44+
await initializeGitHubRepository(github.octokit, options);
4545
});
4646
}
4747

src/migrate/migrateWithOptions.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { withSpinner, withSpinners } from "../shared/cli/spinners.js";
2-
import { OctokitAndOptions } from "../shared/options/readOptions.js";
2+
import { GitHubAndOptions } from "../shared/options/readOptions.js";
33
import { clearUnnecessaryFiles } from "../steps/clearUnnecessaryFiles.js";
44
import { detectExistingContributors } from "../steps/detectExistingContributors.js";
55
import { finalizeDependencies } from "../steps/finalizeDependencies.js";
@@ -11,9 +11,9 @@ import { writeReadme } from "../steps/writeReadme.js";
1111
import { writeStructure } from "../steps/writing/writeStructure.js";
1212

1313
export async function migrateWithOptions({
14-
octokit,
14+
github,
1515
options,
16-
}: OctokitAndOptions) {
16+
}: GitHubAndOptions) {
1717
await withSpinners("Migrating repository structure", [
1818
["Clearing unnecessary files", clearUnnecessaryFiles],
1919
[
@@ -42,15 +42,15 @@ export async function migrateWithOptions({
4242
],
4343
]);
4444

45-
if (octokit) {
45+
if (github) {
4646
await withSpinner("Initializing GitHub repository", async () => {
47-
await initializeGitHubRepository(octokit, options);
47+
await initializeGitHubRepository(github.octokit, options);
4848
});
4949
}
5050

5151
if (!options.excludeContributors) {
5252
await withSpinner("Detecting existing contributors", async () =>
53-
detectExistingContributors(options),
53+
detectExistingContributors(github?.auth, options),
5454
);
5555
}
5656

src/shared/options/ensureRepositoryExists.test.ts

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ vi.mock("../doesRepositoryExist.js", () => ({
2626
},
2727
}));
2828

29+
const auth = "abc123";
2930
const owner = "StubOwner";
3031
const repository = "stub-repository";
3132

@@ -48,27 +49,33 @@ describe("ensureRepositoryExists", () => {
4849
it("returns the repository when octokit is defined and the repository exists", async () => {
4950
mockDoesRepositoryExist.mockResolvedValue(true);
5051
const octokit = createMockOctokit();
51-
const actual = await ensureRepositoryExists(octokit, {
52-
createRepository: false,
53-
owner,
54-
repository,
55-
});
56-
57-
expect(actual).toEqual({ octokit, repository });
52+
const actual = await ensureRepositoryExists(
53+
{ auth, octokit },
54+
{
55+
createRepository: false,
56+
owner,
57+
repository,
58+
},
59+
);
60+
61+
expect(actual).toEqual({ github: { auth, octokit }, repository });
5862
});
5963

6064
it("creates a new repository when createRepository is true and the repository does not exist", async () => {
6165
const octokit = createMockOctokit();
6266

6367
mockDoesRepositoryExist.mockResolvedValue(false);
6468

65-
const actual = await ensureRepositoryExists(octokit, {
66-
createRepository: true,
67-
owner,
68-
repository,
69-
});
69+
const actual = await ensureRepositoryExists(
70+
{ auth, octokit },
71+
{
72+
createRepository: true,
73+
owner,
74+
repository,
75+
},
76+
);
7077

71-
expect(actual).toEqual({ octokit, repository });
78+
expect(actual).toEqual({ github: { auth, octokit }, repository });
7279
expect(octokit.rest.repos.createUsingTemplate).toHaveBeenCalledWith({
7380
name: repository,
7481
owner,
@@ -83,13 +90,16 @@ describe("ensureRepositoryExists", () => {
8390
mockDoesRepositoryExist.mockResolvedValue(false);
8491
mockSelect.mockResolvedValue("create");
8592

86-
const actual = await ensureRepositoryExists(octokit, {
87-
createRepository: false,
88-
owner,
89-
repository,
90-
});
93+
const actual = await ensureRepositoryExists(
94+
{ auth, octokit },
95+
{
96+
createRepository: false,
97+
owner,
98+
repository,
99+
},
100+
);
91101

92-
expect(actual).toEqual({ octokit, repository });
102+
expect(actual).toEqual({ github: { auth, octokit }, repository });
93103
expect(octokit.rest.repos.createUsingTemplate).toHaveBeenCalledWith({
94104
name: repository,
95105
owner,
@@ -108,13 +118,19 @@ describe("ensureRepositoryExists", () => {
108118
mockSelect.mockResolvedValueOnce("different");
109119
mockText.mockResolvedValue(newRepository);
110120

111-
const actual = await ensureRepositoryExists(octokit, {
112-
createRepository: false,
113-
owner,
114-
repository,
121+
const actual = await ensureRepositoryExists(
122+
{ auth, octokit },
123+
{
124+
createRepository: false,
125+
owner,
126+
repository,
127+
},
128+
);
129+
130+
expect(actual).toEqual({
131+
github: { auth, octokit },
132+
repository: newRepository,
115133
});
116-
117-
expect(actual).toEqual({ octokit, repository: newRepository });
118134
expect(octokit.rest.repos.createUsingTemplate).not.toHaveBeenCalled();
119135
});
120136

@@ -128,13 +144,19 @@ describe("ensureRepositoryExists", () => {
128144
.mockResolvedValueOnce("create");
129145
mockText.mockResolvedValue(newRepository);
130146

131-
const actual = await ensureRepositoryExists(octokit, {
132-
createRepository: false,
133-
owner,
134-
repository,
147+
const actual = await ensureRepositoryExists(
148+
{ auth, octokit },
149+
{
150+
createRepository: false,
151+
owner,
152+
repository,
153+
},
154+
);
155+
156+
expect(actual).toEqual({
157+
github: { auth, octokit },
158+
repository: newRepository,
135159
});
136-
137-
expect(actual).toEqual({ octokit, repository: newRepository });
138160
expect(octokit.rest.repos.createUsingTemplate).toHaveBeenCalledWith({
139161
name: newRepository,
140162
owner,
@@ -149,11 +171,14 @@ describe("ensureRepositoryExists", () => {
149171
mockDoesRepositoryExist.mockResolvedValue(false);
150172
mockSelect.mockResolvedValue("local");
151173

152-
const actual = await ensureRepositoryExists(octokit, {
153-
createRepository: false,
154-
owner,
155-
repository,
156-
});
174+
const actual = await ensureRepositoryExists(
175+
{ auth, octokit },
176+
{
177+
createRepository: false,
178+
owner,
179+
repository,
180+
},
181+
);
157182

158183
expect(actual).toEqual({ octokit: undefined, repository });
159184
expect(octokit.rest.repos.createUsingTemplate).not.toHaveBeenCalled();

src/shared/options/ensureRepositoryExists.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
import * as prompts from "@clack/prompts";
2-
import { Octokit } from "octokit";
32

43
import { doesRepositoryExist } from "../doesRepositoryExist.js";
54
import { filterPromptCancel } from "../prompts.js";
65
import { Options } from "../types.js";
6+
import { GitHub } from "./getGitHub.js";
77

88
export type EnsureRepositoryExistsOptions = Pick<
99
Options,
1010
"createRepository" | "owner" | "repository"
1111
>;
1212

1313
export interface RepositoryExistsResult {
14-
octokit: Octokit | undefined;
14+
github: GitHub | undefined;
1515
repository: string;
1616
}
1717

1818
export async function ensureRepositoryExists(
19-
octokit: Octokit | undefined,
19+
github: GitHub | undefined,
2020
options: EnsureRepositoryExistsOptions,
2121
): Promise<Partial<RepositoryExistsResult>> {
2222
// We'll only respect input options once before prompting for them
2323
let { createRepository, repository } = options;
2424

2525
// We'll continuously pester the user for a repository
2626
// until they bail, create a new one, or it exists.
27-
while (octokit) {
28-
if (await doesRepositoryExist(octokit, options)) {
29-
return { octokit, repository };
27+
while (github) {
28+
if (await doesRepositoryExist(github.octokit, options)) {
29+
return { github, repository };
3030
}
3131

3232
const selection = createRepository
@@ -56,13 +56,13 @@ export async function ensureRepositoryExists(
5656
return {};
5757

5858
case "create":
59-
await octokit.rest.repos.createUsingTemplate({
59+
await github.octokit.rest.repos.createUsingTemplate({
6060
name: repository,
6161
owner: options.owner,
6262
template_owner: "JoshuaKGoldberg",
6363
template_repo: "create-typescript-app",
6464
});
65-
return { octokit, repository };
65+
return { github: github, repository };
6666

6767
case "different":
6868
const newRepository = filterPromptCancel(
@@ -79,10 +79,10 @@ export async function ensureRepositoryExists(
7979
break;
8080

8181
case "local":
82-
octokit = undefined;
82+
github = undefined;
8383
break;
8484
}
8585
}
8686

87-
return { octokit, repository };
87+
return { github: github, repository };
8888
}

src/shared/options/getOctokit.test.ts renamed to src/shared/options/getGitHub.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Octokit } from "octokit";
22
import { describe, expect, it, vi } from "vitest";
33

4-
import { getOctokit } from "./getOctokit.js";
4+
import { getGitHub } from "./getGitHub.js";
55

66
const mock$ = vi.fn();
77

@@ -17,7 +17,7 @@ describe("getOctokit", () => {
1717
it("throws an error when gh auth status fails", async () => {
1818
mock$.mockRejectedValueOnce(new Error("Oh no!"));
1919

20-
await expect(getOctokit).rejects.toMatchInlineSnapshot(
20+
await expect(getGitHub).rejects.toMatchInlineSnapshot(
2121
"[Error: GitHub authentication failed.]",
2222
);
2323
});
@@ -26,8 +26,8 @@ describe("getOctokit", () => {
2626
const auth = "abc123";
2727
mock$.mockResolvedValueOnce({}).mockResolvedValueOnce({ stdout: auth });
2828

29-
const actual = await getOctokit();
29+
const actual = await getGitHub();
3030

31-
expect(actual).toEqual(new Octokit({ auth }));
31+
expect(actual).toEqual({ auth, octokit: new Octokit({ auth }) });
3232
});
3333
});
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { $ } from "execa";
22
import { Octokit } from "octokit";
33

4-
export async function getOctokit(): Promise<Octokit | undefined> {
4+
export interface GitHub {
5+
auth: string;
6+
octokit: Octokit;
7+
}
8+
9+
export async function getGitHub(): Promise<GitHub | undefined> {
510
try {
611
await $`gh auth status`;
712
} catch (error) {
@@ -11,6 +16,7 @@ export async function getOctokit(): Promise<Octokit | undefined> {
1116
}
1217

1318
const auth = (await $`gh auth token`).stdout.trim();
19+
const octokit = new Octokit({ auth });
1420

15-
return new Octokit({ auth });
21+
return { auth, octokit };
1622
}

0 commit comments

Comments
 (0)