feat: migrate to ESM and update dependencies (#949)

* feat: migrate to ESM and update dependencies

- Migrated the project to ESM to support the latest `@actions/*` package versions.
- Updated dependencies to their latest versions, including `@actions/core`, `@actions/github`, and others.
- Replaced `.prettierrc.js` with `.prettierrc.json` for configuration.
- Updated ESLint configuration to use ESM and added new rules.
- Refactored test files to use `jest` with ESM imports.
- Removed deprecated files and updated import paths to use `.js` extensions.
- Added new files for improved configuration and removed unnecessary mocks.
- Updated README to reflect breaking changes in version 7.

* docs: update README to clarify ESM migration details and adjust TypeScript configuration
This commit is contained in:
Chiranjib Swain
2026-07-16 11:04:03 -05:00
committed by GitHub
parent b8dd2d9be0
commit bf12e9b00b
60 changed files with 15081 additions and 16281 deletions
-7
View File
@@ -1,7 +0,0 @@
# Ignore list
/*
# Do not ignore these folders:
!__tests__/
!__mocks__/
!src/
-51
View File
@@ -1,51 +0,0 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:eslint-plugin-jest/recommended',
'eslint-config-prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'],
rules: {
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'no-console': 'error',
'yoda': 'error',
'prefer-const': [
'error',
{
destructuring: 'all'
}
],
'no-control-regex': 'off',
'no-constant-condition': ['error', {checkLoops: false}],
'node/no-extraneous-import': 'error'
},
overrides: [
{
files: ['**/*{test,spec}.ts'],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'jest/no-standalone-expect': 'off',
'jest/no-conditional-expect': 'off',
'no-console': 'off',
}
}
],
env: {
node: true,
es6: true,
'jest/globals': true
}
};
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@actions/core" name: "@actions/core"
version: 2.0.3 version: 3.0.1
type: npm type: npm
summary: Actions core lib summary: Actions core lib
homepage: https://github.com/actions/toolkit/tree/main/packages/core homepage: https://github.com/actions/toolkit/tree/main/packages/core
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@actions/exec" name: "@actions/exec"
version: 2.0.0 version: 3.0.0
type: npm type: npm
summary: Actions exec lib summary: Actions exec lib
homepage: https://github.com/actions/toolkit/tree/main/packages/exec homepage: https://github.com/actions/toolkit/tree/main/packages/exec
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@actions/github" name: "@actions/github"
version: 6.0.1 version: 9.1.1
type: npm type: npm
summary: Actions github lib summary: Actions github lib
homepage: https://github.com/actions/toolkit/tree/main/packages/github homepage: https://github.com/actions/toolkit/tree/main/packages/github
@@ -1,6 +1,6 @@
--- ---
name: "@actions/http-client" name: "@actions/http-client"
version: 2.2.3 version: 4.0.1
type: npm type: npm
summary: Actions Http Client summary: Actions Http Client
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client homepage: https://github.com/actions/toolkit/tree/main/packages/http-client
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@actions/io" name: "@actions/io"
version: 2.0.0 version: 3.0.2
type: npm type: npm
summary: Actions io lib summary: Actions io lib
homepage: https://github.com/actions/toolkit/tree/main/packages/io homepage: https://github.com/actions/toolkit/tree/main/packages/io
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/auth-token" name: "@octokit/auth-token"
version: 4.0.0 version: 6.0.0
type: npm type: npm
summary: GitHub API token authentication for browsers and Node.js summary: GitHub API token authentication for browsers and Node.js
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/core" name: "@octokit/core"
version: 5.0.1 version: 7.0.6
type: npm type: npm
summary: Extendable client for GitHub's REST & GraphQL APIs summary: Extendable client for GitHub's REST & GraphQL APIs
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/endpoint" name: "@octokit/endpoint"
version: 9.0.6 version: 11.0.3
type: npm type: npm
summary: Turns REST API endpoints into generic request options summary: Turns REST API endpoints into generic request options
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/graphql" name: "@octokit/graphql"
version: 7.0.2 version: 9.0.3
type: npm type: npm
summary: GitHub GraphQL API client for browsers and Node summary: GitHub GraphQL API client for browsers and Node
homepage: homepage:
-20
View File
@@ -1,20 +0,0 @@
---
name: "@octokit/openapi-types"
version: 19.0.1
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: []
-20
View File
@@ -1,20 +0,0 @@
---
name: "@octokit/openapi-types"
version: 23.0.1
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: []
-20
View File
@@ -1,20 +0,0 @@
---
name: "@octokit/openapi-types"
version: 24.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: []
@@ -1,14 +1,14 @@
--- ---
name: "@octokit/openapi-types" name: "@octokit/openapi-types"
version: 20.0.0 version: 27.0.0
type: npm type: npm
summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com
homepage: homepage:
license: mit license: mit
licenses: licenses:
- sources: LICENSE - sources: LICENSE
text: |- text: |
Copyright 2020 Gregor Martynus Copyright (c) GitHub 2025 - Licensed as MIT.
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: 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:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/plugin-paginate-rest" name: "@octokit/plugin-paginate-rest"
version: 9.2.2 version: 14.0.0
type: npm type: npm
summary: Octokit plugin to paginate REST API endpoint responses summary: Octokit plugin to paginate REST API endpoint responses
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/plugin-rest-endpoint-methods" name: "@octokit/plugin-rest-endpoint-methods"
version: 10.4.1 version: 17.0.0
type: npm type: npm
summary: Octokit plugin adding one method for all of api.github.com REST API endpoints summary: Octokit plugin adding one method for all of api.github.com REST API endpoints
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/plugin-retry" name: "@octokit/plugin-retry"
version: 6.0.1 version: 8.1.0
type: npm type: npm
summary: Automatic retry plugin for octokit summary: Automatic retry plugin for octokit
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/request-error" name: "@octokit/request-error"
version: 5.1.1 version: 7.1.0
type: npm type: npm
summary: Error class for Octokit request errors summary: Error class for Octokit request errors
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/request" name: "@octokit/request"
version: 8.4.1 version: 10.0.10
type: npm type: npm
summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers
and Node and Node
-20
View File
@@ -1,20 +0,0 @@
---
name: "@octokit/types"
version: 12.6.0
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: []
-20
View File
@@ -1,20 +0,0 @@
---
name: "@octokit/types"
version: 13.10.0
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: []
-20
View File
@@ -1,20 +0,0 @@
---
name: "@octokit/types"
version: 13.8.0
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: []
@@ -1,6 +1,6 @@
--- ---
name: "@octokit/types" name: "@octokit/types"
version: 12.1.0 version: 16.0.0
type: npm type: npm
summary: Shared TypeScript definitions for Octokit projects summary: Shared TypeScript definitions for Octokit projects
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: before-after-hook name: before-after-hook
version: 2.2.3 version: 4.0.0
type: npm type: npm
summary: asynchronous before/error/after hooks for internal functionality summary: asynchronous before/error/after hooks for internal functionality
homepage: homepage:
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: brace-expansion name: brace-expansion
version: 5.0.7 version: 5.0.6
type: npm type: npm
summary: Brace expansion as known from sh/bash summary: Brace expansion as known from sh/bash
homepage: homepage:
+47
View File
@@ -0,0 +1,47 @@
---
name: content-type
version: 2.0.0
type: npm
summary: Create and parse HTTP Content-Type header
homepage:
license: mit
licenses:
- sources: LICENSE
text: |
(The MIT License)
Copyright (c) 2015 Douglas Christopher Wilson
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)
[npm-image]: https://img.shields.io/npm/v/content-type
[npm-url]: https://npmjs.org/package/content-type
[downloads-image]: https://img.shields.io/npm/dm/content-type
[downloads-url]: https://npmjs.org/package/content-type
[build-image]: https://img.shields.io/github/actions/workflow/status/jshttp/content-type/ci.yml?branch=master
[build-url]: https://github.com/jshttp/content-type/actions/workflows/ci.yml?query=branch%3Amaster
[coverage-image]: https://img.shields.io/codecov/c/gh/jshttp/content-type
[coverage-url]: https://codecov.io/gh/jshttp/content-type
[license-image]: http://img.shields.io/npm/l/content-type.svg?style=flat
[license-url]: LICENSE
notices: []
-28
View File
@@ -1,28 +0,0 @@
---
name: deprecation
version: 2.3.1
type: npm
summary: Log a deprecation message with stack
homepage:
license: isc
licenses:
- sources: LICENSE
text: |
The ISC License
Copyright (c) Gregor Martynus and contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- sources: README.md
text: "[ISC](LICENSE)"
notices: []
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: js-yaml name: js-yaml
version: 4.2.0 version: 5.1.0
type: npm type: npm
summary: YAML 1.2 parser and serializer summary: YAML 1.2 parser and serializer
homepage: homepage:
+33
View File
@@ -0,0 +1,33 @@
---
name: json-with-bigint
version: 3.5.8
type: npm
summary: JS library that allows you to easily serialize and deserialize data with
BigInt values
homepage:
license: mit
licenses:
- sources: LICENSE
text: |
MIT License
Copyright (c) 2023 Ivan Korolenko
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: []
-26
View File
@@ -1,26 +0,0 @@
---
name: once
version: 1.4.0
type: npm
summary: Run a function exactly one time
homepage:
license: isc
licenses:
- sources: LICENSE
text: |
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
notices: []
+3 -3
View File
@@ -1,8 +1,8 @@
--- ---
name: universal-user-agent name: universal-user-agent
version: 6.0.0 version: 7.0.3
type: npm type: npm
summary: Get a user agent string in both browser and node summary: Get a user agent string across all JavaScript Runtime Environments
homepage: homepage:
license: isc license: isc
licenses: licenses:
@@ -10,7 +10,7 @@ licenses:
text: | text: |
# [ISC License](https://spdx.org/licenses/ISC) # [ISC License](https://spdx.org/licenses/ISC)
Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m) Copyright (c) 2018-2021, Gregor Martynus (https://github.com/gr2m)
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
-26
View File
@@ -1,26 +0,0 @@
---
name: wrappy
version: 1.0.2
type: npm
summary: Callback wrapping utility
homepage: https://github.com/npm/wrappy
license: isc
licenses:
- sources: LICENSE
text: |
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
notices: []
-11
View File
@@ -1,11 +0,0 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
module.exports = {
printWidth: 80,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
trailingComma: 'none',
bracketSpacing: false,
arrowParens: 'avoid'
};
+10
View File
@@ -0,0 +1,10 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid"
}
+4
View File
@@ -4,6 +4,10 @@
Automatically label new pull requests based on the paths of files being changed or the branch name. Automatically label new pull requests based on the paths of files being changed or the branch name.
## What's changed in V7
- **Migrated to ESM** internally to support the latest `@actions/*` package versions. No changes to action inputs, outputs, or behavior.
## Breaking changes in V6 ## Breaking changes in V6
- Upgraded action from node20 to node24. - Upgraded action from node20 to node24.
-43
View File
@@ -1,43 +0,0 @@
export const context = {
payload: {
pull_request: {
number: 123,
head: {
ref: 'head-branch-name'
},
base: {
ref: 'base-branch-name'
}
}
},
repo: {
owner: 'monalisa',
repo: 'helloworld'
}
};
const mockApi = {
rest: {
issues: {
setLabels: jest.fn()
},
pulls: {
get: jest.fn().mockResolvedValue({
data: {
labels: []
}
}),
listFiles: {
endpoint: {
merge: jest.fn().mockReturnValue({})
}
}
},
repos: {
getContent: jest.fn()
}
},
paginate: jest.fn()
};
export const getOctokit = jest.fn().mockImplementation(() => mockApi);
+36 -19
View File
@@ -1,14 +1,31 @@
import { import {jest, describe, beforeEach, it, expect} from '@jest/globals';
getBranchName, import type {BranchMatchConfig} from '../src/branch.js';
checkAnyBranch,
checkAllBranch,
toBranchMatchConfig,
BranchMatchConfig
} from '../src/branch';
import * as github from '@actions/github';
jest.mock('@actions/core'); jest.unstable_mockModule('@actions/core', () => ({
jest.mock('@actions/github'); debug: jest.fn(),
info: jest.fn(),
warning: jest.fn(),
error: jest.fn()
}));
const mockGithubContext = {
payload: {
pull_request: {
number: 123,
head: {ref: 'head-branch-name'},
base: {ref: 'base-branch-name'}
}
},
repo: {owner: 'monalisa', repo: 'helloworld'}
} as any;
jest.unstable_mockModule('@actions/github', () => ({
context: mockGithubContext,
getOctokit: jest.fn()
}));
const {getBranchName, checkAnyBranch, checkAllBranch, toBranchMatchConfig} =
await import('../src/branch.js');
describe('getBranchName', () => { describe('getBranchName', () => {
describe('when the pull requests base branch is requested', () => { describe('when the pull requests base branch is requested', () => {
@@ -28,10 +45,10 @@ describe('getBranchName', () => {
describe('checkAllBranch', () => { describe('checkAllBranch', () => {
beforeEach(() => { beforeEach(() => {
github.context.payload.pull_request!.head = { mockGithubContext.payload.pull_request.head = {
ref: 'test/feature/123' ref: 'test/feature/123'
}; };
github.context.payload.pull_request!.base = { mockGithubContext.payload.pull_request.base = {
ref: 'main' ref: 'main'
}; };
}); });
@@ -87,10 +104,10 @@ describe('checkAllBranch', () => {
describe('checkAnyBranch', () => { describe('checkAnyBranch', () => {
beforeEach(() => { beforeEach(() => {
github.context.payload.pull_request!.head = { mockGithubContext.payload.pull_request.head = {
ref: 'test/feature/123' ref: 'test/feature/123'
}; };
github.context.payload.pull_request!.base = { mockGithubContext.payload.pull_request.base = {
ref: 'main' ref: 'main'
}; };
}); });
@@ -159,7 +176,7 @@ describe('toBranchMatchConfig', () => {
it('sets headBranch in the matchConfig', () => { it('sets headBranch in the matchConfig', () => {
const result = toBranchMatchConfig(config); const result = toBranchMatchConfig(config);
expect(result).toEqual<BranchMatchConfig>({ expect(result).toEqual({
headBranch: ['testing'] headBranch: ['testing']
}); });
}); });
@@ -169,7 +186,7 @@ describe('toBranchMatchConfig', () => {
it('sets headBranch in the matchConfig', () => { it('sets headBranch in the matchConfig', () => {
const result = toBranchMatchConfig(stringConfig); const result = toBranchMatchConfig(stringConfig);
expect(result).toEqual<BranchMatchConfig>({ expect(result).toEqual({
headBranch: ['testing'] headBranch: ['testing']
}); });
}); });
@@ -180,7 +197,7 @@ describe('toBranchMatchConfig', () => {
const config = {'base-branch': ['testing']}; const config = {'base-branch': ['testing']};
it('sets baseBranch in the matchConfig', () => { it('sets baseBranch in the matchConfig', () => {
const result = toBranchMatchConfig(config); const result = toBranchMatchConfig(config);
expect(result).toEqual<BranchMatchConfig>({ expect(result).toEqual({
baseBranch: ['testing'] baseBranch: ['testing']
}); });
}); });
@@ -190,7 +207,7 @@ describe('toBranchMatchConfig', () => {
it('sets baseBranch in the matchConfig', () => { it('sets baseBranch in the matchConfig', () => {
const result = toBranchMatchConfig(stringConfig); const result = toBranchMatchConfig(stringConfig);
expect(result).toEqual<BranchMatchConfig>({ expect(result).toEqual({
baseBranch: ['testing'] baseBranch: ['testing']
}); });
}); });
@@ -201,7 +218,7 @@ describe('toBranchMatchConfig', () => {
const config = {'base-branch': ['testing'], 'head-branch': ['testing']}; const config = {'base-branch': ['testing'], 'head-branch': ['testing']};
it('sets headBranch and baseBranch in the matchConfig', () => { it('sets headBranch and baseBranch in the matchConfig', () => {
const result = toBranchMatchConfig(config); const result = toBranchMatchConfig(config);
expect(result).toEqual<BranchMatchConfig>({ expect(result).toEqual({
baseBranch: ['testing'], baseBranch: ['testing'],
headBranch: ['testing'] headBranch: ['testing']
}); });
+25 -9
View File
@@ -1,5 +1,24 @@
import { import {jest, describe, it, expect} from '@jest/globals';
ChangedFilesMatchConfig, import type {ChangedFilesMatchConfig} from '../src/changedFiles.js';
jest.unstable_mockModule('@actions/core', () => ({
debug: jest.fn(),
info: jest.fn(),
warning: jest.fn(),
error: jest.fn()
}));
jest.unstable_mockModule('@actions/github', () => ({
context: {
payload: {
pull_request: {number: 123, head: {ref: 'head'}, base: {ref: 'base'}}
},
repo: {owner: 'monalisa', repo: 'helloworld'}
},
getOctokit: jest.fn()
}));
const {
checkAllChangedFiles, checkAllChangedFiles,
checkAnyChangedFiles, checkAnyChangedFiles,
toChangedFilesMatchConfig, toChangedFilesMatchConfig,
@@ -7,10 +26,7 @@ import {
checkIfAllGlobsMatchAnyFile, checkIfAllGlobsMatchAnyFile,
checkIfAnyGlobMatchesAllFiles, checkIfAnyGlobMatchesAllFiles,
checkIfAllGlobsMatchAllFiles checkIfAllGlobsMatchAllFiles
} from '../src/changedFiles'; } = await import('../src/changedFiles.js');
jest.mock('@actions/core');
jest.mock('@actions/github');
describe('checkAllChangedFiles', () => { describe('checkAllChangedFiles', () => {
const changedFiles = ['foo.txt', 'bar.txt']; const changedFiles = ['foo.txt', 'bar.txt'];
@@ -92,7 +108,7 @@ describe('toChangedFilesMatchConfig', () => {
it('returns an empty object', () => { it('returns an empty object', () => {
const result = toChangedFilesMatchConfig(config); const result = toChangedFilesMatchConfig(config);
expect(result).toEqual<ChangedFilesMatchConfig>({}); expect(result).toEqual({});
}); });
}); });
@@ -129,7 +145,7 @@ describe('toChangedFilesMatchConfig', () => {
it('sets the value in the config object', () => { it('sets the value in the config object', () => {
const result = toChangedFilesMatchConfig(config); const result = toChangedFilesMatchConfig(config);
expect(result).toEqual<ChangedFilesMatchConfig>({ expect(result).toEqual({
changedFiles: [{anyGlobToAnyFile: ['testing']}] changedFiles: [{anyGlobToAnyFile: ['testing']}]
}); });
}); });
@@ -140,7 +156,7 @@ describe('toChangedFilesMatchConfig', () => {
it(`sets the string as an array in the config object`, () => { it(`sets the string as an array in the config object`, () => {
const result = toChangedFilesMatchConfig(config); const result = toChangedFilesMatchConfig(config);
expect(result).toEqual<ChangedFilesMatchConfig>({ expect(result).toEqual({
changedFiles: [{anyGlobToAnyFile: ['testing']}] changedFiles: [{anyGlobToAnyFile: ['testing']}]
}); });
}); });
+58 -19
View File
@@ -1,26 +1,65 @@
import {jest, describe, it, expect, beforeEach, beforeAll} from '@jest/globals';
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
import * as core from '@actions/core';
import * as api from '../src/api';
import {labeler} from '../src/labeler';
import * as github from '@actions/github';
import * as fs from 'fs'; import * as fs from 'fs';
import {checkMatchConfigs} from '../src/labeler'; import type {
import {
MatchConfig, MatchConfig,
BaseMatchConfig
} from '../src/api/get-label-configs.js';
// Define API mock functions at module level
const getPullRequestsMock = jest.fn<any>();
const getLabelConfigsMock = jest.fn<any>();
const setLabelsMock = jest.fn<any>();
const getChangedFilesMock = jest.fn<any>();
const getContentMock = jest.fn<any>();
jest.unstable_mockModule('@actions/core', () => ({
getInput: jest.fn(),
getMultilineInput: jest.fn(),
getBooleanInput: jest.fn(),
setOutput: jest.fn(),
setFailed: jest.fn(),
error: jest.fn(),
warning: jest.fn(),
info: jest.fn(),
debug: jest.fn()
}));
jest.unstable_mockModule('@actions/github', () => ({
context: {
payload: {
pull_request: {
number: 123,
head: {ref: 'head-branch'},
base: {ref: 'base-branch'}
}
},
repo: {owner: 'monalisa', repo: 'helloworld'}
},
getOctokit: jest.fn()
}));
jest.unstable_mockModule('../src/api/index.js', () => ({
getPullRequests: getPullRequestsMock,
getLabelConfigs: getLabelConfigsMock,
setLabels: setLabelsMock,
getChangedFiles: getChangedFilesMock,
getContent: getContentMock
}));
const core = await import('@actions/core');
const github = await import('@actions/github');
const api = await import('../src/api/index.js');
const {labeler, checkMatchConfigs} = await import('../src/labeler.js');
const {
toMatchConfig, toMatchConfig,
getLabelConfigMapFromObject, getLabelConfigMapFromObject,
getLabelConfigResultFromObject, getLabelConfigResultFromObject,
BaseMatchConfig,
configUsesChangedFiles configUsesChangedFiles
} from '../src/api/get-label-configs'; } = await import('../src/api/get-label-configs.js');
jest.mock('@actions/core');
jest.mock('../src/api');
beforeAll(() => { beforeAll(() => {
jest.spyOn(core, 'getInput').mockImplementation((name, options) => { (core.getInput as jest.Mock).mockImplementation(() => undefined);
return jest.requireActual('@actions/core').getInput(name, options);
});
}); });
const loadYaml = (filepath: string) => { const loadYaml = (filepath: string) => {
@@ -422,14 +461,14 @@ describe('labeler error handling', () => {
jest.resetAllMocks(); jest.resetAllMocks();
(github.getOctokit as jest.Mock).mockReturnValue(mockClient); (github.getOctokit as jest.Mock).mockReturnValue(mockClient);
(api.getPullRequests as jest.Mock).mockReturnValue([ getPullRequestsMock.mockReturnValue([
{ {
...mockPullRequest, ...mockPullRequest,
data: {labels: [{name: 'old-label'}]} data: {labels: [{name: 'old-label'}]}
} }
]); ]);
(api.getLabelConfigs as jest.Mock).mockResolvedValue({ getLabelConfigsMock.mockResolvedValue({
labelConfigs: new Map([['new-label', ['dummy-config']]]), labelConfigs: new Map([['new-label', ['dummy-config']]]),
changedFilesLimit: undefined changedFilesLimit: undefined
}); });
@@ -439,7 +478,7 @@ describe('labeler error handling', () => {
}); });
it('throws a custom error for HttpError 403 with "unauthorized" message', async () => { it('throws a custom error for HttpError 403 with "unauthorized" message', async () => {
(api.setLabels as jest.Mock).mockRejectedValue({ setLabelsMock.mockRejectedValue({
name: 'HttpError', name: 'HttpError',
status: 403, status: 403,
message: 'Request failed with status code 403: Unauthorized' message: 'Request failed with status code 403: Unauthorized'
@@ -456,7 +495,7 @@ describe('labeler error handling', () => {
status: 404, status: 404,
message: 'Not Found' message: 'Not Found'
}; };
(api.setLabels as jest.Mock).mockRejectedValue(unexpectedError); setLabelsMock.mockRejectedValue(unexpectedError);
// NOTE: In the current implementation, labeler rethrows the raw error object (not an Error instance). // NOTE: In the current implementation, labeler rethrows the raw error object (not an Error instance).
// `rejects.toThrow` only works with real Error objects, so here we must use `rejects.toEqual`. // `rejects.toThrow` only works with real Error objects, so here we must use `rejects.toEqual`.
@@ -469,7 +508,7 @@ describe('labeler error handling', () => {
name: 'HttpError', name: 'HttpError',
message: 'Resource not accessible by integration' message: 'Resource not accessible by integration'
}; };
(api.setLabels as jest.Mock).mockRejectedValue(error); setLabelsMock.mockRejectedValue(error);
await labeler(); await labeler();
+101 -42
View File
@@ -1,23 +1,75 @@
import {run} from '../src/labeler'; import {
import * as github from '@actions/github'; jest,
import * as core from '@actions/core'; describe,
it,
test,
expect,
afterAll,
beforeEach
} from '@jest/globals';
import path from 'path'; import path from 'path';
import {fileURLToPath} from 'url';
import fs from 'fs'; import fs from 'fs';
jest.mock('@actions/core'); const __dirname = path.dirname(fileURLToPath(import.meta.url));
jest.mock('@actions/github');
// Define mock functions before mocking modules
const setLabelsMock = jest.fn<any>();
const reposMock = jest.fn<any>();
const paginateMock = jest.fn<any>();
const getPullMock = jest.fn<any>().mockResolvedValue({data: {labels: []}});
const listFilesMergeMock = jest.fn<any>().mockReturnValue({});
const coreGetInputMock = jest.fn<any>();
const coreGetMultilineInputMock = jest.fn<any>();
const coreGetBooleanInputMock = jest.fn<any>();
const coreErrorMock = jest.fn<any>();
const coreWarningMock = jest.fn<any>();
const coreSetFailedMock = jest.fn<any>();
const setOutputSpy = jest.fn<any>();
const mockGithubContext: any = {
payload: {
pull_request: {
number: 123,
head: {ref: 'head-branch-name'},
base: {ref: 'base-branch-name'}
}
},
repo: {owner: 'monalisa', repo: 'helloworld'}
};
jest.unstable_mockModule('@actions/core', () => ({
getInput: coreGetInputMock,
getMultilineInput: coreGetMultilineInputMock,
getBooleanInput: coreGetBooleanInputMock,
setOutput: setOutputSpy,
setFailed: coreSetFailedMock,
error: coreErrorMock,
warning: coreWarningMock,
info: jest.fn(),
debug: jest.fn()
}));
jest.unstable_mockModule('@actions/github', () => ({
context: mockGithubContext,
getOctokit: jest.fn(() => ({
rest: {
issues: {setLabels: setLabelsMock},
repos: {getContent: reposMock},
pulls: {
get: getPullMock,
listFiles: {endpoint: {merge: listFilesMergeMock}}
}
},
paginate: paginateMock
}))
}));
const {run} = await import('../src/labeler.js');
const gh = github.getOctokit('_');
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 readFileSyncMock = jest.spyOn(fs, 'readFileSync'); const readFileSyncMock = jest.spyOn(fs, 'readFileSync');
const existsSyncMock = jest.spyOn(fs, 'existsSync'); const existsSyncMock = jest.spyOn(fs, 'existsSync');
const coreErrorMock = jest.spyOn(core, 'error');
const coreWarningMock = jest.spyOn(core, 'warning');
const coreSetFailedMock = jest.spyOn(core, 'setFailed');
const setOutputSpy = jest.spyOn(core, 'setOutput');
class HttpError extends Error { class HttpError extends Error {
constructor(message: string) { constructor(message: string) {
@@ -59,20 +111,27 @@ const configureInput = (
'pr-number': string[]; 'pr-number': string[];
}> }>
) => { ) => {
jest coreGetInputMock.mockImplementation(
.spyOn(core, 'getInput') (name: unknown) => mockInput[name as keyof typeof mockInput]
.mockImplementation((name: string, ...opts) => mockInput[name]); );
jest coreGetMultilineInputMock.mockImplementation(
.spyOn(core, 'getMultilineInput') (name: unknown) => mockInput[name as keyof typeof mockInput]
.mockImplementation((name: string, ...opts) => mockInput[name]); );
jest coreGetBooleanInputMock.mockImplementation(
.spyOn(core, 'getBooleanInput') (name: unknown) => mockInput[name as keyof typeof mockInput]
.mockImplementation((name: string, ...opts) => mockInput[name]); );
}; };
afterAll(() => jest.restoreAllMocks()); afterAll(async () => {
jest.restoreAllMocks();
});
describe('run', () => { describe('run', () => {
beforeEach(() => {
getPullMock.mockResolvedValue({data: {labels: []}} as any);
mockGithubContext.payload.pull_request.head = {ref: 'head-branch-name'};
mockGithubContext.payload.pull_request.base = {ref: 'base-branch-name'};
});
it('(with dot: false) adds labels to PRs that match our glob patterns', async () => { it('(with dot: false) adds labels to PRs that match our glob patterns', async () => {
configureInput({}); configureInput({});
usingLabelerConfigYaml('only_pdfs.yml'); usingLabelerConfigYaml('only_pdfs.yml');
@@ -169,7 +228,7 @@ describe('run', () => {
it('adds labels based on the branch names that match the regexp pattern', async () => { it('adds labels based on the branch names that match the regexp pattern', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/testing-time'}; mockGithubContext.payload.pull_request.head = {ref: 'test/testing-time'};
usingLabelerConfigYaml('branches.yml'); usingLabelerConfigYaml('branches.yml');
await run(); await run();
@@ -187,7 +246,7 @@ describe('run', () => {
it('adds multiple labels based on branch names that match different regexp patterns', async () => { it('adds multiple labels based on branch names that match different regexp patterns', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = { mockGithubContext.payload.pull_request.head = {
ref: 'test/feature/123' ref: 'test/feature/123'
}; };
usingLabelerConfigYaml('branches.yml'); usingLabelerConfigYaml('branches.yml');
@@ -213,7 +272,7 @@ describe('run', () => {
it('can support multiple branches by batching', async () => { it('can support multiple branches by batching', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'fix/123'}; mockGithubContext.payload.pull_request.head = {ref: 'fix/123'};
usingLabelerConfigYaml('branches.yml'); usingLabelerConfigYaml('branches.yml');
await run(); await run();
@@ -231,7 +290,7 @@ describe('run', () => {
it('can support multiple branches by providing an array', async () => { it('can support multiple branches by providing an array', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'array/123'}; mockGithubContext.payload.pull_request.head = {ref: 'array/123'};
usingLabelerConfigYaml('branches.yml'); usingLabelerConfigYaml('branches.yml');
await run(); await run();
@@ -644,7 +703,7 @@ describe('run', () => {
describe('changed-files-labels-limit', () => { describe('changed-files-labels-limit', () => {
it('applies all labels when count is within limit', async () => { it('applies all labels when count is within limit', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('limit_3.yml'); usingLabelerConfigYaml('limit_3.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -667,7 +726,7 @@ describe('run', () => {
it('skips changed-files labels when count exceeds limit', async () => { it('skips changed-files labels when count exceeds limit', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml'); usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -686,7 +745,7 @@ describe('run', () => {
it('still applies branch-based labels when changed-files limit is exceeded', async () => { it('still applies branch-based labels when changed-files limit is exceeded', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'}; mockGithubContext.payload.pull_request.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('limit_1.yml'); usingLabelerConfigYaml('limit_1.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -711,7 +770,7 @@ describe('run', () => {
it('applies all labels when no limit is set', async () => { it('applies all labels when no limit is set', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('mixed_labels.yml'); usingLabelerConfigYaml('mixed_labels.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -736,7 +795,7 @@ describe('run', () => {
it('does not count preexisting labels toward the limit', async () => { it('does not count preexisting labels toward the limit', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml'); usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -763,7 +822,7 @@ describe('run', () => {
it('skips new labels when new count exceeds limit even with preexisting', async () => { it('skips new labels when new count exceeds limit even with preexisting', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml'); usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -784,7 +843,7 @@ describe('run', () => {
it('applies labels when new count equals the limit', async () => { it('applies labels when new count equals the limit', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('limit_2.yml'); usingLabelerConfigYaml('limit_2.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -807,7 +866,7 @@ describe('run', () => {
it('skips all changed-files labels when limit is 0', async () => { it('skips all changed-files labels when limit is 0', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'}; mockGithubContext.payload.pull_request.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('limit_0.yml'); usingLabelerConfigYaml('limit_0.yml');
mockGitHubResponseChangedFiles('components/a/file.ts'); mockGitHubResponseChangedFiles('components/a/file.ts');
getPullMock.mockResolvedValue(<any>{ getPullMock.mockResolvedValue(<any>{
@@ -831,7 +890,7 @@ describe('run', () => {
// a "changed-files label" and subject to the limit, even if it matches // a "changed-files label" and subject to the limit, even if it matches
// via the branch rule // via the branch rule
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'}; mockGithubContext.payload.pull_request.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('mixed_rules.yml'); usingLabelerConfigYaml('mixed_rules.yml');
mockGitHubResponseChangedFiles('unrelated/file.ts'); mockGitHubResponseChangedFiles('unrelated/file.ts');
getPullMock.mockResolvedValue(<any>{ getPullMock.mockResolvedValue(<any>{
@@ -856,7 +915,7 @@ describe('run', () => {
describe('max-files-changed', () => { describe('max-files-changed', () => {
it('applies labels when changed files count is within limit', async () => { it('applies labels when changed files count is within limit', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml'); usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file.ts', 'components/a/file.ts',
@@ -880,7 +939,7 @@ describe('run', () => {
it('skips file-based labels when changed files exceed limit', async () => { it('skips file-based labels when changed files exceed limit', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml'); usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file1.ts', 'components/a/file1.ts',
@@ -902,7 +961,7 @@ describe('run', () => {
it('applies labels when changed files count equals limit', async () => { it('applies labels when changed files count equals limit', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml'); usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file1.ts', 'components/a/file1.ts',
@@ -928,7 +987,7 @@ describe('run', () => {
it('still applies branch-based labels when max-files-changed is exceeded', async () => { it('still applies branch-based labels when max-files-changed is exceeded', async () => {
configureInput({}); configureInput({});
github.context.payload.pull_request!.head = {ref: 'test/some-feature'}; mockGithubContext.payload.pull_request.head = {ref: 'test/some-feature'};
usingLabelerConfigYaml('max_files_with_branch.yml'); usingLabelerConfigYaml('max_files_with_branch.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'components/a/file1.ts', 'components/a/file1.ts',
@@ -954,7 +1013,7 @@ describe('run', () => {
it('preserves preexisting changed-files labels with sync-labels when max-files-changed is exceeded', async () => { it('preserves preexisting changed-files labels with sync-labels when max-files-changed is exceeded', async () => {
configureInput({'sync-labels': true}); configureInput({'sync-labels': true});
github.context.payload.pull_request!.head = {ref: 'main'}; mockGithubContext.payload.pull_request.head = {ref: 'main'};
usingLabelerConfigYaml('max_files_5.yml'); usingLabelerConfigYaml('max_files_5.yml');
mockGitHubResponseChangedFiles( mockGitHubResponseChangedFiles(
'unrelated/file1.ts', 'unrelated/file1.ts',
+11953 -14022
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
{
"type": "module"
}
+74
View File
@@ -0,0 +1,74 @@
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import jest from 'eslint-plugin-jest';
import n from 'eslint-plugin-n';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
export default [
{
ignores: ['**/*', '!src/**', '!__tests__/**']
},
js.configs.recommended,
{
files: ['**/*.ts'],
languageOptions: {
parser: tsParser,
ecmaVersion: 2022,
sourceType: 'module',
globals: {
...globals.node,
...globals.es2015
}
},
plugins: {
'@typescript-eslint': tsPlugin,
n
},
rules: {
...tsPlugin.configs.recommended.rules,
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'no-console': 'error',
yoda: 'error',
'prefer-const': [
'error',
{
destructuring: 'all'
}
],
'no-control-regex': 'off',
'no-constant-condition': ['error', {checkLoops: false}],
'no-undef': 'off',
'no-useless-assignment': 'off',
'n/no-extraneous-import': 'error'
}
},
{
files: ['**/*{test,spec}.ts'],
plugins: {jest},
languageOptions: {
globals: {
...globals.jest
}
},
rules: {
...jest.configs['flat/recommended'].rules,
'@typescript-eslint/no-unused-vars': 'off',
'jest/no-standalone-expect': 'off',
'jest/no-conditional-expect': 'off',
'no-console': 'off'
}
},
prettier
];
-10
View File
@@ -1,10 +0,0 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}
+23
View File
@@ -0,0 +1,23 @@
export default {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.ts$': [
'ts-jest',
{
useESM: true,
diagnostics: {
ignoreCodes: [151002]
}
}
]
},
extensionsToTreatAsEsm: ['.ts'],
transformIgnorePatterns: ['node_modules/(?!(@actions|@octokit)/)'],
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1'
},
verbose: true
}
+2621 -1754
View File
File diff suppressed because it is too large Load Diff
+26 -28
View File
@@ -1,18 +1,19 @@
{ {
"name": "labeler", "name": "labeler",
"version": "6.2.0", "version": "7.0.0",
"description": "Labels pull requests by files altered", "description": "Labels pull requests by files altered",
"type": "module",
"main": "lib/main.js", "main": "lib/main.js",
"engines": { "engines": {
"node": ">=24" "node": ">=24"
}, },
"scripts": { "scripts": {
"build": "tsc && ncc build lib/main.js", "build": "tsc && ncc build lib/main.js",
"format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"", "format": "prettier --no-error-on-unmatched-pattern --write \"**/*.{ts,yml,yaml}\"",
"format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"", "format-check": "prettier --no-error-on-unmatched-pattern --check \"**/*.{ts,yml,yaml}\"",
"lint": "eslint --config ./.eslintrc.js \"**/*.ts\"", "lint": "eslint \"**/*.ts\"",
"lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix", "lint:fix": "eslint \"**/*.ts\" --fix",
"test": "jest" "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -27,32 +28,29 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^2.0.3", "@actions/core": "^3.0.1",
"@actions/github": "^6.0.1", "@actions/github": "^9.1.1",
"@octokit/plugin-retry": "^6.0.0", "@octokit/plugin-retry": "^8.1.0",
"js-yaml": "^4.2.0", "js-yaml": "^5.1.0",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"minimatch": "^10.2.5" "minimatch": "^10.2.5"
}, },
"overrides": {
"undici": "6.27.0"
},
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.14", "@eslint/js": "^10.0.1",
"@types/js-yaml": "^4.0.9", "@jest/globals": "^30.4.1",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/minimatch": "^5.1.2", "@types/node": "^24.13.2",
"@types/node": "^24.1.0", "@typescript-eslint/eslint-plugin": "^8.62.0",
"@typescript-eslint/eslint-plugin": "^8.61.1", "@typescript-eslint/parser": "^8.62.0",
"@typescript-eslint/parser": "^8.59.0", "@vercel/ncc": "^0.44.0",
"@vercel/ncc": "^0.38.3", "eslint": "^10.5.0",
"eslint": "^8.57.0", "eslint-config-prettier": "^10.1.8",
"eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^29.15.2",
"eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^18.1.0",
"eslint-plugin-node": "^11.1.0", "globals": "^17.7.0",
"jest": "^29.7.0", "jest": "^30.4.2",
"prettier": "^3.4.2", "prettier": "^3.8.4",
"ts-jest": "^29.2.5", "ts-jest": "^29.4.11",
"typescript": "^5.7.3" "typescript": "^6.0.3"
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import {ClientType} from './types'; import {ClientType} from './types.js';
export const getChangedFiles = async ( export const getChangedFiles = async (
client: ClientType, client: ClientType,
+2 -2
View File
@@ -1,7 +1,7 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import {getChangedFiles} from './get-changed-files'; import {getChangedFiles} from './get-changed-files.js';
import {ClientType} from './types'; import {ClientType} from './types.js';
export async function* getPullRequests( export async function* getPullRequests(
client: ClientType, client: ClientType,
+1 -1
View File
@@ -1,5 +1,5 @@
import * as github from '@actions/github'; import * as github from '@actions/github';
import {ClientType} from './types'; import {ClientType} from './types.js';
export const getContent = async ( export const getContent = async (
client: ClientType, client: ClientType,
+4 -4
View File
@@ -1,15 +1,15 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
import fs from 'fs'; import fs from 'fs';
import {ClientType} from './types'; import {ClientType} from './types.js';
import {getContent} from './get-content'; import {getContent} from './get-content.js';
import { import {
ChangedFilesMatchConfig, ChangedFilesMatchConfig,
toChangedFilesMatchConfig toChangedFilesMatchConfig
} from '../changedFiles'; } from '../changedFiles.js';
import {toBranchMatchConfig, BranchMatchConfig} from '../branch'; import {toBranchMatchConfig, BranchMatchConfig} from '../branch.js';
export interface MatchConfig { export interface MatchConfig {
all?: BaseMatchConfig[]; all?: BaseMatchConfig[];
+6 -6
View File
@@ -1,6 +1,6 @@
export * from './get-changed-files'; export * from './get-changed-files.js';
export * from './get-changed-pull-requests'; export * from './get-changed-pull-requests.js';
export * from './get-content'; export * from './get-content.js';
export * from './get-label-configs'; export * from './get-label-configs.js';
export * from './set-labels'; export * from './set-labels.js';
export * from './types'; export * from './types.js';
+1 -1
View File
@@ -1,5 +1,5 @@
import * as github from '@actions/github'; import * as github from '@actions/github';
import {ClientType} from './types'; import {ClientType} from './types.js';
export const setLabels = async ( export const setLabels = async (
client: ClientType, client: ClientType,
+1 -1
View File
@@ -1,7 +1,7 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import {Minimatch} from 'minimatch'; import {Minimatch} from 'minimatch';
import {printPattern, isObject, kebabToCamel} from './utils'; import {printPattern, isObject, kebabToCamel} from './utils.js';
export interface ChangedFilesMatchConfig { export interface ChangedFilesMatchConfig {
changedFiles?: ChangedFilesGlobPatternsConfig[]; changedFiles?: ChangedFilesGlobPatternsConfig[];
+1 -1
View File
@@ -1,5 +1,5 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import {getPrNumbers} from './get-pr-numbers'; import {getPrNumbers} from './get-pr-numbers.js';
export const getInputs = () => ({ export const getInputs = () => ({
token: core.getInput('repo-token'), token: core.getInput('repo-token'),
+1 -1
View File
@@ -1 +1 @@
export * from './get-inputs'; export * from './get-inputs.js';
+7 -6
View File
@@ -1,19 +1,19 @@
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import * as pluginRetry from '@octokit/plugin-retry'; import * as pluginRetry from '@octokit/plugin-retry';
import * as api from './api'; import * as api from './api/index.js';
import isEqual from 'lodash.isequal'; import isEqual from 'lodash.isequal';
import {getInputs} from './get-inputs'; import {getInputs} from './get-inputs/index.js';
import { import {
BaseMatchConfig, BaseMatchConfig,
MatchConfig, MatchConfig,
configUsesChangedFiles configUsesChangedFiles
} from './api/get-label-configs'; } from './api/get-label-configs.js';
import {checkAllChangedFiles, checkAnyChangedFiles} from './changedFiles'; import {checkAllChangedFiles, checkAnyChangedFiles} from './changedFiles.js';
import {checkAnyBranch, checkAllBranch} from './branch'; import {checkAnyBranch, checkAllBranch} from './branch.js';
type ClientType = ReturnType<typeof github.getOctokit>; type ClientType = ReturnType<typeof github.getOctokit>;
@@ -142,7 +142,8 @@ export async function labeler() {
) { ) {
throw new Error( throw new Error(
`Failed to set labels for PR #${pullRequest.number}. The workflow does not have permission to create labels. ` + `Failed to set labels for PR #${pullRequest.number}. The workflow does not have permission to create labels. ` +
`Ensure the 'issues: write' permission is granted in the workflow file or manually create the missing labels in the repository before running the action.` `Ensure the 'issues: write' permission is granted in the workflow file or manually create the missing labels in the repository before running the action.`,
{cause: error}
); );
} else if ( } else if (
error.name !== 'HttpError' || error.name !== 'HttpError' ||
+1 -1
View File
@@ -1,3 +1,3 @@
import {run} from './labeler'; import {run} from './labeler.js';
run(); run();
+5 -5
View File
@@ -2,8 +2,9 @@
"compilerOptions": { "compilerOptions": {
/* Basic Options */ /* Basic Options */
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "target": "ES2022", /* Emit ES2022-compatible JavaScript. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "module": "NodeNext", /* Use Node.js ESM module semantics. */
"moduleResolution": "NodeNext", /* Resolve modules using Node.js ESM rules. */
// "allowJs": true, /* Allow javascript files to be compiled. */ // "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */ // "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
@@ -38,7 +39,7 @@
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */ /* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node16' or 'NodeNext'. */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
@@ -46,7 +47,6 @@
// "types": [], /* Type declaration files to be included in compilation. */ // "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */ /* Source Map Options */
@@ -59,5 +59,5 @@
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}, },
"exclude": ["node_modules", "__tests__", "__mocks__"] "exclude": ["node_modules", "__tests__", "__mocks__", "jest.config.ts"]
} }