Skip to content

Commit a009b62

Browse files
authored
Center labels on maintainers usage (#1170)
2 parents a1229a6 + 183f42c commit a009b62

File tree

12 files changed

+475
-368
lines changed

12 files changed

+475
-368
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
All changes that impact users of this module are documented in this file, in the [Common Changelog](https://common-changelog.org) format with some additional specifications defined in the CONTRIBUTING file. This codebase adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
44

5+
## Unreleased [major]
6+
7+
> Development of this release was supported by the [French Ministry for Foreign Affairs](https://www.diplomatie.gouv.fr/fr/politique-etrangere-de-la-france/diplomatie-numerique/) through its ministerial [State Startups incubator](https://beta.gouv.fr/startups/open-terms-archive.html) under the aegis of the Ambassador for Digital Affairs.
8+
9+
### Changed
10+
11+
- **Breaking:** Replace generic issue labels with more descriptive names to better understand why terms tracking was interrupted; existing labels will be automatically updated on the first run
12+
- Add `⚠ needs intervention` label to highlight critical issues requiring manual action to restore terms tracking
13+
514
## 5.7.0 - 2025-06-30
615

716
_Full changeset and discussions: [#1169](https://github.com/OpenTermsArchive/engine/pull/1169)._

src/reporter/github/index.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { createRequire } from 'module';
33
import { Octokit } from 'octokit';
44

55
import logger from '../../logger/index.js';
6+
import { LABELS, MANAGED_BY_OTA_MARKER, DEPRECATED_MANAGED_BY_OTA_MARKER } from '../labels.js';
67

78
const require = createRequire(import.meta.url);
89

9-
export const MANAGED_BY_OTA_MARKER = '[managed by OTA]';
10-
1110
export default class GitHub {
1211
static ISSUE_STATE_CLOSED = 'closed';
1312
static ISSUE_STATE_OPEN = 'open';
1413
static ISSUE_STATE_ALL = 'all';
14+
static MAX_LABEL_DESCRIPTION_LENGTH = 100;
1515

1616
constructor(repository) {
1717
const { version } = require('../../../package.json');
@@ -49,9 +49,19 @@ export default class GitHub {
4949
}
5050

5151
async initialize() {
52-
this.MANAGED_LABELS = require('./labels.json');
52+
this.MANAGED_LABELS = Object.values(LABELS);
5353
try {
5454
const existingLabels = await this.getRepositoryLabels();
55+
const labelsToRemove = existingLabels.filter(label => label.description && label.description.includes(DEPRECATED_MANAGED_BY_OTA_MARKER));
56+
57+
if (labelsToRemove.length) {
58+
logger.info(`Removing labels with deprecated markers: ${labelsToRemove.map(label => `"${label.name}"`).join(', ')}`);
59+
60+
for (const label of labelsToRemove) {
61+
await this.deleteLabel(label.name); /* eslint-disable-line no-await-in-loop */
62+
}
63+
}
64+
5565
const existingLabelsNames = existingLabels.map(label => label.name);
5666
const missingLabels = this.MANAGED_LABELS.filter(label => !existingLabelsNames.includes(label.name));
5767

@@ -116,6 +126,13 @@ export default class GitHub {
116126
});
117127
}
118128

129+
async deleteLabel(name) {
130+
await this.octokit.request('DELETE /repos/{owner}/{repo}/labels/{name}', {
131+
...this.commonParams,
132+
name,
133+
});
134+
}
135+
119136
async getIssue(title) {
120137
return (await this.issues).get(title);
121138
}
@@ -174,25 +191,25 @@ export default class GitHub {
174191
}
175192
}
176193

177-
async createOrUpdateIssue({ title, description, label }) {
194+
async createOrUpdateIssue({ title, description, labels }) {
178195
try {
179196
const issue = await this.getIssue(title);
180197

181198
if (!issue) {
182-
const createdIssue = await this.createIssue({ title, description, labels: [label] });
199+
const createdIssue = await this.createIssue({ title, description, labels });
183200

184201
return logger.info(`Created issue #${createdIssue.number} "${title}": ${createdIssue.html_url}`);
185202
}
186203

187204
const managedLabelsNames = this.MANAGED_LABELS.map(label => label.name);
188205
const labelsNotManagedToKeep = issue.labels.map(label => label.name).filter(label => !managedLabelsNames.includes(label));
189-
const [managedLabel] = issue.labels.filter(label => managedLabelsNames.includes(label.name)); // It is assumed that only one specific reason for failure is possible at a time, making managed labels mutually exclusive
206+
const managedLabels = issue.labels.filter(label => managedLabelsNames.includes(label.name));
190207

191-
if (issue.state !== GitHub.ISSUE_STATE_CLOSED && managedLabel?.name === label) {
192-
return;
208+
if (issue.state !== GitHub.ISSUE_STATE_CLOSED && labels.every(label => managedLabels.some(managedLabel => managedLabel.name === label))) {
209+
return; // if all requested labels are already assigned to the issue, the error is redundant with the one already reported and no further action is necessary
193210
}
194211

195-
const updatedIssue = await this.updateIssue(issue, { state: GitHub.ISSUE_STATE_OPEN, labels: [ label, ...labelsNotManagedToKeep ] });
212+
const updatedIssue = await this.updateIssue(issue, { state: GitHub.ISSUE_STATE_OPEN, labels: [ ...labels, ...labelsNotManagedToKeep ] });
196213

197214
await this.addCommentToIssue({ issue, comment: description });
198215

src/reporter/github/index.test.js

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
import { createRequire } from 'module';
2-
31
import { expect } from 'chai';
42
import nock from 'nock';
53

6-
import GitHub from './index.js';
4+
import { LABELS } from '../labels.js';
75

8-
const require = createRequire(import.meta.url);
6+
import GitHub from './index.js';
97

108
describe('GitHub', function () {
119
this.timeout(5000);
1210

1311
let MANAGED_LABELS;
1412
let github;
15-
const EXISTING_OPEN_ISSUE = { number: 1, title: 'Opened issue', description: 'Issue description', state: GitHub.ISSUE_STATE_OPEN, labels: [{ name: 'location' }] };
16-
const EXISTING_CLOSED_ISSUE = { number: 2, title: 'Closed issue', description: 'Issue description', state: GitHub.ISSUE_STATE_CLOSED, labels: [{ name: '403' }] };
13+
const EXISTING_OPEN_ISSUE = { number: 1, title: 'Opened issue', description: 'Issue description', state: GitHub.ISSUE_STATE_OPEN, labels: [{ name: 'page access restriction' }, { name: 'server error' }] };
14+
const EXISTING_CLOSED_ISSUE = { number: 2, title: 'Closed issue', description: 'Issue description', state: GitHub.ISSUE_STATE_CLOSED, labels: [{ name: 'empty content' }] };
1715

1816
before(async () => {
19-
MANAGED_LABELS = require('./labels.json');
17+
MANAGED_LABELS = Object.values(LABELS);
2018
github = new GitHub('owner/repo');
2119
nock('https://api.github.com')
2220
.get('/repos/owner/repo/issues')
@@ -274,23 +272,23 @@ describe('GitHub', function () {
274272

275273
describe('#createOrUpdateIssue', () => {
276274
before(() => {
277-
github.MANAGED_LABELS = require('./labels.json');
275+
github.MANAGED_LABELS = Object.values(LABELS);
278276
});
279277

280278
context('when the issue does not exist', () => {
281279
let createIssueScope;
282280
const ISSUE_TO_CREATE = {
283281
title: 'New Issue',
284282
description: 'Description of the new issue',
285-
label: 'bug',
283+
labels: ['empty response'],
286284
};
287285

288286
before(async () => {
289287
createIssueScope = nock('https://api.github.com')
290288
.post('/repos/owner/repo/issues', {
291289
title: ISSUE_TO_CREATE.title,
292290
body: ISSUE_TO_CREATE.description,
293-
labels: [ISSUE_TO_CREATE.label],
291+
labels: ISSUE_TO_CREATE.labels,
294292
})
295293
.reply(200, { number: 123 });
296294

@@ -313,14 +311,14 @@ describe('GitHub', function () {
313311

314312
before(async () => {
315313
updateIssueScope = nock('https://api.github.com')
316-
.patch(`/repos/owner/repo/issues/${EXISTING_CLOSED_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['location'] })
314+
.patch(`/repos/owner/repo/issues/${EXISTING_CLOSED_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['page access restriction'] })
317315
.reply(200);
318316

319317
addCommentScope = nock('https://api.github.com')
320318
.post(`/repos/owner/repo/issues/${EXISTING_CLOSED_ISSUE.number}/comments`, { body: EXISTING_CLOSED_ISSUE.description })
321319
.reply(200);
322320

323-
await github.createOrUpdateIssue({ title: EXISTING_CLOSED_ISSUE.title, description: EXISTING_CLOSED_ISSUE.description, label: 'location' });
321+
await github.createOrUpdateIssue({ title: EXISTING_CLOSED_ISSUE.title, description: EXISTING_CLOSED_ISSUE.description, labels: ['page access restriction'] });
324322
});
325323

326324
after(() => {
@@ -344,14 +342,14 @@ describe('GitHub', function () {
344342

345343
before(async () => {
346344
updateIssueScope = nock('https://api.github.com')
347-
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['404'] })
345+
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: ['empty content'] })
348346
.reply(200);
349347

350348
addCommentScope = nock('https://api.github.com')
351349
.post(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}/comments`, { body: EXISTING_OPEN_ISSUE.description })
352350
.reply(200);
353351

354-
await github.createOrUpdateIssue({ title: EXISTING_OPEN_ISSUE.title, description: EXISTING_OPEN_ISSUE.description, label: '404' });
352+
await github.createOrUpdateIssue({ title: EXISTING_OPEN_ISSUE.title, description: EXISTING_OPEN_ISSUE.description, labels: ['empty content'] });
355353
});
356354

357355
after(() => {
@@ -367,25 +365,27 @@ describe('GitHub', function () {
367365
expect(addCommentScope.isDone()).to.be.true;
368366
});
369367
});
370-
context('when the reason did not change', () => {
368+
369+
context('when all requested labels are already present', () => {
371370
let addCommentScope;
372371
let updateIssueScope;
373372

374373
before(async () => {
375374
updateIssueScope = nock('https://api.github.com')
376-
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: EXISTING_OPEN_ISSUE.labels })
375+
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`)
377376
.reply(200);
378377

379378
addCommentScope = nock('https://api.github.com')
380-
.post(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}/comments`, { body: EXISTING_OPEN_ISSUE.description })
379+
.post(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}/comments`)
381380
.reply(200);
382381

383-
await github.createOrUpdateIssue({ title: EXISTING_OPEN_ISSUE.title, description: EXISTING_OPEN_ISSUE.description, label: EXISTING_OPEN_ISSUE.labels[0].name });
382+
await github.createOrUpdateIssue({ title: EXISTING_OPEN_ISSUE.title, description: EXISTING_OPEN_ISSUE.description, labels: [ 'page access restriction', 'server error' ] });
384383
});
385384

386385
after(() => {
387-
github.issuesCache.delete(EXISTING_OPEN_ISSUE.title);
388386
github.issuesCache.set(EXISTING_OPEN_ISSUE.title, EXISTING_OPEN_ISSUE);
387+
388+
nock.cleanAll();
389389
});
390390

391391
it('does not attempt to updates the issue’s labels', () => {
@@ -396,6 +396,37 @@ describe('GitHub', function () {
396396
expect(addCommentScope.isDone()).to.be.false;
397397
});
398398
});
399+
400+
context('when some but not all requested labels are present', () => {
401+
let addCommentScope;
402+
let updateIssueScope;
403+
404+
before(async () => {
405+
updateIssueScope = nock('https://api.github.com')
406+
.patch(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}`, { state: GitHub.ISSUE_STATE_OPEN, labels: [ 'page access restriction', 'empty content' ] })
407+
.reply(200);
408+
409+
addCommentScope = nock('https://api.github.com')
410+
.post(`/repos/owner/repo/issues/${EXISTING_OPEN_ISSUE.number}/comments`, { body: EXISTING_OPEN_ISSUE.description })
411+
.reply(200);
412+
413+
await github.createOrUpdateIssue({ title: EXISTING_OPEN_ISSUE.title, description: EXISTING_OPEN_ISSUE.description, labels: [ 'page access restriction', 'empty content' ] });
414+
});
415+
416+
after(() => {
417+
github.issuesCache.set(EXISTING_OPEN_ISSUE.title, EXISTING_OPEN_ISSUE);
418+
419+
nock.cleanAll();
420+
});
421+
422+
it("updates the issue's labels", () => {
423+
expect(updateIssueScope.isDone()).to.be.true;
424+
});
425+
426+
it('adds a comment to the issue', () => {
427+
expect(addCommentScope.isDone()).to.be.true;
428+
});
429+
});
399430
});
400431
});
401432
});

src/reporter/github/labels.json

Lines changed: 0 additions & 87 deletions
This file was deleted.

src/reporter/github/labels.test.js

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)