Skip to content

Commit 44f6637

Browse files
committed
fix: make issue-labels-migrate idempotent when target labels already exist
1 parent de550d4 commit 44f6637

1 file changed

Lines changed: 70 additions & 0 deletions

File tree

.github/workflows/issue-labels-migrate.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,57 @@ jobs:
2525
{ from: 'documentation', to: 'type:documentation' }
2626
];
2727
28+
function isAlreadyExistsError(err) {
29+
return err?.status === 422
30+
&& Array.isArray(err?.errors)
31+
&& err.errors.some(e => e.code === 'already_exists' && e.field === 'name');
32+
}
33+
34+
async function relabelItems(from, to) {
35+
const items = await github.paginate(github.rest.issues.listForRepo, {
36+
owner: context.repo.owner,
37+
repo: context.repo.repo,
38+
state: 'all',
39+
labels: from,
40+
per_page: 100
41+
});
42+
43+
if (items.length === 0) {
44+
core.info(`No issues or PRs found with legacy label "${from}"`);
45+
return;
46+
}
47+
48+
for (const item of items) {
49+
const existing = (item.labels || [])
50+
.map(l => (typeof l === 'string' ? l : l.name))
51+
.filter(Boolean);
52+
53+
if (!existing.includes(to)) {
54+
await github.rest.issues.addLabels({
55+
owner: context.repo.owner,
56+
repo: context.repo.repo,
57+
issue_number: item.number,
58+
labels: [to]
59+
});
60+
}
61+
62+
try {
63+
await github.rest.issues.removeLabel({
64+
owner: context.repo.owner,
65+
repo: context.repo.repo,
66+
issue_number: item.number,
67+
name: from
68+
});
69+
} catch (err) {
70+
if (err.status !== 404) {
71+
throw err;
72+
}
73+
}
74+
}
75+
76+
core.info(`Re-labeled ${items.length} issues/PRs: ${from} → ${to}`);
77+
}
78+
2879
for (const { from, to } of MIGRATIONS) {
2980
try {
3081
await github.rest.issues.getLabel({
@@ -43,6 +94,25 @@ jobs:
4394
} catch (err) {
4495
if (err.status === 404) {
4596
core.info(`Legacy label "${from}" not found — no migration needed`);
97+
} else if (isAlreadyExistsError(err)) {
98+
core.info(`Target label "${to}" already exists; applying fallback migration for "${from}"`);
99+
100+
await relabelItems(from, to);
101+
102+
try {
103+
await github.rest.issues.deleteLabel({
104+
owner: context.repo.owner,
105+
repo: context.repo.repo,
106+
name: from
107+
});
108+
core.info(`Deleted legacy label "${from}" after fallback migration`);
109+
} catch (deleteErr) {
110+
if (deleteErr.status === 404) {
111+
core.info(`Legacy label "${from}" already removed`);
112+
} else {
113+
throw deleteErr;
114+
}
115+
}
46116
} else {
47117
core.warning(`Failed to migrate ${from}: ${err.message}`);
48118
}

0 commit comments

Comments
 (0)