mirror of
https://github.com/actions/labeler.git
synced 2025-12-10 11:41:56 +00:00
fix: Limit number of labels added to 100 (#497)
* push to excess labels to avoid reaching the limit
* build dist
* never set more than 100 labels
* use splice instead of set
* ignore IDE folders
* install @octokit/plugin-retry
* always setLabels
* fix indentations
* fix specs
* add spec for excess labels
* prettier
* licensed cache
* revert to !!core.getInput('sync-labels')
* better warning for exceeded labels
* keep manually-added labels
* nest the dedupe logic
* rename `removeLabel` to `removeLabelFromList` to avoid confusion
* use Sets, and issue a call only if labels have actually changed
* remove IDE config folders from gitignore
* remove obsolete duplucation check
---------
Co-authored-by: Mark Massoud <mark@unrealcloud.io>
This commit is contained in:
@@ -3,7 +3,7 @@ name: "@octokit/openapi-types"
|
||||
version: 12.11.0
|
||||
type: npm
|
||||
summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com
|
||||
homepage:
|
||||
homepage:
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
20
.licenses/npm/@octokit/openapi-types-17.2.0.dep.yml
generated
Normal file
20
.licenses/npm/@octokit/openapi-types-17.2.0.dep.yml
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: "@octokit/openapi-types"
|
||||
version: 17.2.0
|
||||
type: npm
|
||||
summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com
|
||||
homepage:
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |-
|
||||
Copyright 2020 Gregor Martynus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
- sources: README.md
|
||||
text: "[MIT](LICENSE)"
|
||||
notices: []
|
||||
34
.licenses/npm/@octokit/plugin-retry.dep.yml
generated
Normal file
34
.licenses/npm/@octokit/plugin-retry.dep.yml
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: "@octokit/plugin-retry"
|
||||
version: 5.0.2
|
||||
type: npm
|
||||
summary: Automatic retry plugin for octokit
|
||||
homepage:
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
- sources: README.md
|
||||
text: "[MIT](LICENSE)"
|
||||
notices: []
|
||||
@@ -3,7 +3,7 @@ name: "@octokit/types"
|
||||
version: 6.41.0
|
||||
type: npm
|
||||
summary: Shared TypeScript definitions for Octokit projects
|
||||
homepage:
|
||||
homepage:
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
20
.licenses/npm/@octokit/types-9.2.3.dep.yml
generated
Normal file
20
.licenses/npm/@octokit/types-9.2.3.dep.yml
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: "@octokit/types"
|
||||
version: 9.2.3
|
||||
type: npm
|
||||
summary: Shared TypeScript definitions for Octokit projects
|
||||
homepage:
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
MIT License Copyright (c) 2019 Octokit contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
- sources: README.md
|
||||
text: "[MIT](LICENSE)"
|
||||
notices: []
|
||||
31
.licenses/npm/bottleneck.dep.yml
generated
Normal file
31
.licenses/npm/bottleneck.dep.yml
generated
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: bottleneck
|
||||
version: 2.19.5
|
||||
type: npm
|
||||
summary: Distributed task scheduler and rate limiter
|
||||
homepage:
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Simon Grondin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
notices: []
|
||||
@@ -13,8 +13,7 @@ export const context = {
|
||||
const mockApi = {
|
||||
rest: {
|
||||
issues: {
|
||||
addLabels: jest.fn(),
|
||||
removeLabel: jest.fn()
|
||||
setLabels: jest.fn()
|
||||
},
|
||||
pulls: {
|
||||
get: jest.fn().mockResolvedValue({}),
|
||||
|
||||
@@ -8,11 +8,11 @@ jest.mock('@actions/core');
|
||||
jest.mock('@actions/github');
|
||||
|
||||
const gh = github.getOctokit('_');
|
||||
const addLabelsMock = jest.spyOn(gh.rest.issues, 'addLabels');
|
||||
const removeLabelMock = jest.spyOn(gh.rest.issues, 'removeLabel');
|
||||
const setLabelsMock = jest.spyOn(gh.rest.issues, 'setLabels');
|
||||
const reposMock = jest.spyOn(gh.rest.repos, 'getContent');
|
||||
const paginateMock = jest.spyOn(gh, 'paginate');
|
||||
const getPullMock = jest.spyOn(gh.rest.pulls, 'get');
|
||||
const coreWarningMock = jest.spyOn(core, 'warning');
|
||||
|
||||
const yamlFixtures = {
|
||||
'only_pdfs.yml': fs.readFileSync('__tests__/fixtures/only_pdfs.yml')
|
||||
@@ -41,12 +41,16 @@ describe('run', () => {
|
||||
configureInput({});
|
||||
usingLabelerConfigYaml('only_pdfs.yml');
|
||||
mockGitHubResponseChangedFiles('foo.pdf');
|
||||
getPullMock.mockResolvedValue(<any>{
|
||||
data: {
|
||||
labels: []
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(removeLabelMock).toHaveBeenCalledTimes(0);
|
||||
expect(addLabelsMock).toHaveBeenCalledTimes(1);
|
||||
expect(addLabelsMock).toHaveBeenCalledWith({
|
||||
expect(setLabelsMock).toHaveBeenCalledTimes(1);
|
||||
expect(setLabelsMock).toHaveBeenCalledWith({
|
||||
owner: 'monalisa',
|
||||
repo: 'helloworld',
|
||||
issue_number: 123,
|
||||
@@ -58,12 +62,16 @@ describe('run', () => {
|
||||
configureInput({dot: true});
|
||||
usingLabelerConfigYaml('only_pdfs.yml');
|
||||
mockGitHubResponseChangedFiles('.foo.pdf');
|
||||
getPullMock.mockResolvedValue(<any>{
|
||||
data: {
|
||||
labels: []
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(removeLabelMock).toHaveBeenCalledTimes(0);
|
||||
expect(addLabelsMock).toHaveBeenCalledTimes(1);
|
||||
expect(addLabelsMock).toHaveBeenCalledWith({
|
||||
expect(setLabelsMock).toHaveBeenCalledTimes(1);
|
||||
expect(setLabelsMock).toHaveBeenCalledWith({
|
||||
owner: 'monalisa',
|
||||
repo: 'helloworld',
|
||||
issue_number: 123,
|
||||
@@ -75,11 +83,15 @@ describe('run', () => {
|
||||
configureInput({});
|
||||
usingLabelerConfigYaml('only_pdfs.yml');
|
||||
mockGitHubResponseChangedFiles('.foo.pdf');
|
||||
getPullMock.mockResolvedValue(<any>{
|
||||
data: {
|
||||
labels: []
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(removeLabelMock).toHaveBeenCalledTimes(0);
|
||||
expect(addLabelsMock).toHaveBeenCalledTimes(0);
|
||||
expect(setLabelsMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('(with dot: true) does not add labels to PRs that do not match our glob patterns', async () => {
|
||||
@@ -89,8 +101,7 @@ describe('run', () => {
|
||||
|
||||
await run();
|
||||
|
||||
expect(removeLabelMock).toHaveBeenCalledTimes(0);
|
||||
expect(addLabelsMock).toHaveBeenCalledTimes(0);
|
||||
expect(setLabelsMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('(with sync-labels: true) it deletes preexisting PR labels that no longer match the glob pattern', async () => {
|
||||
@@ -104,19 +115,18 @@ describe('run', () => {
|
||||
mockGitHubResponseChangedFiles('foo.txt');
|
||||
getPullMock.mockResolvedValue(<any>{
|
||||
data: {
|
||||
labels: [{name: 'touched-a-pdf-file'}]
|
||||
labels: [{name: 'touched-a-pdf-file'}, {name: 'manually-added'}]
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(addLabelsMock).toHaveBeenCalledTimes(0);
|
||||
expect(removeLabelMock).toHaveBeenCalledTimes(1);
|
||||
expect(removeLabelMock).toHaveBeenCalledWith({
|
||||
expect(setLabelsMock).toHaveBeenCalledTimes(1);
|
||||
expect(setLabelsMock).toHaveBeenCalledWith({
|
||||
owner: 'monalisa',
|
||||
repo: 'helloworld',
|
||||
issue_number: 123,
|
||||
name: 'touched-a-pdf-file'
|
||||
labels: ['manually-added']
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,14 +141,43 @@ describe('run', () => {
|
||||
mockGitHubResponseChangedFiles('foo.txt');
|
||||
getPullMock.mockResolvedValue(<any>{
|
||||
data: {
|
||||
labels: [{name: 'touched-a-pdf-file'}]
|
||||
labels: [{name: 'touched-a-pdf-file'}, {name: 'manually-added'}]
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(addLabelsMock).toHaveBeenCalledTimes(0);
|
||||
expect(removeLabelMock).toHaveBeenCalledTimes(0);
|
||||
expect(setLabelsMock).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('(with sync-labels: false) it only logs the excess labels', async () => {
|
||||
configureInput({
|
||||
'repo-token': 'foo',
|
||||
'configuration-path': 'bar',
|
||||
'sync-labels': false
|
||||
});
|
||||
|
||||
usingLabelerConfigYaml('only_pdfs.yml');
|
||||
mockGitHubResponseChangedFiles('foo.pdf');
|
||||
|
||||
const existingLabels = Array.from({length: 100}).map((_, idx) => ({
|
||||
name: `existing-label-${idx}`
|
||||
}));
|
||||
getPullMock.mockResolvedValue(<any>{
|
||||
data: {
|
||||
labels: existingLabels
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(setLabelsMock).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(coreWarningMock).toHaveBeenCalledTimes(1);
|
||||
expect(coreWarningMock).toHaveBeenCalledWith(
|
||||
'Maximum of 100 labels allowed. Excess labels: touched-a-pdf-file',
|
||||
{title: 'Label limit for a PR exceeded'}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
1693
dist/index.js
vendored
1693
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
67
package-lock.json
generated
67
package-lock.json
generated
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "labeler",
|
||||
"version": "4.0.1",
|
||||
"version": "4.1.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "labeler",
|
||||
"version": "4.0.1",
|
||||
"version": "4.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/github": "^5.1.1",
|
||||
"@octokit/plugin-retry": "^5.0.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"minimatch": "^7.4.3"
|
||||
},
|
||||
@@ -1223,6 +1224,34 @@
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-retry": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-5.0.2.tgz",
|
||||
"integrity": "sha512-/Z7rWLCfjwmaVdyFuMkZoAnhfrvYgtvDrbO2d6lv7XrvJa8gFGB5tLUMngfuyMBfDCc5B9+EVu7IkQx5ebVlMg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^9.0.0",
|
||||
"bottleneck": "^2.15.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": {
|
||||
"version": "17.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.2.0.tgz",
|
||||
"integrity": "sha512-MazrFNx4plbLsGl+LFesMo96eIXkFgEtaKbnNpdh4aQ0VM10aoylFsTYP1AEjkeoRNZiiPe3T6Gl2Hr8dJWdlQ=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-retry/node_modules/@octokit/types": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.3.tgz",
|
||||
"integrity": "sha512-MMeLdHyFIALioycq+LFcA71v0S2xpQUX2cw6pPbHQjaibcHYwLnmK/kMZaWuGfGfjBJZ3wRUq+dOaWsvrPJVvA==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^17.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
@@ -1978,6 +2007,11 @@
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"node_modules/bottleneck": {
|
||||
"version": "2.19.5",
|
||||
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
|
||||
"integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
@@ -6799,6 +6833,30 @@
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-retry": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-5.0.2.tgz",
|
||||
"integrity": "sha512-/Z7rWLCfjwmaVdyFuMkZoAnhfrvYgtvDrbO2d6lv7XrvJa8gFGB5tLUMngfuyMBfDCc5B9+EVu7IkQx5ebVlMg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^9.0.0",
|
||||
"bottleneck": "^2.15.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": {
|
||||
"version": "17.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.2.0.tgz",
|
||||
"integrity": "sha512-MazrFNx4plbLsGl+LFesMo96eIXkFgEtaKbnNpdh4aQ0VM10aoylFsTYP1AEjkeoRNZiiPe3T6Gl2Hr8dJWdlQ=="
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "9.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.3.tgz",
|
||||
"integrity": "sha512-MMeLdHyFIALioycq+LFcA71v0S2xpQUX2cw6pPbHQjaibcHYwLnmK/kMZaWuGfGfjBJZ3wRUq+dOaWsvrPJVvA==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^17.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
@@ -7374,6 +7432,11 @@
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"bottleneck": {
|
||||
"version": "2.19.5",
|
||||
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
|
||||
"integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/github": "^5.1.1",
|
||||
"@octokit/plugin-retry": "^5.0.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"minimatch": "^7.4.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as github from '@actions/github';
|
||||
import * as pluginRetry from '@octokit/plugin-retry';
|
||||
import * as yaml from 'js-yaml';
|
||||
import {Minimatch} from 'minimatch';
|
||||
|
||||
@@ -11,6 +12,9 @@ interface MatchConfig {
|
||||
type StringOrMatchConfig = string | MatchConfig;
|
||||
type ClientType = ReturnType<typeof github.getOctokit>;
|
||||
|
||||
// GitHub Issues cannot have more than 100 labels
|
||||
const GITHUB_MAX_LABELS = 100;
|
||||
|
||||
export async function run() {
|
||||
try {
|
||||
const token = core.getInput('repo-token');
|
||||
@@ -24,7 +28,7 @@ export async function run() {
|
||||
return;
|
||||
}
|
||||
|
||||
const client: ClientType = github.getOctokit(token);
|
||||
const client: ClientType = github.getOctokit(token, {}, pluginRetry.retry);
|
||||
|
||||
const {data: pullRequest} = await client.rest.pulls.get({
|
||||
owner: github.context.repo.owner,
|
||||
@@ -39,24 +43,33 @@ export async function run() {
|
||||
configPath
|
||||
);
|
||||
|
||||
const labels: string[] = [];
|
||||
const labelsToRemove: string[] = [];
|
||||
const prLabels: string[] = pullRequest.labels.map(label => label.name);
|
||||
const allLabels: Set<string> = new Set(prLabels);
|
||||
|
||||
for (const [label, globs] of labelGlobs.entries()) {
|
||||
core.debug(`processing ${label}`);
|
||||
if (checkGlobs(changedFiles, globs, dot)) {
|
||||
labels.push(label);
|
||||
} else if (pullRequest.labels.find(l => l.name === label)) {
|
||||
labelsToRemove.push(label);
|
||||
allLabels.add(label);
|
||||
} else if (syncLabels) {
|
||||
allLabels.delete(label);
|
||||
}
|
||||
}
|
||||
|
||||
const labels = [...allLabels].slice(0, GITHUB_MAX_LABELS);
|
||||
const excessLabels = [...allLabels].slice(GITHUB_MAX_LABELS);
|
||||
|
||||
try {
|
||||
if (labels.length > 0) {
|
||||
await addLabels(client, prNumber, labels);
|
||||
if (!isListEqual(prLabels, labels)) {
|
||||
await setLabels(client, prNumber, labels);
|
||||
}
|
||||
|
||||
if (syncLabels && labelsToRemove.length) {
|
||||
await removeLabels(client, prNumber, labelsToRemove);
|
||||
if (excessLabels.length) {
|
||||
core.warning(
|
||||
`Maximum of ${GITHUB_MAX_LABELS} labels allowed. Excess labels: ${excessLabels.join(
|
||||
', '
|
||||
)}`,
|
||||
{title: 'Label limit for a PR exceeded'}
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (
|
||||
@@ -260,32 +273,19 @@ function checkMatch(
|
||||
return true;
|
||||
}
|
||||
|
||||
async function addLabels(
|
||||
function isListEqual(listA: string[], listB: string[]): boolean {
|
||||
return listA.length === listB.length && listA.every(el => listB.includes(el));
|
||||
}
|
||||
|
||||
async function setLabels(
|
||||
client: ClientType,
|
||||
prNumber: number,
|
||||
labels: string[]
|
||||
) {
|
||||
await client.rest.issues.addLabels({
|
||||
await client.rest.issues.setLabels({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: labels
|
||||
});
|
||||
}
|
||||
|
||||
async function removeLabels(
|
||||
client: ClientType,
|
||||
prNumber: number,
|
||||
labels: string[]
|
||||
) {
|
||||
await Promise.all(
|
||||
labels.map(label =>
|
||||
client.rest.issues.removeLabel({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
name: label
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user