Skip to content

Commit 559934f

Browse files
fix: allow existing labels (never gh label delete) (#737)
## PR Checklist - [x] Addresses an existing open issue: fixes #735 - [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 Removes `gh label delete` because if a label already exists, we're always either... * ...editing it to be the equivalent outcome label * ...leaving it be because it's an unrelated repo-specific label While I'm here, applies a quick optimization to only send edit requests for existing labels whose info is different from their equivalent outcome.
1 parent 8c819f6 commit 559934f

File tree

4 files changed

+254
-71
lines changed

4 files changed

+254
-71
lines changed

src/steps/initializeGitHubRepository/labels/getExistingEquivalentLabel.test.ts

+22-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { describe, expect, it } from "vitest";
22

33
import { getExistingEquivalentLabel } from "./getExistingEquivalentLabel.js";
44

5+
const createLabel = (name: string) => ({
6+
color: "#000000",
7+
description: "A good label.",
8+
name,
9+
});
10+
511
describe("getExistingEquivalentLabel", () => {
612
it("returns undefined when there are no existing labels", () => {
713
const actual = getExistingEquivalentLabel([], "abc");
@@ -10,38 +16,45 @@ describe("getExistingEquivalentLabel", () => {
1016
});
1117

1218
it("returns undefined when no existing label matches", () => {
13-
const actual = getExistingEquivalentLabel(["abc"], "def");
19+
const actual = getExistingEquivalentLabel([createLabel("abc")], "def");
1420

1521
expect(actual).toBe(undefined);
1622
});
1723

1824
it("returns an existing un-prefixed label when it matches by name", () => {
19-
const actual = getExistingEquivalentLabel(["def", "abc", "ghi"], "abc");
25+
const abcLabel = createLabel("abc");
26+
const actual = getExistingEquivalentLabel(
27+
[createLabel("def"), abcLabel, createLabel("ghi")],
28+
"abc",
29+
);
2030

21-
expect(actual).toBe("abc");
31+
expect(actual).toBe(abcLabel);
2232
});
2333

2434
it("returns an existing prefixed label when it matches by name", () => {
25-
const actual = getExistingEquivalentLabel(["abc: def"], "abc: def");
35+
const abcDefLabel = createLabel("abc: def");
36+
const actual = getExistingEquivalentLabel([abcDefLabel], "abc: def");
2637

27-
expect(actual).toBe("abc: def");
38+
expect(actual).toBe(abcDefLabel);
2839
});
2940

3041
it("returns the existing label when it matches excluding prefix", () => {
42+
const abcLabel = createLabel("abc");
3143
const actual = getExistingEquivalentLabel(
32-
["abc: def", "abc", "ghi"],
44+
[createLabel("abc: def"), abcLabel, createLabel("ghi")],
3345
"type: abc",
3446
);
3547

36-
expect(actual).toBe("abc");
48+
expect(actual).toBe(abcLabel);
3749
});
3850

3951
it("returns the existing label when it matches an alias", () => {
52+
const enhancementLabel = createLabel("enhancement");
4053
const actual = getExistingEquivalentLabel(
41-
["abc: def", "enhancement", "ghi"],
54+
[createLabel("abc: def"), enhancementLabel, createLabel("ghi")],
4255
"type: feature",
4356
);
4457

45-
expect(actual).toBe("enhancement");
58+
expect(actual).toBe(enhancementLabel);
4659
});
4760
});

src/steps/initializeGitHubRepository/labels/getExistingEquivalentLabel.ts

+12-6
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@ const aliases = new Map([
33
["help wanted", "status: accepting prs"],
44
]);
55

6+
export interface GhLabelData {
7+
color: string;
8+
description: string;
9+
name: string;
10+
}
11+
612
export function getExistingEquivalentLabel(
7-
existingLabels: string[],
13+
existingLabels: GhLabelData[],
814
outcomeLabel: string,
915
) {
1016
const outcomeTrimmed = outcomeLabel.replace(/\w+: (\w+)/, "$1");
1117

12-
return existingLabels.find((existingLabel) => {
18+
return existingLabels.find(({ name: existingName }) => {
1319
return (
14-
existingLabel === outcomeLabel ||
15-
existingLabel === outcomeTrimmed ||
16-
aliases.get(existingLabel) === outcomeLabel ||
17-
existingLabel.replace(/\w+: (\w+)/, "$1") === outcomeLabel
20+
existingName === outcomeLabel ||
21+
existingName === outcomeTrimmed ||
22+
aliases.get(existingName) === outcomeLabel ||
23+
existingName.replace(/\w+: (\w+)/, "$1") === outcomeLabel
1824
);
1925
});
2026
}

src/steps/initializeGitHubRepository/labels/initializeRepositoryLabels.test.ts

+207-35
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,49 @@ vi.mock("execa", () => ({
1010
},
1111
}));
1212

13+
const mockOutcomeLabel = {
14+
color: "000000",
15+
description: "def ghi",
16+
name: "abc",
17+
};
18+
1319
vi.mock("./outcomeLabels.js", () => ({
14-
outcomeLabels: [
15-
{
16-
color: "000000",
17-
description: "def ghi",
18-
name: "abc",
19-
},
20-
],
20+
get outcomeLabels() {
21+
return [mockOutcomeLabel];
22+
},
2123
}));
2224

2325
describe("migrateRepositoryLabels", () => {
24-
it("creates a setup label when it doesn't already exist", async () => {
26+
it("creates a outcome label when labels stdout is returned", async () => {
27+
mock$.mockResolvedValue({
28+
stdout: "",
29+
});
30+
31+
await initializeRepositoryLabels();
32+
33+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
34+
[
35+
[
36+
[
37+
"gh label list --json color,description,name",
38+
],
39+
],
40+
[
41+
[
42+
"gh label create ",
43+
" --color ",
44+
" --description ",
45+
"",
46+
],
47+
"abc",
48+
"000000",
49+
"def ghi",
50+
],
51+
]
52+
`);
53+
});
54+
55+
it("creates a outcome label when it doesn't already exist", async () => {
2556
mock$.mockResolvedValue({
2657
stdout: JSON.stringify([
2758
{
@@ -34,37 +65,148 @@ describe("migrateRepositoryLabels", () => {
3465

3566
await initializeRepositoryLabels();
3667

37-
expect(mock$).toHaveBeenCalledWith(
38-
["gh label create ", " --color ", " --description ", ""],
39-
"abc",
40-
"000000",
41-
"def ghi",
42-
);
68+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
69+
[
70+
[
71+
[
72+
"gh label list --json color,description,name",
73+
],
74+
],
75+
[
76+
[
77+
"gh label create ",
78+
" --color ",
79+
" --description ",
80+
"",
81+
],
82+
"abc",
83+
"000000",
84+
"def ghi",
85+
],
86+
]
87+
`);
88+
});
89+
90+
it("doesn't edit a outcome label when it already exists with the same information", async () => {
91+
mock$.mockResolvedValue({
92+
stdout: JSON.stringify([mockOutcomeLabel]),
93+
});
94+
95+
await initializeRepositoryLabels();
96+
97+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
98+
[
99+
[
100+
[
101+
"gh label list --json color,description,name",
102+
],
103+
],
104+
]
105+
`);
43106
});
44107

45-
it("edits a setup label when it already exists", async () => {
108+
it("edits a outcome label when it already exists with different color", async () => {
109+
mock$.mockResolvedValue({
110+
stdout: JSON.stringify([
111+
{
112+
...mockOutcomeLabel,
113+
color: "111111",
114+
},
115+
]),
116+
});
117+
118+
await initializeRepositoryLabels();
119+
120+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
121+
[
122+
[
123+
[
124+
"gh label list --json color,description,name",
125+
],
126+
],
127+
[
128+
[
129+
"gh label edit ",
130+
" --color ",
131+
" --description ",
132+
" --name ",
133+
"",
134+
],
135+
"abc",
136+
"000000",
137+
"def ghi",
138+
"abc",
139+
],
140+
]
141+
`);
142+
});
143+
144+
it("edits a outcome label when it already exists with different description", async () => {
145+
mock$.mockResolvedValue({
146+
stdout: JSON.stringify([
147+
{
148+
...mockOutcomeLabel,
149+
description: "updated",
150+
},
151+
]),
152+
});
153+
154+
await initializeRepositoryLabels();
155+
156+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
157+
[
158+
[
159+
[
160+
"gh label list --json color,description,name",
161+
],
162+
],
163+
[
164+
[
165+
"gh label edit ",
166+
" --color ",
167+
" --description ",
168+
" --name ",
169+
"",
170+
],
171+
"abc",
172+
"000000",
173+
"def ghi",
174+
"abc",
175+
],
176+
]
177+
`);
178+
});
179+
180+
it("deletes an existing non-outcome label when the equivalent outcome label already exists", async () => {
46181
mock$.mockResolvedValue({
47182
stdout: JSON.stringify([
48183
{
49184
color: "000000",
50185
description: "def ghi",
51186
name: "abc",
52187
},
188+
{
189+
color: "000000",
190+
description: "def ghi",
191+
name: "area: abc",
192+
},
53193
]),
54194
});
55195

56196
await initializeRepositoryLabels();
57197

58-
expect(mock$).toHaveBeenCalledWith(
59-
["gh label edit ", " --color ", " --description ", " --name ", ""],
60-
"abc",
61-
"000000",
62-
"def ghi",
63-
"abc",
64-
);
198+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
199+
[
200+
[
201+
[
202+
"gh label list --json color,description,name",
203+
],
204+
],
205+
]
206+
`);
65207
});
66208

67-
it("deletes a pre-existing label when it isn't a setup label", async () => {
209+
it("deletes a pre-existing label when it isn't a outcome label", async () => {
68210
mock$.mockResolvedValue({
69211
stdout: JSON.stringify([
70212
{
@@ -77,30 +219,60 @@ describe("migrateRepositoryLabels", () => {
77219

78220
await initializeRepositoryLabels();
79221

80-
expect(mock$).toHaveBeenCalledWith(
81-
["gh label delete ", " --yes"],
82-
"unknown",
83-
);
84-
expect(mock$).toHaveBeenCalledTimes(3);
222+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
223+
[
224+
[
225+
[
226+
"gh label list --json color,description,name",
227+
],
228+
],
229+
[
230+
[
231+
"gh label create ",
232+
" --color ",
233+
" --description ",
234+
"",
235+
],
236+
"abc",
237+
"000000",
238+
"def ghi",
239+
],
240+
]
241+
`);
85242
});
86243

87-
it("doesn't delete a pre-existing label when it isn't a setup label", async () => {
244+
it("doesn't delete a pre-existing label when it isn't a outcome label", async () => {
88245
mock$.mockResolvedValue({
89246
stdout: JSON.stringify([
90247
{
91248
color: "000000",
92249
description: "def ghi",
93-
name: "abc",
250+
name: "jkl",
94251
},
95252
]),
96253
});
97254

98255
await initializeRepositoryLabels();
99256

100-
expect(mock$).not.toHaveBeenCalledWith(
101-
["gh label delete ", " --yes"],
102-
"abc",
103-
);
104-
expect(mock$).toHaveBeenCalledTimes(2);
257+
expect(mock$.mock.calls).toMatchInlineSnapshot(`
258+
[
259+
[
260+
[
261+
"gh label list --json color,description,name",
262+
],
263+
],
264+
[
265+
[
266+
"gh label create ",
267+
" --color ",
268+
" --description ",
269+
"",
270+
],
271+
"abc",
272+
"000000",
273+
"def ghi",
274+
],
275+
]
276+
`);
105277
});
106278
});

0 commit comments

Comments
 (0)