├── .husky ├── .gitignore └── pre-commit ├── index.js ├── .eslintignore ├── .prettierignore ├── .gitattributes ├── demo.png ├── .editorconfig ├── .eslintrc.json ├── jest.config.js ├── .github ├── dependabot.yml └── workflows │ ├── auto-merge.yml │ ├── tags.yml │ └── main.yml ├── tsconfig.json ├── src ├── action.ts ├── support.ts ├── __tests__ │ ├── support.test.ts │ ├── action.test.ts │ └── helpers.test.ts ├── types.ts ├── check.ts ├── context.ts └── helpers.ts ├── action.yaml ├── LICENSE ├── package.json ├── FAQ.md ├── README.md ├── .gitignore └── dist ├── sourcemap-register.js └── licenses.txt /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('./dist/index').start(); 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/** -diff linguist-generated=true 2 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z0al/dependent-issues/HEAD/demo.png -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname $0)/_/husky.sh" 3 | 4 | npm run package && git add dist 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_size = 2 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{yml,yaml}] 11 | indent_style = space 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier"], 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 9, 6 | "sourceType": "module" 7 | }, 8 | "env": { 9 | "node": true, 10 | "es6": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | moduleFileExtensions: ['js', 'ts'], 4 | testEnvironment: 'node', 5 | testMatch: ['**/*.test.ts'], 6 | transform: { 7 | '^.+\\.ts$': 'ts-jest', 8 | }, 9 | verbose: true, 10 | }; 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Enable version updates for npm 4 | - package-ecosystem: 'npm' 5 | # Look for `package.json` and `lock` files in the `root` directory 6 | directory: '/' 7 | # Check the npm registry for updates weekly 8 | schedule: 9 | interval: 'daily' 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "outDir": "./lib", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "esModuleInterop": true, 10 | "sourceMap": true 11 | }, 12 | "exclude": ["node_modules", "**/*.test.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Auto-merge 2 | 3 | on: 4 | - pull_request_target 5 | 6 | jobs: 7 | auto-merge: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 12 | with: 13 | github-token: ${{ secrets.DEPENDABOT_TOKEN }} 14 | target: minor 15 | approve: false 16 | command: squash and merge 17 | -------------------------------------------------------------------------------- /.github/workflows/tags.yml: -------------------------------------------------------------------------------- 1 | name: Update Tags 2 | 3 | on: 4 | release: 5 | types: 6 | - created 7 | - published 8 | - edited 9 | 10 | jobs: 11 | action-tagger: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: Actions-R-Us/actions-tagger@latest 16 | with: 17 | publish_latest_tag: false 18 | prefer_branch_releases: false 19 | token: ${{ github.token }} 20 | -------------------------------------------------------------------------------- /src/action.ts: -------------------------------------------------------------------------------- 1 | // Packages 2 | import * as core from '@actions/core'; 3 | 4 | // Ours 5 | import { checkIssues } from './check'; 6 | import { getActionContext } from './context'; 7 | 8 | // Entry point 9 | export async function start() { 10 | try { 11 | await checkIssues(await getActionContext()); 12 | } catch (error) { 13 | if (!(error instanceof Error)) { 14 | return core.setFailed(`${error}`); 15 | } 16 | 17 | core.setFailed(error.stack || error.message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/support.ts: -------------------------------------------------------------------------------- 1 | // Ours 2 | import { Issue } from './types'; 3 | 4 | /** 5 | * Workflows triggered by Dependabot PRs will run with read-only 6 | * permissions. We need to ignore them. 7 | * 8 | * https://bit.ly/2NDzjUM 9 | */ 10 | function isDependabotPR(issue: Issue) { 11 | return issue.user?.login === 'dependabot[bot]'; 12 | } 13 | 14 | export function isSupported( 15 | config: { ignore_dependabot: string }, 16 | issue: Issue 17 | ) { 18 | return !(config.ignore_dependabot === 'on' && isDependabotPR(issue)); 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - run: yarn install 12 | - run: yarn test 13 | - run: yarn lint 14 | release: 15 | name: Release 16 | needs: test 17 | if: ${{ github.actor != 'dependabot[bot]' && github.ref == 'refs/heads/main' }} 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v2 21 | - run: yarn 22 | - env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | run: yarn semantic-release 25 | -------------------------------------------------------------------------------- /src/__tests__/support.test.ts: -------------------------------------------------------------------------------- 1 | import { isSupported } from '../support'; 2 | 3 | describe('isSupported', () => { 4 | it('should not fail when given incomplete issue data', () => { 5 | const config: any = { 6 | ignore_dependabot: 'off', 7 | }; 8 | const issue: any = { 9 | user: null, 10 | }; 11 | 12 | expect(isSupported(config, issue)).toEqual(true); 13 | }); 14 | 15 | it('should return false for ignored Dependabot issues or pull requests', () => { 16 | const config: any = { 17 | ignore_dependabot: 'on', 18 | }; 19 | const issue: any = { 20 | user: { 21 | login: 'dependabot[bot]', 22 | }, 23 | }; 24 | 25 | expect(isSupported(config, issue)).toEqual(false); 26 | }); 27 | 28 | it('should return true for non-ignored Dependabot issues or pull requests', () => { 29 | const config: any = { 30 | ignore_dependabot: 'off', 31 | }; 32 | const issue: any = { 33 | user: { 34 | login: 'dependabot[bot]', 35 | }, 36 | }; 37 | 38 | expect(isSupported(config, issue)).toEqual(true); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { Octokit } from '@octokit/rest'; 2 | import * as github from '@actions/github'; 3 | 4 | // https://stackoverflow.com/questions/48011353/how-to-unwrap-type-of-a-promise 5 | type UnwrapPromise = T extends PromiseLike ? U : T; 6 | 7 | export type GithubClient = Octokit; 8 | 9 | export type Issue = UnwrapPromise< 10 | ReturnType 11 | >['data']; 12 | 13 | export type Comment = UnwrapPromise< 14 | ReturnType 15 | >['data']; 16 | 17 | export type Dependency = Required & { 18 | blocker?: boolean; 19 | }; 20 | export type Repository = Required; 21 | 22 | export type ActionContext = { 23 | client: GithubClient; 24 | readOnlyClient: GithubClient; 25 | issues: Issue[]; 26 | repo: Repository; 27 | config: { 28 | actionName: string; 29 | commentBody: string; 30 | commentSignature: string; 31 | label: string; 32 | check_issues: string; 33 | ignore_dependabot: string; 34 | keywords: string[]; 35 | }; 36 | }; 37 | -------------------------------------------------------------------------------- /action.yaml: -------------------------------------------------------------------------------- 1 | name: Dependent Issues 2 | description: Mark issues as dependent on another 3 | author: Ahmed T. Ali 4 | 5 | branding: 6 | icon: box 7 | color: black 8 | 9 | inputs: 10 | label: 11 | required: false 12 | description: The label to use to mark dependent issues 13 | default: dependent 14 | 15 | check_issues: 16 | required: false 17 | description: Enable checking for dependencies in issues 18 | default: off 19 | 20 | ignore_dependabot: 21 | required: false 22 | description: Ignore Dependabot PRs. 23 | default: off 24 | 25 | keywords: 26 | required: false 27 | description: A comma-separated list of keywords 28 | default: depends on, blocked by 29 | 30 | comment: 31 | required: false 32 | description: A custom comment body. It supports `{{ dependencies }}` token. 33 | default: > 34 | This PR/issue depends on: 35 | 36 | {{ dependencies }} 37 | 38 | By **[Dependent Issues](https://github.com/z0al/dependent-issues)** (🤖). Happy coding! 39 | 40 | runs: 41 | using: node16 42 | main: ./index.js 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2017 Ahmed T. Ali 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dependent-issues", 3 | "version": "0.0.0-development", 4 | "private": true, 5 | "main": "lib/action.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "format": "prettier --write **/*.ts", 9 | "lint": "eslint src/**/*.ts", 10 | "prepackage": "rimraf -rf dist && rimraf -rf lib && yarn build", 11 | "package": "ncc build --source-map --license licenses.txt", 12 | "test": "jest --passWithNoTests", 13 | "postinstall": "husky install", 14 | "semantic-release": "semantic-release" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/z0al/dependent-issues.git" 19 | }, 20 | "author": "", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@actions/core": "^1.9.1", 24 | "@actions/github": "^5.0.3", 25 | "@octokit/rest": "18.12.0", 26 | "dequal": "^2.0.2", 27 | "issue-regex": "^3.1.0", 28 | "lodash.uniqby": "^4.7.0" 29 | }, 30 | "devDependencies": { 31 | "@types/jest": "^27.5.0", 32 | "@types/js-yaml": "^4.0.5", 33 | "@types/lodash.uniqby": "^4.7.7", 34 | "@types/node": "^17.0.43", 35 | "@typescript-eslint/parser": "^5.30.5", 36 | "@vercel/ncc": "^0.34.0", 37 | "eslint": "^8.19.0", 38 | "eslint-config-prettier": "^8.5.0", 39 | "husky": "^7.0.4", 40 | "jest": "^27.5.1", 41 | "js-yaml": "^4.1.0", 42 | "prettier": "^2.7.1", 43 | "rimraf": "^3.0.2", 44 | "semantic-release": "^17.4.7", 45 | "ts-jest": "^27.1.4", 46 | "typescript": "^4.7.4" 47 | }, 48 | "prettier": { 49 | "printWidth": 72, 50 | "useTabs": true, 51 | "singleQuote": true 52 | }, 53 | "release": { 54 | "branches": [ 55 | "main" 56 | ], 57 | "plugins": [ 58 | "@semantic-release/commit-analyzer", 59 | "@semantic-release/release-notes-generator", 60 | "@semantic-release/github" 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/check.ts: -------------------------------------------------------------------------------- 1 | // Packages 2 | import * as core from '@actions/core'; 3 | 4 | // Ours 5 | import { ActionContext } from './types'; 6 | import { isSupported } from './support'; 7 | 8 | import { 9 | IssueManager, 10 | DependencyResolver, 11 | DependencyExtractor, 12 | formatDependency, 13 | } from './helpers'; 14 | 15 | export async function checkIssues(context: ActionContext) { 16 | const { client, readOnlyClient, config, repo } = context; 17 | 18 | const manager = new IssueManager(client, repo, config); 19 | const extractor = new DependencyExtractor(repo, config.keywords); 20 | const resolver = new DependencyResolver( 21 | readOnlyClient, 22 | context.issues, 23 | repo 24 | ); 25 | 26 | for (const issue of context.issues) { 27 | core.startGroup(`Checking #${issue.number}`); 28 | 29 | if (!isSupported(config, issue)) { 30 | core.info('Unsupported issue or pull request. Skipped'); 31 | core.endGroup(); 32 | continue; 33 | } 34 | 35 | let dependencies = extractor.fromIssue(issue); 36 | 37 | if (dependencies.length === 0) { 38 | core.info('No dependencies found. Running clean-up'); 39 | await manager.removeLabel(issue); 40 | await manager.removeActionComments(issue); 41 | await manager.updateCommitStatus(issue, []); 42 | 43 | core.endGroup(); 44 | continue; 45 | } 46 | 47 | let isBlocked = false; 48 | 49 | core.info( 50 | `Depends on: ${dependencies 51 | .map((dep) => formatDependency(dep, repo)) 52 | .join(', ')}` 53 | ); 54 | 55 | dependencies = await Promise.all( 56 | dependencies.map(async (dep) => { 57 | const issue = await resolver.get(dep); 58 | if (issue.state === 'open') { 59 | isBlocked = true; 60 | } 61 | 62 | return { ...dep, blocker: issue.state === 'open' }; 63 | }) 64 | ); 65 | 66 | core.info( 67 | `Blocked by: ${dependencies 68 | .filter((dep) => dep.blocker) 69 | .map((dep) => formatDependency(dep, repo)) 70 | .join(', ')}` 71 | ); 72 | 73 | core.info('Updating labels'); 74 | // Toggle label 75 | isBlocked 76 | ? await manager.addLabel(issue) 77 | : await manager.removeLabel(issue); 78 | 79 | core.info('Updating Action comments'); 80 | await manager.writeComment( 81 | issue, 82 | manager.generateComment(dependencies, dependencies, config), 83 | !isBlocked 84 | ); 85 | 86 | core.info( 87 | `Updating PR status${issue.pull_request ? '' : '. Skipped'}` 88 | ); 89 | await manager.updateCommitStatus(issue, dependencies); 90 | core.endGroup(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions (FAQs) 2 | 3 | ## Using cross-repository dependencies within an organization 4 | 5 | 1. Setup the action 6 | 7 | Set up the action in the repository, where you'd like to create issues and pull requests, that need to use issues or pull requests from another repository within your organization. 8 | 9 | Remember to setup 10 | ```yml 11 | on: 12 | schedule: 13 | - cron: '0 0 * * *' 14 | ``` 15 | to regularly check the status of referenced issues in other repositories. Adjust the cron-schedule to your personal needs. 16 | 17 | 2. Create a new PAT 18 | 19 | The default setup uses the pre-configured `GITHUB_TOKEN`, but that only enables access to the repository, in which the action is set up. 20 | Because of that, you need to create a new PAT (Personal Access Token). Consider using the organization account, to create the PAT. 21 | 22 | Follow the official documentation to create a new PAT: 23 | 24 | [https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) 25 | 26 | When setting up the new PAT, you need to grant `repo` permissions. Explanations for each permission can be found in the official documentation: 27 | 28 | [https://docs.github.com/en/developers/apps/scopes-for-oauth-apps](https://docs.github.com/en/developers/apps/scopes-for-oauth-apps) 29 | 30 | 3. Add a new secret in GitHub to access the PAT within the action-workflow 31 | 32 | Follow the instructions under [https://docs.github.com/en/actions/reference/authentication-in-a-workflow#granting-additional-permissions](https://docs.github.com/en/actions/reference/authentication-in-a-workflow#granting-additional-permissions) to add a new secret to the repository. 33 | 34 | 4. Integrate the secret in the dependent-issue workflow 35 | 36 | Replace the usage of the `GITHUB_TOKEN` with your secret: 37 | 38 | ```yml 39 | jobs: 40 | check: 41 | steps: 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.YOUR_NEW_TOKEN }} 44 | ``` 45 | 46 | > Always use the GitHub secret-management to store your PATs! Never put secrets directly in your repository-source code! 47 | 48 | 5. Reference issues and pull requests from other organization repositories 49 | 50 | Reference the issue or pull-request with the URL like the following example: 51 | 52 | ```md 53 | Depends on https://github.com/z0al/dependent-issues/pull/1 54 | ``` 55 | -------------------------------------------------------------------------------- /src/context.ts: -------------------------------------------------------------------------------- 1 | // Packages 2 | import * as core from '@actions/core'; 3 | import * as github from '@actions/github'; 4 | 5 | // Ours 6 | import { ActionContext, GithubClient, Issue } from './types'; 7 | 8 | export async function getActionContext(): Promise { 9 | core.startGroup('Context'); 10 | 11 | const config = { 12 | actionName: 'Dependent Issues', 13 | commentBody: core.getInput('comment'), 14 | commentSignature: 15 | '', 16 | label: core.getInput('label'), 17 | check_issues: core.getInput('check_issues'), 18 | ignore_dependabot: core.getInput('ignore_dependabot'), 19 | keywords: core 20 | .getInput('keywords') 21 | .trim() 22 | .split(',') 23 | .map((kw) => kw.trim()), 24 | }; 25 | 26 | if (config.keywords.length === 0) { 27 | throw new Error('invalid keywords'); 28 | } 29 | 30 | if (!process.env.GITHUB_TOKEN) { 31 | throw new Error('env.GITHUB_TOKEN must not be empty'); 32 | } 33 | 34 | const client = github.getOctokit( 35 | process.env.GITHUB_TOKEN 36 | ) as unknown as GithubClient; 37 | 38 | const readOnlyClient = github.getOctokit( 39 | process.env.GITHUB_READ_TOKEN || process.env.GITHUB_TOKEN 40 | ) as unknown as GithubClient; 41 | 42 | const { issue, repo } = github.context; 43 | 44 | let issues: Issue[] = []; 45 | 46 | // If we are running in an issue context then only run checks 47 | // for the issue in question. Unless, it's a close event because 48 | // then the issue could be a dependency of another PR/issue. 49 | if (issue?.number) { 50 | core.info(`Payload issue: #${issue?.number}`); 51 | const remoteIssue = ( 52 | await client.rest.issues.get({ 53 | ...repo, 54 | issue_number: issue.number, 55 | }) 56 | ).data; 57 | 58 | // Ignore closed PR/issues 59 | if (remoteIssue.state === 'open') { 60 | issues = [remoteIssue]; 61 | } 62 | } 63 | 64 | // Otherwise, check all open issues. 65 | // If a label was provided in the config, check all open issues 66 | // that have the label on them. 67 | if (issues.length === 0) { 68 | core.info(`Payload issue: None or closed`); 69 | const options = { 70 | ...repo, 71 | state: 'open' as 'open', 72 | per_page: 100, 73 | }; 74 | 75 | let queryParams = config.label 76 | ? { ...options, labels: config.label } 77 | : options; 78 | 79 | const method: any = 80 | config.check_issues === 'on' 81 | ? client.rest.issues.listForRepo 82 | : client.rest.pulls.list; 83 | 84 | issues = (await client.paginate(method, queryParams)) as Issue[]; 85 | core.info(`No. of open issues: ${issues.length}`); 86 | } 87 | 88 | core.endGroup(); 89 | 90 | return { 91 | client, 92 | readOnlyClient, 93 | config, 94 | repo, 95 | issues, 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /src/__tests__/action.test.ts: -------------------------------------------------------------------------------- 1 | // Ours 2 | import * as action from '../action'; 3 | import { ActionContext, GithubClient } from '../types'; 4 | 5 | var gh: GithubClient; 6 | var inputs: ActionContext['config']; 7 | 8 | // Mock @actions modules 9 | jest.mock('@actions/core', () => { 10 | inputs = { 11 | label: 'dependent', 12 | issues: 'off', 13 | keywords: 'depends on, blocked by', 14 | comment: 'This PR/issue depends on:\n\n{{ dependencies }}', 15 | } as any; 16 | 17 | return { 18 | getInput: jest 19 | .fn() 20 | .mockImplementation((key: string) => (inputs as any)[key]), 21 | info: jest.fn(), 22 | startGroup: jest.fn(), 23 | endGroup: jest.fn(), 24 | }; 25 | }); 26 | 27 | jest.mock('@actions/github', () => { 28 | gh = { 29 | rest: { 30 | pulls: { 31 | get: jest.fn().mockResolvedValue({ 32 | data: { 33 | head: { sha: '' }, 34 | }, 35 | }) as any, 36 | list: jest.fn().mockResolvedValue([ 37 | { 38 | number: 1, 39 | body: 'This work depends on #2 and blocked by user/another-repo#3', 40 | pull_request: {}, 41 | labels: [{ name: 'bug' }], 42 | }, 43 | { 44 | number: 2, 45 | pull_request: {}, 46 | body: 'This work does not depend on anything', 47 | labels: [], 48 | state: 'open', 49 | }, 50 | ]) as any, 51 | }, 52 | issues: { 53 | get: jest.fn().mockResolvedValue({ 54 | data: { 55 | number: 3, 56 | state: 'open', 57 | }, 58 | }) as any, 59 | addLabels: jest.fn() as any, 60 | createComment: jest.fn() as any, 61 | listComments: jest.fn().mockResolvedValue([]) as any, 62 | }, 63 | repos: { 64 | createCommitStatus: jest.fn() as any, 65 | }, 66 | }, 67 | paginate: jest.fn().mockImplementation((fn, opt) => { 68 | const { per_page: _, ...rest } = opt; 69 | return fn(rest); 70 | }) as any, 71 | } as GithubClient; 72 | 73 | return { 74 | context: { 75 | repo: { owner: 'owner', repo: 'repo' }, 76 | issue: {}, 77 | }, 78 | getOctokit: jest.fn().mockReturnValue(gh), 79 | }; 80 | }); 81 | 82 | process.env.GITHUB_TOKEN = ''; 83 | 84 | test('it works in default config', async () => { 85 | // Trigger action 86 | await action.start(); 87 | 88 | expect(gh.rest.issues.createComment).toHaveBeenCalledWith({ 89 | issue_number: 1, 90 | owner: 'owner', 91 | repo: 'repo', 92 | body: `This PR/issue depends on: 93 | 94 | * owner/repo#2 95 | * user/another-repo#3 96 | `, 97 | }); 98 | 99 | expect(gh.rest.issues.createComment).toHaveBeenCalledTimes(1); 100 | 101 | expect(gh.rest.issues.addLabels).toHaveBeenCalledWith({ 102 | owner: 'owner', 103 | repo: 'repo', 104 | issue_number: 1, 105 | labels: ['dependent'], 106 | }); 107 | 108 | expect(gh.rest.issues.addLabels).toHaveBeenCalledTimes(1); 109 | 110 | expect(gh.rest.repos.createCommitStatus).toHaveBeenCalledWith({ 111 | owner: 'owner', 112 | repo: 'repo', 113 | description: 'Blocked by #2 and 1 more issues', 114 | state: 'pending', 115 | context: 'Dependent Issues', 116 | sha: '', 117 | }); 118 | 119 | expect(gh.rest.repos.createCommitStatus).toHaveBeenCalledWith({ 120 | owner: 'owner', 121 | repo: 'repo', 122 | description: 'No dependencies', 123 | state: 'success', 124 | context: 'Dependent Issues', 125 | sha: '', 126 | }); 127 | 128 | expect(gh.rest.repos.createCommitStatus).toHaveBeenCalledTimes(2); 129 | }); 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dependent Issues 2 | 3 | > A GitHub Action for marking issues as dependent on another 4 | 5 | It works with PRs and issues and supports cross-repository dependencies. 6 | 7 | ## Usage 8 | 9 | Create `.github/workflows/dependent-issues.yml` with the following content: 10 | 11 | ```yaml 12 | name: Dependent Issues 13 | 14 | on: 15 | issues: 16 | types: 17 | - opened 18 | - edited 19 | - closed 20 | - reopened 21 | pull_request_target: 22 | types: 23 | - opened 24 | - edited 25 | - closed 26 | - reopened 27 | # Makes sure we always add status check for PRs. Useful only if 28 | # this action is required to pass before merging. Otherwise, it 29 | # can be removed. 30 | - synchronize 31 | 32 | # Schedule a daily check. Useful if you reference cross-repository 33 | # issues or pull requests. Otherwise, it can be removed. 34 | schedule: 35 | - cron: '0 0 * * *' 36 | 37 | jobs: 38 | check: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: z0al/dependent-issues@v1 42 | env: 43 | # (Required) The token to use to make API calls to GitHub. 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | # (Optional) The token to use to make API calls to GitHub for remote repos. 46 | GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }} 47 | 48 | with: 49 | # (Optional) The label to use to mark dependent issues 50 | label: dependent 51 | 52 | # (Optional) Enable checking for dependencies in issues. 53 | # Enable by setting the value to "on". Default "off" 54 | check_issues: off 55 | 56 | # (Optional) Ignore dependabot PRs. 57 | # Enable by setting the value to "on". Default "off" 58 | ignore_dependabot: off 59 | 60 | # (Optional) A comma-separated list of keywords. Default 61 | # "depends on, blocked by" 62 | keywords: depends on, blocked by 63 | 64 | # (Optional) A custom comment body. It supports `{{ dependencies }}` token. 65 | comment: > 66 | This PR/issue depends on: 67 | 68 | {{ dependencies }} 69 | 70 | By **[Dependent Issues](https://github.com/z0al/dependent-issues)** (🤖). Happy coding! 71 | ``` 72 | 73 | Here how it can look like in practice: 74 | 75 | ![example](./demo.png) 76 | 77 | ## Inputs 78 | 79 | - **label** (Optional): The label to use to mark dependent issues. Default `dependent`. 80 | - **check_issues** (Optional): Enable checking for dependencies in issues. Enable by setting the value to `on`. Default `off`. 81 | - **ignore_dependabot** (Optional): Ignore dependabot PRs. Enable by setting the value to `on`. Default `off`. Use this if you run the action on `pull_request` rather than `pull_request_target`. 82 | - **keywords** (Optional): A comma-separated list of keywords. Default `depends on, blocked by`. 83 | - **comment** (Optional): A custom comment body. It supports `{{ dependencies }}` token. 84 | 85 | ## Environment variables 86 | 87 | - **GITHUB_TOKEN** (Required): The token to use to make API calls to GitHub. 88 | 89 | ## FAQ 90 | 91 | Trouble setting up the action? Check the [FAQ](./FAQ.md). 92 | 93 | ## Credits 94 | 95 | Special thanks to [Jason Etcovitch](https://github.com/JasonEtco) for the original bot idea. 96 | 97 | ## License 98 | 99 | MIT © [Ahmed T. Ali](https://github.com/z0al) 100 | 101 | [dependabot-change]: https://github.blog/changelog/2021-02-19-github-actions-workflows-triggered-by-dependabot-prs-will-run-with-read-only-permissions/ 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/node,linux,macos,windows,vim,visualstudiocode 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node,linux,macos,windows,vim,visualstudiocode 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### macOS ### 21 | # General 22 | .DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | 29 | 30 | # Thumbnails 31 | ._* 32 | 33 | # Files that might appear in the root of a volume 34 | .DocumentRevisions-V100 35 | .fseventsd 36 | .Spotlight-V100 37 | .TemporaryItems 38 | .Trashes 39 | .VolumeIcon.icns 40 | .com.apple.timemachine.donotpresent 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | ### Node ### 50 | # Logs 51 | logs 52 | *.log 53 | npm-debug.log* 54 | yarn-debug.log* 55 | yarn-error.log* 56 | lerna-debug.log* 57 | 58 | # Diagnostic reports (https://nodejs.org/api/report.html) 59 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 60 | 61 | # Runtime data 62 | pids 63 | *.pid 64 | *.seed 65 | *.pid.lock 66 | 67 | # Directory for instrumented libs generated by jscoverage/JSCover 68 | lib-cov 69 | 70 | # Coverage directory used by tools like istanbul 71 | coverage 72 | *.lcov 73 | 74 | # nyc test coverage 75 | .nyc_output 76 | 77 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 78 | .grunt 79 | 80 | # Bower dependency directory (https://bower.io/) 81 | bower_components 82 | 83 | # node-waf configuration 84 | .lock-wscript 85 | 86 | # Compiled binary addons (https://nodejs.org/api/addons.html) 87 | build/Release 88 | 89 | # Dependency directories 90 | node_modules/ 91 | jspm_packages/ 92 | 93 | # TypeScript v1 declaration files 94 | typings/ 95 | 96 | # TypeScript cache 97 | *.tsbuildinfo 98 | 99 | # Optional npm cache directory 100 | .npm 101 | 102 | # Optional eslint cache 103 | .eslintcache 104 | 105 | # Microbundle cache 106 | .rpt2_cache/ 107 | .rts2_cache_cjs/ 108 | .rts2_cache_es/ 109 | .rts2_cache_umd/ 110 | 111 | # Optional REPL history 112 | .node_repl_history 113 | 114 | # Output of 'npm pack' 115 | *.tgz 116 | 117 | # Yarn Integrity file 118 | .yarn-integrity 119 | 120 | # dotenv environment variables file 121 | .env 122 | .env.test 123 | .env*.local 124 | 125 | # parcel-bundler cache (https://parceljs.org/) 126 | .cache 127 | .parcel-cache 128 | 129 | # Next.js build output 130 | .next 131 | 132 | # Nuxt.js build / generate output 133 | .nuxt 134 | 135 | # Gatsby files 136 | .cache/ 137 | # Comment in the public line in if your project uses Gatsby and not Next.js 138 | # https://nextjs.org/blog/next-9-1#public-directory-support 139 | # public 140 | 141 | # vuepress build output 142 | .vuepress/dist 143 | 144 | # Serverless directories 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | .fusebox/ 149 | 150 | # DynamoDB Local files 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | .tern-port 155 | 156 | # Stores VSCode versions used for testing VSCode extensions 157 | .vscode-test 158 | 159 | ### Vim ### 160 | # Swap 161 | [._]*.s[a-v][a-z] 162 | !*.svg # comment out if you don't need vector files 163 | [._]*.sw[a-p] 164 | [._]s[a-rt-v][a-z] 165 | [._]ss[a-gi-z] 166 | [._]sw[a-p] 167 | 168 | # Session 169 | Session.vim 170 | Sessionx.vim 171 | 172 | # Temporary 173 | .netrwhist 174 | # Auto-generated tag files 175 | tags 176 | # Persistent undo 177 | [._]*.un~ 178 | 179 | ### VisualStudioCode ### 180 | .vscode/* 181 | !.vscode/tasks.json 182 | !.vscode/launch.json 183 | *.code-workspace 184 | 185 | ### VisualStudioCode Patch ### 186 | # Ignore all local history of files 187 | .history 188 | .ionide 189 | 190 | ### Windows ### 191 | # Windows thumbnail cache files 192 | Thumbs.db 193 | Thumbs.db:encryptable 194 | ehthumbs.db 195 | ehthumbs_vista.db 196 | 197 | # Dump file 198 | *.stackdump 199 | 200 | # Folder config file 201 | [Dd]esktop.ini 202 | 203 | # Recycle Bin used on file shares 204 | $RECYCLE.BIN/ 205 | 206 | # Windows Installer files 207 | *.cab 208 | *.msi 209 | *.msix 210 | *.msm 211 | *.msp 212 | 213 | # Windows shortcuts 214 | *.lnk 215 | 216 | # End of https://www.toptal.com/developers/gitignore/api/node,linux,macos,windows,vim,visualstudiocode 217 | 218 | lib/ 219 | -------------------------------------------------------------------------------- /src/helpers.ts: -------------------------------------------------------------------------------- 1 | // Packages 2 | import { dequal } from 'dequal'; 3 | import uniqBy from 'lodash.uniqby'; 4 | import IssueRegex from 'issue-regex'; 5 | 6 | // Ours 7 | import { 8 | Dependency, 9 | Issue, 10 | Repository, 11 | GithubClient, 12 | ActionContext, 13 | Comment, 14 | } from './types'; 15 | 16 | export function formatDependency(dep: Dependency, repo?: Repository) { 17 | const depRepo = { owner: dep.owner, repo: dep.repo }; 18 | 19 | if (dequal(depRepo, repo)) { 20 | return `#${dep.number}`; 21 | } 22 | 23 | return `${dep.owner}/${dep.repo}#${dep.number}`; 24 | } 25 | 26 | export class DependencyExtractor { 27 | private regex: RegExp; 28 | private issueRegex = IssueRegex(); 29 | private urlRegex = 30 | /https?:\/\/github\.com\/(?:\w[\w-.]+\/\w[\w-.]+|\B)\/(?:issues|pull)\/[1-9]\d*\b/; 31 | private keywordRegex: RegExp; 32 | 33 | constructor(private repo: Repository, keywords: string[]) { 34 | this.keywordRegex = new RegExp( 35 | keywords.map((kw) => kw.trim().replace(/\s+/g, '\\s+')).join('|'), 36 | 'i' 37 | ); 38 | 39 | this.regex = this.buildRegex(); 40 | } 41 | 42 | private buildRegex() { 43 | const flags = this.issueRegex.flags + 'i'; 44 | const ref = `${this.issueRegex.source}|${this.urlRegex.source}`; 45 | 46 | return new RegExp( 47 | `(?:${this.keywordRegex.source})\\s+(${ref})`, 48 | flags 49 | ); 50 | } 51 | 52 | private deduplicate(deps: Dependency[]) { 53 | return uniqBy(deps, formatDependency); 54 | } 55 | 56 | private match(text: string) { 57 | const references = text.match(this.regex) || []; 58 | 59 | return references.map((ref) => { 60 | // Get rid of keywords now 61 | ref = ref.replace(this.keywordRegex, '').trim(); 62 | 63 | // Remove full URL if found. Should return either '#number' or 64 | // 'owner/repo#number' format 65 | return ref 66 | .replace(/https?:\/\/github\.com\//i, '') 67 | .replace(/\/(issues|pull)\//i, '#'); 68 | }); 69 | } 70 | 71 | public fromIssue(issue: Issue) { 72 | const dependencies: Dependency[] = []; 73 | 74 | for (const issueLink of this.match(issue.body || '')) { 75 | // Can be '#number' or 'owner/repo#number' 76 | // 1) #number 77 | if (issueLink.startsWith('#')) { 78 | const issueNumber = Number(issueLink.slice(1)); 79 | 80 | // Prevent self-referencing 81 | if (issueNumber !== issue.number) { 82 | dependencies.push({ 83 | ...this.repo, 84 | number: issueNumber, 85 | }); 86 | } 87 | 88 | continue; 89 | } 90 | 91 | // 2) owner/repo#number 92 | const [owner, rest] = issueLink.split('/'); 93 | const [repoName, issueNumber] = rest.split('#'); 94 | 95 | dependencies.push({ 96 | owner, 97 | repo: repoName, 98 | number: Number(issueNumber), 99 | }); 100 | } 101 | 102 | return this.deduplicate(dependencies); 103 | } 104 | } 105 | 106 | export class DependencyResolver { 107 | private cache: Map; 108 | 109 | constructor( 110 | private gh: GithubClient, 111 | issues: Issue[], 112 | repo: Repository 113 | ) { 114 | this.cache = new Map(); 115 | 116 | // Populate the cache with the known issues 117 | issues.forEach((issue) => { 118 | this.cache.set( 119 | this.cacheKey({ 120 | ...repo, 121 | number: issue.number, 122 | }), 123 | { issue, repo } 124 | ); 125 | }); 126 | } 127 | 128 | private cacheKey(dep: Dependency) { 129 | return `${dep.owner}/${dep.repo}#${dep.number}`; 130 | } 131 | 132 | async get(dep: Dependency) { 133 | const key = this.cacheKey(dep); 134 | const cachedIssue = this.cache.get(key)?.issue; 135 | 136 | if (cachedIssue) { 137 | return cachedIssue; 138 | } 139 | 140 | // Fetch from GitHub 141 | const remoteIssue = ( 142 | await this.gh.rest.issues.get({ 143 | owner: dep.owner, 144 | repo: dep.repo, 145 | issue_number: dep.number, 146 | }) 147 | ).data; 148 | 149 | this.cache.set(key, { 150 | issue: remoteIssue, 151 | repo: { 152 | owner: dep.owner, 153 | repo: dep.repo, 154 | }, 155 | }); 156 | 157 | return remoteIssue; 158 | } 159 | } 160 | 161 | export class IssueManager { 162 | constructor( 163 | private gh: GithubClient, 164 | private repo: Repository, 165 | private config: ActionContext['config'] 166 | ) {} 167 | 168 | hasLabel(issue: Issue) { 169 | const labels = issue.labels.map((label) => 170 | typeof label === 'string' ? label : label.name 171 | ); 172 | 173 | return labels.includes(this.config.label); 174 | } 175 | 176 | async addLabel(issue: Issue) { 177 | const shouldAddLabel = !this.hasLabel(issue); 178 | 179 | if (shouldAddLabel) { 180 | await this.gh.rest.issues.addLabels({ 181 | ...this.repo, 182 | issue_number: issue.number, 183 | labels: [this.config.label], 184 | }); 185 | } 186 | } 187 | 188 | async removeLabel(issue: Issue) { 189 | const shouldRemoveLabel = this.hasLabel(issue); 190 | 191 | if (shouldRemoveLabel) { 192 | await this.gh.rest.issues.removeLabel({ 193 | ...this.repo, 194 | issue_number: issue.number, 195 | name: this.config.label, 196 | }); 197 | } 198 | } 199 | 200 | /** 201 | * Adds a unique text at the end of the text to distinguish the 202 | * action own's comments. 203 | */ 204 | private sign(text: string) { 205 | return text.trim() + '\n' + this.config.commentSignature; 206 | } 207 | 208 | private isSigned(text?: string) { 209 | if (!text) { 210 | return false; 211 | } 212 | 213 | return text.trim().endsWith(this.config.commentSignature); 214 | } 215 | 216 | private originalText(signed?: string) { 217 | if (!signed) { 218 | return ''; 219 | } 220 | 221 | return signed 222 | .trim() 223 | .slice(0, -1 * this.config.commentSignature.length) 224 | .trim(); 225 | } 226 | 227 | public generateComment( 228 | deps: Dependency[], 229 | dependencies: Dependency[], 230 | config: ActionContext['config'] 231 | ) { 232 | // e.g: 233 | // * facebook/react#999 234 | // * ~~facebook/react#1~~ 235 | const dependenciesList = deps 236 | .map((dep) => { 237 | const link = formatDependency(dep); 238 | return '* ' + (dep.blocker ? link : `~~${link}~~`); 239 | }) 240 | .join('\n'); 241 | 242 | // 243 | return config.commentBody.replace( 244 | /\{\{\s*dependencies\s*\}\}/gi, 245 | dependenciesList 246 | ); 247 | } 248 | 249 | async writeComment(issue: Issue, text: string, create = false) { 250 | const signedText = this.sign(text); 251 | 252 | const issueComments: Comment[] = await this.gh.paginate( 253 | this.gh.rest.issues.listComments as any, 254 | { ...this.repo, issue_number: issue.number, per_page: 100 } 255 | ); 256 | 257 | const currentComment = issueComments.find((comment) => 258 | this.isSigned(comment.body) 259 | ); 260 | 261 | // Exit early if the content is the same 262 | if (currentComment) { 263 | const newContent = text.trim(); 264 | const existingContent = this.originalText(currentComment.body); 265 | 266 | if (existingContent === newContent) { 267 | return; 268 | } 269 | } 270 | 271 | // Delete old comment if necessary 272 | if (create && currentComment) { 273 | await this.gh.rest.issues.deleteComment({ 274 | ...this.repo, 275 | comment_id: currentComment.id, 276 | }); 277 | } 278 | 279 | const commentParams = { ...this.repo, body: signedText }; 280 | 281 | // Write comment 282 | currentComment && !create 283 | ? await this.gh.rest.issues.updateComment({ 284 | ...commentParams, 285 | comment_id: currentComment.id, 286 | }) 287 | : await this.gh.rest.issues.createComment({ 288 | ...commentParams, 289 | issue_number: issue.number, 290 | }); 291 | } 292 | 293 | async removeActionComments(issue: Issue) { 294 | const issueComments: Comment[] = await this.gh.paginate( 295 | this.gh.rest.issues.listComments as any, 296 | { ...this.repo, issue_number: issue.number, per_page: 100 } 297 | ); 298 | const existingComments = issueComments.filter((comment) => 299 | this.isSigned(comment.body) 300 | ); 301 | 302 | await Promise.all( 303 | existingComments.map((comment: any) => 304 | this.gh.rest.issues.deleteComment({ 305 | ...this.repo, 306 | comment_id: comment.id, 307 | }) 308 | ) 309 | ); 310 | } 311 | 312 | async updateCommitStatus(issue: Issue, dependencies: Dependency[]) { 313 | if (!issue.pull_request) { 314 | return; 315 | } 316 | 317 | const blockers = dependencies.filter((dep) => dep.blocker); 318 | const isBlocked = blockers.length > 0; 319 | const firstDependency = isBlocked 320 | ? formatDependency(blockers[0], this.repo) 321 | : ''; 322 | 323 | const description = !isBlocked 324 | ? dependencies.length === 0 325 | ? 'No dependencies' 326 | : 'All dependencies are resolved' 327 | : blockers.length == 1 328 | ? `Blocked by ${firstDependency}` 329 | : `Blocked by ${firstDependency} and ${ 330 | blockers.length - 1 331 | } more issues`; 332 | 333 | // Get the PR Head SHA 334 | const pull = ( 335 | await this.gh.rest.pulls.get({ 336 | ...this.repo, 337 | pull_number: issue.number, 338 | }) 339 | ).data; 340 | 341 | return this.gh.rest.repos.createCommitStatus({ 342 | ...this.repo, 343 | description, 344 | sha: pull.head.sha, 345 | context: this.config.actionName, 346 | state: isBlocked ? 'pending' : 'success', 347 | }); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /src/__tests__/helpers.test.ts: -------------------------------------------------------------------------------- 1 | // Ours 2 | import { ActionContext, GithubClient, Issue } from '../types'; 3 | import { 4 | DependencyExtractor, 5 | DependencyResolver, 6 | IssueManager, 7 | formatDependency, 8 | } from '../helpers'; 9 | 10 | test('formatDependency', () => { 11 | const repo = { owner: 'owner', repo: 'repo' }; 12 | const dep = { ...repo, number: 141 }; 13 | 14 | expect(formatDependency(dep)).toEqual('owner/repo#141'); 15 | expect(formatDependency(dep, repo)).toEqual('#141'); 16 | }); 17 | 18 | test('DependencyExtractor', () => { 19 | const repo = { 20 | owner: 'github', 21 | repo: 'atom', 22 | }; 23 | 24 | const body = ` 25 | Should match: 26 | 27 | - Plain issue: 28 | - Depends on #666 29 | - Blocked by #123 30 | - From another repository: 31 | - Depends on another/repo#123 32 | - Full issue URL: 33 | - Depends on https://github.com/another/repo/issues/141 34 | - Depends on http://github.com/another/repo/issues/404 35 | - Depends on https://github.com/another/repo/pull/142 36 | - Crazy formatting: 37 | - Depends on ano-ther.999/re_po#123 38 | - In brackets: 39 | - (Depends on #486) 40 | - [Depends on #3167] 41 | - 42 | 43 | Should NOT match: 44 | 45 | - Depends on #0 46 | - Depends on another/repo#0 47 | - Depends on nonrepo#123 48 | - Depends on non/-repo#123 49 | - Depends on user_repo#123 50 | - Depends on this/is/not/repo#123 51 | - Depends on #123hashtag 52 | - Depends on https://github.com/another/repo/pulls/142 53 | `; 54 | 55 | const issue = { body } as Issue; 56 | 57 | const expectedDeps = [ 58 | // Depends on #666 59 | { 60 | ...repo, 61 | number: 666, 62 | }, 63 | // Blocked by #123 64 | { 65 | ...repo, 66 | number: 123, 67 | }, 68 | // Depends on another/repo#123 69 | { 70 | owner: 'another', 71 | repo: 'repo', 72 | number: 123, 73 | }, 74 | // Depends on https://github.com/another/repo/issues/141 75 | { 76 | owner: 'another', 77 | repo: 'repo', 78 | number: 141, 79 | }, 80 | // Depends on http://github.com/another/repo/issues/404 81 | { 82 | owner: 'another', 83 | repo: 'repo', 84 | number: 404, 85 | }, 86 | // Depends on https://github.com/another/repo/pull/142 87 | { 88 | owner: 'another', 89 | repo: 'repo', 90 | number: 142, 91 | }, 92 | // Depends on ano-ther.999/re_po#123 93 | { 94 | owner: 'ano-ther.999', 95 | repo: 're_po', 96 | number: 123, 97 | }, 98 | // (Depends on #486) 99 | { 100 | ...repo, 101 | number: 486, 102 | }, 103 | // [Depends on #3167] 104 | { 105 | ...repo, 106 | number: 3167, 107 | }, 108 | // 109 | { 110 | owner: 'another', 111 | repo: 'repo', 112 | number: 18767, 113 | }, 114 | ]; 115 | 116 | const extractor = new DependencyExtractor(repo, [ 117 | ' depends On', 118 | 'blocked by', 119 | ]); 120 | 121 | expect(extractor.fromIssue(issue)).toEqual(expectedDeps); 122 | }); 123 | 124 | describe('DependencyResolver', () => { 125 | let gh: GithubClient; 126 | let issuesGet: jest.Mock; 127 | let resolver: DependencyResolver; 128 | 129 | const repo = { 130 | owner: 'facebook', 131 | repo: 'react', 132 | }; 133 | 134 | const contextIssues = [1, 2, 3].map((number) => ({ 135 | title: `Issue ${number}`, 136 | number, 137 | })) as Issue[]; 138 | 139 | beforeEach(() => { 140 | issuesGet = jest.fn(); 141 | 142 | gh = { 143 | rest: { 144 | issues: { 145 | get: issuesGet as any, 146 | }, 147 | }, 148 | } as GithubClient; 149 | 150 | resolver = new DependencyResolver(gh, contextIssues, repo); 151 | }); 152 | 153 | it('resolves context issues', async () => { 154 | expect( 155 | await resolver.get({ 156 | ...repo, 157 | number: 1, 158 | }) 159 | ).toEqual({ number: 1, title: 'Issue 1' }); 160 | }); 161 | 162 | it('fetches unknown issues', async () => { 163 | const issue = { number: 4, title: 'Issue 4' }; 164 | issuesGet.mockResolvedValue({ data: issue }); 165 | 166 | const dependency = { 167 | ...repo, 168 | number: 4, 169 | }; 170 | 171 | const resolvedIssue = await resolver.get(dependency); 172 | 173 | expect(issuesGet).toHaveBeenCalledWith({ 174 | ...repo, 175 | issue_number: 4, 176 | }); 177 | 178 | expect(resolvedIssue).toEqual(issue); 179 | }); 180 | 181 | it('caches fetched issues', async () => { 182 | const issue = { number: 4, title: 'Issue 4' }; 183 | issuesGet.mockResolvedValue({ data: issue }); 184 | 185 | const dependency = { 186 | ...repo, 187 | number: 4, 188 | }; 189 | 190 | await resolver.get(dependency); 191 | await resolver.get(dependency); 192 | const resolvedIssue = await resolver.get(dependency); 193 | 194 | expect(resolvedIssue).toEqual(issue); 195 | expect(issuesGet).toHaveBeenCalledTimes(1); 196 | }); 197 | }); 198 | 199 | describe('IssueManager', () => { 200 | let gh: GithubClient; 201 | let manager: IssueManager; 202 | 203 | const repo = { 204 | owner: 'Microsoft', 205 | repo: 'vscode', 206 | }; 207 | 208 | const config = { 209 | actionName: 'my-action', 210 | label: 'my-label', 211 | commentSignature: '', 212 | } as ActionContext['config']; 213 | 214 | let listComments: jest.Mock; 215 | 216 | beforeEach(() => { 217 | listComments = jest.fn(); 218 | 219 | gh = { 220 | paginate: jest 221 | .fn() 222 | .mockImplementation((_, options) => 223 | listComments(options) 224 | ) as any, 225 | rest: { 226 | issues: { 227 | addLabels: jest.fn() as any, 228 | removeLabel: jest.fn() as any, 229 | listComments: listComments as any, 230 | deleteComment: jest.fn() as any, 231 | updateComment: jest.fn() as any, 232 | createComment: jest.fn() as any, 233 | }, 234 | pulls: { 235 | get: jest.fn() as any, 236 | }, 237 | repos: { 238 | createCommitStatus: jest.fn() as any, 239 | }, 240 | }, 241 | } as GithubClient; 242 | 243 | manager = new IssueManager(gh, repo, config); 244 | }); 245 | 246 | describe('updateCommitStatus', () => { 247 | const pr = { 248 | head: { 249 | sha: '', 250 | }, 251 | }; 252 | 253 | beforeEach(() => { 254 | ((gh.rest.pulls.get as unknown) as jest.Mock< 255 | any, 256 | any 257 | >).mockResolvedValue({ data: pr }); 258 | }); 259 | 260 | it('ignores non-PRs', async () => { 261 | const issue = {} as any; 262 | await manager.updateCommitStatus(issue, []); 263 | expect(gh.rest.pulls.get).not.toHaveBeenCalled(); 264 | expect(gh.rest.repos.createCommitStatus).not.toHaveBeenCalled(); 265 | }); 266 | 267 | it('sets the correct status on success', async () => { 268 | const issue = { number: 141, pull_request: {} } as any; 269 | 270 | await manager.updateCommitStatus(issue, []); 271 | 272 | expect(gh.rest.pulls.get).toHaveBeenCalledWith({ 273 | ...repo, 274 | pull_number: issue.number, 275 | }); 276 | 277 | expect(gh.rest.repos.createCommitStatus).toHaveBeenCalledWith({ 278 | ...repo, 279 | description: 'No dependencies', 280 | state: 'success', 281 | sha: pr.head.sha, 282 | context: config.actionName, 283 | }); 284 | }); 285 | 286 | it('sets the correct status on pending', async () => { 287 | const issue = { number: 141, pull_request: {} } as any; 288 | 289 | await manager.updateCommitStatus(issue, [ 290 | { repo: 'repo', owner: 'owner', number: 999, blocker: true }, 291 | { blocker: true } as any, 292 | { blocker: true } as any, 293 | ]); 294 | 295 | expect(gh.rest.pulls.get).toHaveBeenCalledWith({ 296 | ...repo, 297 | pull_number: issue.number, 298 | }); 299 | 300 | expect(gh.rest.repos.createCommitStatus).toHaveBeenCalledWith({ 301 | ...repo, 302 | description: 'Blocked by owner/repo#999 and 2 more issues', 303 | state: 'pending', 304 | sha: pr.head.sha, 305 | context: config.actionName, 306 | }); 307 | }); 308 | }); 309 | 310 | describe('writeComment', () => { 311 | const comments = [ 312 | { id: 1, body: 'Random text' }, 313 | { 314 | id: 2, 315 | body: ` Existing text\t\n${config.commentSignature}\n\n `, 316 | }, 317 | { id: 3, body: 'Random text' }, 318 | { id: 4, body: 'Random text' }, 319 | ]; 320 | 321 | beforeEach(() => { 322 | listComments.mockResolvedValue(comments); 323 | }); 324 | 325 | it('updates existing comment', async () => { 326 | const text = ' This is the updated text\n'; 327 | const issue = { number: 141 } as any; 328 | await manager.writeComment(issue, text); 329 | 330 | expect(gh.paginate).toHaveBeenCalled(); 331 | 332 | expect(gh.rest.issues.listComments).toHaveBeenCalledWith( 333 | expect.objectContaining({ 334 | ...repo, 335 | issue_number: issue.number, 336 | }) 337 | ); 338 | 339 | expect(gh.rest.issues.updateComment).toHaveBeenCalledWith({ 340 | ...repo, 341 | body: text.trim() + '\n' + config.commentSignature, 342 | comment_id: 2, 343 | }); 344 | 345 | expect(gh.rest.issues.deleteComment).not.toHaveBeenCalled(); 346 | expect(gh.rest.issues.createComment).not.toHaveBeenCalled(); 347 | }); 348 | 349 | it('creates a new comment if required', async () => { 350 | const text = ' This is the updated text\n'; 351 | const issue = { number: 141 } as any; 352 | await manager.writeComment(issue, text, true); 353 | 354 | expect(gh.paginate).toHaveBeenCalled(); 355 | 356 | expect(gh.rest.issues.listComments).toHaveBeenCalledWith({ 357 | ...repo, 358 | issue_number: issue.number, 359 | per_page: 100, 360 | }); 361 | 362 | expect(gh.rest.issues.deleteComment).toHaveBeenCalledWith({ 363 | ...repo, 364 | comment_id: 2, 365 | }); 366 | 367 | expect(gh.rest.issues.createComment).toHaveBeenCalledWith({ 368 | ...repo, 369 | issue_number: issue.number, 370 | body: text.trim() + '\n' + config.commentSignature, 371 | }); 372 | 373 | expect(gh.rest.issues.updateComment).not.toHaveBeenCalled(); 374 | }); 375 | 376 | it('exits early if the text is the same', async () => { 377 | const text = 'Existing text'; 378 | const issue = { number: 141 } as any; 379 | await manager.writeComment(issue, text); 380 | await manager.writeComment(issue, text, true); 381 | 382 | expect(gh.paginate).toHaveBeenCalled(); 383 | expect(gh.rest.issues.listComments).toHaveBeenCalledWith( 384 | expect.objectContaining({ 385 | ...repo, 386 | issue_number: issue.number, 387 | }) 388 | ); 389 | 390 | expect(gh.rest.issues.updateComment).not.toHaveBeenCalled(); 391 | expect(gh.rest.issues.deleteComment).not.toHaveBeenCalled(); 392 | expect(gh.rest.issues.createComment).not.toHaveBeenCalled(); 393 | }); 394 | }); 395 | }); 396 | -------------------------------------------------------------------------------- /dist/sourcemap-register.js: -------------------------------------------------------------------------------- 1 | (()=>{var e={650:e=>{var r=Object.prototype.toString;var n=typeof Buffer.alloc==="function"&&typeof Buffer.allocUnsafe==="function"&&typeof Buffer.from==="function";function isArrayBuffer(e){return r.call(e).slice(8,-1)==="ArrayBuffer"}function fromArrayBuffer(e,r,t){r>>>=0;var o=e.byteLength-r;if(o<0){throw new RangeError("'offset' is out of bounds")}if(t===undefined){t=o}else{t>>>=0;if(t>o){throw new RangeError("'length' is out of bounds")}}return n?Buffer.from(e.slice(r,r+t)):new Buffer(new Uint8Array(e.slice(r,r+t)))}function fromString(e,r){if(typeof r!=="string"||r===""){r="utf8"}if(!Buffer.isEncoding(r)){throw new TypeError('"encoding" must be a valid string encoding')}return n?Buffer.from(e,r):new Buffer(e,r)}function bufferFrom(e,r,t){if(typeof e==="number"){throw new TypeError('"value" argument must not be a number')}if(isArrayBuffer(e)){return fromArrayBuffer(e,r,t)}if(typeof e==="string"){return fromString(e,r)}return n?Buffer.from(e):new Buffer(e)}e.exports=bufferFrom},274:(e,r,n)=>{var t=n(339);var o=Object.prototype.hasOwnProperty;var i=typeof Map!=="undefined";function ArraySet(){this._array=[];this._set=i?new Map:Object.create(null)}ArraySet.fromArray=function ArraySet_fromArray(e,r){var n=new ArraySet;for(var t=0,o=e.length;t=0){return r}}else{var n=t.toSetString(e);if(o.call(this._set,n)){return this._set[n]}}throw new Error('"'+e+'" is not in the set.')};ArraySet.prototype.at=function ArraySet_at(e){if(e>=0&&e{var t=n(190);var o=5;var i=1<>1;return r?-n:n}r.encode=function base64VLQ_encode(e){var r="";var n;var i=toVLQSigned(e);do{n=i&a;i>>>=o;if(i>0){n|=u}r+=t.encode(n)}while(i>0);return r};r.decode=function base64VLQ_decode(e,r,n){var i=e.length;var s=0;var l=0;var c,p;do{if(r>=i){throw new Error("Expected more digits in base 64 VLQ value.")}p=t.decode(e.charCodeAt(r++));if(p===-1){throw new Error("Invalid base64 digit: "+e.charAt(r-1))}c=!!(p&u);p&=a;s=s+(p<{var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");r.encode=function(e){if(0<=e&&e{r.GREATEST_LOWER_BOUND=1;r.LEAST_UPPER_BOUND=2;function recursiveSearch(e,n,t,o,i,a){var u=Math.floor((n-e)/2)+e;var s=i(t,o[u],true);if(s===0){return u}else if(s>0){if(n-u>1){return recursiveSearch(u,n,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return n1){return recursiveSearch(e,u,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return u}else{return e<0?-1:e}}}r.search=function search(e,n,t,o){if(n.length===0){return-1}var i=recursiveSearch(-1,n.length,e,n,t,o||r.GREATEST_LOWER_BOUND);if(i<0){return-1}while(i-1>=0){if(t(n[i],n[i-1],true)!==0){break}--i}return i}},680:(e,r,n)=>{var t=n(339);function generatedPositionAfter(e,r){var n=e.generatedLine;var o=r.generatedLine;var i=e.generatedColumn;var a=r.generatedColumn;return o>n||o==n&&a>=i||t.compareByGeneratedPositionsInflated(e,r)<=0}function MappingList(){this._array=[];this._sorted=true;this._last={generatedLine:-1,generatedColumn:0}}MappingList.prototype.unsortedForEach=function MappingList_forEach(e,r){this._array.forEach(e,r)};MappingList.prototype.add=function MappingList_add(e){if(generatedPositionAfter(this._last,e)){this._last=e;this._array.push(e)}else{this._sorted=false;this._array.push(e)}};MappingList.prototype.toArray=function MappingList_toArray(){if(!this._sorted){this._array.sort(t.compareByGeneratedPositionsInflated);this._sorted=true}return this._array};r.H=MappingList},758:(e,r)=>{function swap(e,r,n){var t=e[r];e[r]=e[n];e[n]=t}function randomIntInRange(e,r){return Math.round(e+Math.random()*(r-e))}function doQuickSort(e,r,n,t){if(n{var t;var o=n(339);var i=n(345);var a=n(274).I;var u=n(449);var s=n(758).U;function SourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}return n.sections!=null?new IndexedSourceMapConsumer(n,r):new BasicSourceMapConsumer(n,r)}SourceMapConsumer.fromSourceMap=function(e,r){return BasicSourceMapConsumer.fromSourceMap(e,r)};SourceMapConsumer.prototype._version=3;SourceMapConsumer.prototype.__generatedMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_generatedMappings",{configurable:true,enumerable:true,get:function(){if(!this.__generatedMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__generatedMappings}});SourceMapConsumer.prototype.__originalMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_originalMappings",{configurable:true,enumerable:true,get:function(){if(!this.__originalMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__originalMappings}});SourceMapConsumer.prototype._charIsMappingSeparator=function SourceMapConsumer_charIsMappingSeparator(e,r){var n=e.charAt(r);return n===";"||n===","};SourceMapConsumer.prototype._parseMappings=function SourceMapConsumer_parseMappings(e,r){throw new Error("Subclasses must implement _parseMappings")};SourceMapConsumer.GENERATED_ORDER=1;SourceMapConsumer.ORIGINAL_ORDER=2;SourceMapConsumer.GREATEST_LOWER_BOUND=1;SourceMapConsumer.LEAST_UPPER_BOUND=2;SourceMapConsumer.prototype.eachMapping=function SourceMapConsumer_eachMapping(e,r,n){var t=r||null;var i=n||SourceMapConsumer.GENERATED_ORDER;var a;switch(i){case SourceMapConsumer.GENERATED_ORDER:a=this._generatedMappings;break;case SourceMapConsumer.ORIGINAL_ORDER:a=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var u=this.sourceRoot;a.map((function(e){var r=e.source===null?null:this._sources.at(e.source);r=o.computeSourceURL(u,r,this._sourceMapURL);return{source:r,generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:e.name===null?null:this._names.at(e.name)}}),this).forEach(e,t)};SourceMapConsumer.prototype.allGeneratedPositionsFor=function SourceMapConsumer_allGeneratedPositionsFor(e){var r=o.getArg(e,"line");var n={source:o.getArg(e,"source"),originalLine:r,originalColumn:o.getArg(e,"column",0)};n.source=this._findSourceIndex(n.source);if(n.source<0){return[]}var t=[];var a=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,i.LEAST_UPPER_BOUND);if(a>=0){var u=this._originalMappings[a];if(e.column===undefined){var s=u.originalLine;while(u&&u.originalLine===s){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}else{var l=u.originalColumn;while(u&&u.originalLine===r&&u.originalColumn==l){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}}return t};r.SourceMapConsumer=SourceMapConsumer;function BasicSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sources");var u=o.getArg(n,"names",[]);var s=o.getArg(n,"sourceRoot",null);var l=o.getArg(n,"sourcesContent",null);var c=o.getArg(n,"mappings");var p=o.getArg(n,"file",null);if(t!=this._version){throw new Error("Unsupported version: "+t)}if(s){s=o.normalize(s)}i=i.map(String).map(o.normalize).map((function(e){return s&&o.isAbsolute(s)&&o.isAbsolute(e)?o.relative(s,e):e}));this._names=a.fromArray(u.map(String),true);this._sources=a.fromArray(i,true);this._absoluteSources=this._sources.toArray().map((function(e){return o.computeSourceURL(s,e,r)}));this.sourceRoot=s;this.sourcesContent=l;this._mappings=c;this._sourceMapURL=r;this.file=p}BasicSourceMapConsumer.prototype=Object.create(SourceMapConsumer.prototype);BasicSourceMapConsumer.prototype.consumer=SourceMapConsumer;BasicSourceMapConsumer.prototype._findSourceIndex=function(e){var r=e;if(this.sourceRoot!=null){r=o.relative(this.sourceRoot,r)}if(this._sources.has(r)){return this._sources.indexOf(r)}var n;for(n=0;n1){v.source=l+_[1];l+=_[1];v.originalLine=i+_[2];i=v.originalLine;v.originalLine+=1;v.originalColumn=a+_[3];a=v.originalColumn;if(_.length>4){v.name=c+_[4];c+=_[4]}}m.push(v);if(typeof v.originalLine==="number"){d.push(v)}}}s(m,o.compareByGeneratedPositionsDeflated);this.__generatedMappings=m;s(d,o.compareByOriginalPositions);this.__originalMappings=d};BasicSourceMapConsumer.prototype._findMapping=function SourceMapConsumer_findMapping(e,r,n,t,o,a){if(e[n]<=0){throw new TypeError("Line must be greater than or equal to 1, got "+e[n])}if(e[t]<0){throw new TypeError("Column must be greater than or equal to 0, got "+e[t])}return i.search(e,r,o,a)};BasicSourceMapConsumer.prototype.computeColumnSpans=function SourceMapConsumer_computeColumnSpans(){for(var e=0;e=0){var t=this._generatedMappings[n];if(t.generatedLine===r.generatedLine){var i=o.getArg(t,"source",null);if(i!==null){i=this._sources.at(i);i=o.computeSourceURL(this.sourceRoot,i,this._sourceMapURL)}var a=o.getArg(t,"name",null);if(a!==null){a=this._names.at(a)}return{source:i,line:o.getArg(t,"originalLine",null),column:o.getArg(t,"originalColumn",null),name:a}}}return{source:null,line:null,column:null,name:null}};BasicSourceMapConsumer.prototype.hasContentsOfAllSources=function BasicSourceMapConsumer_hasContentsOfAllSources(){if(!this.sourcesContent){return false}return this.sourcesContent.length>=this._sources.size()&&!this.sourcesContent.some((function(e){return e==null}))};BasicSourceMapConsumer.prototype.sourceContentFor=function SourceMapConsumer_sourceContentFor(e,r){if(!this.sourcesContent){return null}var n=this._findSourceIndex(e);if(n>=0){return this.sourcesContent[n]}var t=e;if(this.sourceRoot!=null){t=o.relative(this.sourceRoot,t)}var i;if(this.sourceRoot!=null&&(i=o.urlParse(this.sourceRoot))){var a=t.replace(/^file:\/\//,"");if(i.scheme=="file"&&this._sources.has(a)){return this.sourcesContent[this._sources.indexOf(a)]}if((!i.path||i.path=="/")&&this._sources.has("/"+t)){return this.sourcesContent[this._sources.indexOf("/"+t)]}}if(r){return null}else{throw new Error('"'+t+'" is not in the SourceMap.')}};BasicSourceMapConsumer.prototype.generatedPositionFor=function SourceMapConsumer_generatedPositionFor(e){var r=o.getArg(e,"source");r=this._findSourceIndex(r);if(r<0){return{line:null,column:null,lastColumn:null}}var n={source:r,originalLine:o.getArg(e,"line"),originalColumn:o.getArg(e,"column")};var t=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,o.getArg(e,"bias",SourceMapConsumer.GREATEST_LOWER_BOUND));if(t>=0){var i=this._originalMappings[t];if(i.source===n.source){return{line:o.getArg(i,"generatedLine",null),column:o.getArg(i,"generatedColumn",null),lastColumn:o.getArg(i,"lastGeneratedColumn",null)}}}return{line:null,column:null,lastColumn:null}};t=BasicSourceMapConsumer;function IndexedSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sections");if(t!=this._version){throw new Error("Unsupported version: "+t)}this._sources=new a;this._names=new a;var u={line:-1,column:0};this._sections=i.map((function(e){if(e.url){throw new Error("Support for url field in sections not implemented.")}var n=o.getArg(e,"offset");var t=o.getArg(n,"line");var i=o.getArg(n,"column");if(t{var t=n(449);var o=n(339);var i=n(274).I;var a=n(680).H;function SourceMapGenerator(e){if(!e){e={}}this._file=o.getArg(e,"file",null);this._sourceRoot=o.getArg(e,"sourceRoot",null);this._skipValidation=o.getArg(e,"skipValidation",false);this._sources=new i;this._names=new i;this._mappings=new a;this._sourcesContents=null}SourceMapGenerator.prototype._version=3;SourceMapGenerator.fromSourceMap=function SourceMapGenerator_fromSourceMap(e){var r=e.sourceRoot;var n=new SourceMapGenerator({file:e.file,sourceRoot:r});e.eachMapping((function(e){var t={generated:{line:e.generatedLine,column:e.generatedColumn}};if(e.source!=null){t.source=e.source;if(r!=null){t.source=o.relative(r,t.source)}t.original={line:e.originalLine,column:e.originalColumn};if(e.name!=null){t.name=e.name}}n.addMapping(t)}));e.sources.forEach((function(t){var i=t;if(r!==null){i=o.relative(r,t)}if(!n._sources.has(i)){n._sources.add(i)}var a=e.sourceContentFor(t);if(a!=null){n.setSourceContent(t,a)}}));return n};SourceMapGenerator.prototype.addMapping=function SourceMapGenerator_addMapping(e){var r=o.getArg(e,"generated");var n=o.getArg(e,"original",null);var t=o.getArg(e,"source",null);var i=o.getArg(e,"name",null);if(!this._skipValidation){this._validateMapping(r,n,t,i)}if(t!=null){t=String(t);if(!this._sources.has(t)){this._sources.add(t)}}if(i!=null){i=String(i);if(!this._names.has(i)){this._names.add(i)}}this._mappings.add({generatedLine:r.line,generatedColumn:r.column,originalLine:n!=null&&n.line,originalColumn:n!=null&&n.column,source:t,name:i})};SourceMapGenerator.prototype.setSourceContent=function SourceMapGenerator_setSourceContent(e,r){var n=e;if(this._sourceRoot!=null){n=o.relative(this._sourceRoot,n)}if(r!=null){if(!this._sourcesContents){this._sourcesContents=Object.create(null)}this._sourcesContents[o.toSetString(n)]=r}else if(this._sourcesContents){delete this._sourcesContents[o.toSetString(n)];if(Object.keys(this._sourcesContents).length===0){this._sourcesContents=null}}};SourceMapGenerator.prototype.applySourceMap=function SourceMapGenerator_applySourceMap(e,r,n){var t=r;if(r==null){if(e.file==null){throw new Error("SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, "+'or the source map\'s "file" property. Both were omitted.')}t=e.file}var a=this._sourceRoot;if(a!=null){t=o.relative(a,t)}var u=new i;var s=new i;this._mappings.unsortedForEach((function(r){if(r.source===t&&r.originalLine!=null){var i=e.originalPositionFor({line:r.originalLine,column:r.originalColumn});if(i.source!=null){r.source=i.source;if(n!=null){r.source=o.join(n,r.source)}if(a!=null){r.source=o.relative(a,r.source)}r.originalLine=i.line;r.originalColumn=i.column;if(i.name!=null){r.name=i.name}}}var l=r.source;if(l!=null&&!u.has(l)){u.add(l)}var c=r.name;if(c!=null&&!s.has(c)){s.add(c)}}),this);this._sources=u;this._names=s;e.sources.forEach((function(r){var t=e.sourceContentFor(r);if(t!=null){if(n!=null){r=o.join(n,r)}if(a!=null){r=o.relative(a,r)}this.setSourceContent(r,t)}}),this)};SourceMapGenerator.prototype._validateMapping=function SourceMapGenerator_validateMapping(e,r,n,t){if(r&&typeof r.line!=="number"&&typeof r.column!=="number"){throw new Error("original.line and original.column are not numbers -- you probably meant to omit "+"the original mapping entirely and only map the generated position. If so, pass "+"null for the original mapping instead of an object with empty or null values.")}if(e&&"line"in e&&"column"in e&&e.line>0&&e.column>=0&&!r&&!n&&!t){return}else if(e&&"line"in e&&"column"in e&&r&&"line"in r&&"column"in r&&e.line>0&&e.column>=0&&r.line>0&&r.column>=0&&n){return}else{throw new Error("Invalid mapping: "+JSON.stringify({generated:e,source:n,original:r,name:t}))}};SourceMapGenerator.prototype._serializeMappings=function SourceMapGenerator_serializeMappings(){var e=0;var r=1;var n=0;var i=0;var a=0;var u=0;var s="";var l;var c;var p;var f;var g=this._mappings.toArray();for(var h=0,d=g.length;h0){if(!o.compareByGeneratedPositionsInflated(c,g[h-1])){continue}l+=","}}l+=t.encode(c.generatedColumn-e);e=c.generatedColumn;if(c.source!=null){f=this._sources.indexOf(c.source);l+=t.encode(f-u);u=f;l+=t.encode(c.originalLine-1-i);i=c.originalLine-1;l+=t.encode(c.originalColumn-n);n=c.originalColumn;if(c.name!=null){p=this._names.indexOf(c.name);l+=t.encode(p-a);a=p}}s+=l}return s};SourceMapGenerator.prototype._generateSourcesContent=function SourceMapGenerator_generateSourcesContent(e,r){return e.map((function(e){if(!this._sourcesContents){return null}if(r!=null){e=o.relative(r,e)}var n=o.toSetString(e);return Object.prototype.hasOwnProperty.call(this._sourcesContents,n)?this._sourcesContents[n]:null}),this)};SourceMapGenerator.prototype.toJSON=function SourceMapGenerator_toJSON(){var e={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};if(this._file!=null){e.file=this._file}if(this._sourceRoot!=null){e.sourceRoot=this._sourceRoot}if(this._sourcesContents){e.sourcesContent=this._generateSourcesContent(e.sources,e.sourceRoot)}return e};SourceMapGenerator.prototype.toString=function SourceMapGenerator_toString(){return JSON.stringify(this.toJSON())};r.h=SourceMapGenerator},351:(e,r,n)=>{var t;var o=n(591).h;var i=n(339);var a=/(\r?\n)/;var u=10;var s="$$$isSourceNode$$$";function SourceNode(e,r,n,t,o){this.children=[];this.sourceContents={};this.line=e==null?null:e;this.column=r==null?null:r;this.source=n==null?null:n;this.name=o==null?null:o;this[s]=true;if(t!=null)this.add(t)}SourceNode.fromStringWithSourceMap=function SourceNode_fromStringWithSourceMap(e,r,n){var t=new SourceNode;var o=e.split(a);var u=0;var shiftNextLine=function(){var e=getNextLine();var r=getNextLine()||"";return e+r;function getNextLine(){return u=0;r--){this.prepend(e[r])}}else if(e[s]||typeof e==="string"){this.children.unshift(e)}else{throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+e)}return this};SourceNode.prototype.walk=function SourceNode_walk(e){var r;for(var n=0,t=this.children.length;n0){r=[];for(n=0;n{function getArg(e,r,n){if(r in e){return e[r]}else if(arguments.length===3){return n}else{throw new Error('"'+r+'" is a required argument.')}}r.getArg=getArg;var n=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;var t=/^data:.+\,.+$/;function urlParse(e){var r=e.match(n);if(!r){return null}return{scheme:r[1],auth:r[2],host:r[3],port:r[4],path:r[5]}}r.urlParse=urlParse;function urlGenerate(e){var r="";if(e.scheme){r+=e.scheme+":"}r+="//";if(e.auth){r+=e.auth+"@"}if(e.host){r+=e.host}if(e.port){r+=":"+e.port}if(e.path){r+=e.path}return r}r.urlGenerate=urlGenerate;function normalize(e){var n=e;var t=urlParse(e);if(t){if(!t.path){return e}n=t.path}var o=r.isAbsolute(n);var i=n.split(/\/+/);for(var a,u=0,s=i.length-1;s>=0;s--){a=i[s];if(a==="."){i.splice(s,1)}else if(a===".."){u++}else if(u>0){if(a===""){i.splice(s+1,u);u=0}else{i.splice(s,2);u--}}}n=i.join("/");if(n===""){n=o?"/":"."}if(t){t.path=n;return urlGenerate(t)}return n}r.normalize=normalize;function join(e,r){if(e===""){e="."}if(r===""){r="."}var n=urlParse(r);var o=urlParse(e);if(o){e=o.path||"/"}if(n&&!n.scheme){if(o){n.scheme=o.scheme}return urlGenerate(n)}if(n||r.match(t)){return r}if(o&&!o.host&&!o.path){o.host=r;return urlGenerate(o)}var i=r.charAt(0)==="/"?r:normalize(e.replace(/\/+$/,"")+"/"+r);if(o){o.path=i;return urlGenerate(o)}return i}r.join=join;r.isAbsolute=function(e){return e.charAt(0)==="/"||n.test(e)};function relative(e,r){if(e===""){e="."}e=e.replace(/\/$/,"");var n=0;while(r.indexOf(e+"/")!==0){var t=e.lastIndexOf("/");if(t<0){return r}e=e.slice(0,t);if(e.match(/^([^\/]+:\/)?\/*$/)){return r}++n}return Array(n+1).join("../")+r.substr(e.length+1)}r.relative=relative;var o=function(){var e=Object.create(null);return!("__proto__"in e)}();function identity(e){return e}function toSetString(e){if(isProtoString(e)){return"$"+e}return e}r.toSetString=o?identity:toSetString;function fromSetString(e){if(isProtoString(e)){return e.slice(1)}return e}r.fromSetString=o?identity:fromSetString;function isProtoString(e){if(!e){return false}var r=e.length;if(r<9){return false}if(e.charCodeAt(r-1)!==95||e.charCodeAt(r-2)!==95||e.charCodeAt(r-3)!==111||e.charCodeAt(r-4)!==116||e.charCodeAt(r-5)!==111||e.charCodeAt(r-6)!==114||e.charCodeAt(r-7)!==112||e.charCodeAt(r-8)!==95||e.charCodeAt(r-9)!==95){return false}for(var n=r-10;n>=0;n--){if(e.charCodeAt(n)!==36){return false}}return true}function compareByOriginalPositions(e,r,n){var t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0||n){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0){return t}t=e.generatedLine-r.generatedLine;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByOriginalPositions=compareByOriginalPositions;function compareByGeneratedPositionsDeflated(e,r,n){var t=e.generatedLine-r.generatedLine;if(t!==0){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0||n){return t}t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsDeflated=compareByGeneratedPositionsDeflated;function strcmp(e,r){if(e===r){return 0}if(e===null){return 1}if(r===null){return-1}if(e>r){return 1}return-1}function compareByGeneratedPositionsInflated(e,r){var n=e.generatedLine-r.generatedLine;if(n!==0){return n}n=e.generatedColumn-r.generatedColumn;if(n!==0){return n}n=strcmp(e.source,r.source);if(n!==0){return n}n=e.originalLine-r.originalLine;if(n!==0){return n}n=e.originalColumn-r.originalColumn;if(n!==0){return n}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsInflated=compareByGeneratedPositionsInflated;function parseSourceMapInput(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))}r.parseSourceMapInput=parseSourceMapInput;function computeSourceURL(e,r,n){r=r||"";if(e){if(e[e.length-1]!=="/"&&r[0]!=="/"){e+="/"}r=e+r}if(n){var t=urlParse(n);if(!t){throw new Error("sourceMapURL could not be parsed")}if(t.path){var o=t.path.lastIndexOf("/");if(o>=0){t.path=t.path.substring(0,o+1)}}r=join(urlGenerate(t),r)}return normalize(r)}r.computeSourceURL=computeSourceURL},997:(e,r,n)=>{n(591).h;r.SourceMapConsumer=n(952).SourceMapConsumer;n(351)},284:(e,r,n)=>{e=n.nmd(e);var t=n(997).SourceMapConsumer;var o=n(17);var i;try{i=n(147);if(!i.existsSync||!i.readFileSync){i=null}}catch(e){}var a=n(650);function dynamicRequire(e,r){return e.require(r)}var u=false;var s=false;var l=false;var c="auto";var p={};var f={};var g=/^data:application\/json[^,]+base64,/;var h=[];var d=[];function isInBrowser(){if(c==="browser")return true;if(c==="node")return false;return typeof window!=="undefined"&&typeof XMLHttpRequest==="function"&&!(window.require&&window.module&&window.process&&window.process.type==="renderer")}function hasGlobalProcessEventEmitter(){return typeof process==="object"&&process!==null&&typeof process.on==="function"}function globalProcessVersion(){if(typeof process==="object"&&process!==null){return process.version}else{return""}}function globalProcessStderr(){if(typeof process==="object"&&process!==null){return process.stderr}}function globalProcessExit(e){if(typeof process==="object"&&process!==null&&typeof process.exit==="function"){return process.exit(e)}}function handlerExec(e){return function(r){for(var n=0;n"}var n=this.getLineNumber();if(n!=null){r+=":"+n;var t=this.getColumnNumber();if(t){r+=":"+t}}}var o="";var i=this.getFunctionName();var a=true;var u=this.isConstructor();var s=!(this.isToplevel()||u);if(s){var l=this.getTypeName();if(l==="[object Object]"){l="null"}var c=this.getMethodName();if(i){if(l&&i.indexOf(l)!=0){o+=l+"."}o+=i;if(c&&i.indexOf("."+c)!=i.length-c.length-1){o+=" [as "+c+"]"}}else{o+=l+"."+(c||"")}}else if(u){o+="new "+(i||"")}else if(i){o+=i}else{o+=r;a=false}if(a){o+=" ("+r+")"}return o}function cloneCallSite(e){var r={};Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach((function(n){r[n]=/^(?:is|get)/.test(n)?function(){return e[n].call(e)}:e[n]}));r.toString=CallSiteToString;return r}function wrapCallSite(e,r){if(r===undefined){r={nextPosition:null,curPosition:null}}if(e.isNative()){r.curPosition=null;return e}var n=e.getFileName()||e.getScriptNameOrSourceURL();if(n){var t=e.getLineNumber();var o=e.getColumnNumber()-1;var i=/^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/;var a=i.test(globalProcessVersion())?0:62;if(t===1&&o>a&&!isInBrowser()&&!e.isEval()){o-=a}var u=mapSourcePosition({source:n,line:t,column:o});r.curPosition=u;e=cloneCallSite(e);var s=e.getFunctionName;e.getFunctionName=function(){if(r.nextPosition==null){return s()}return r.nextPosition.name||s()};e.getFileName=function(){return u.source};e.getLineNumber=function(){return u.line};e.getColumnNumber=function(){return u.column+1};e.getScriptNameOrSourceURL=function(){return u.source};return e}var l=e.isEval()&&e.getEvalOrigin();if(l){l=mapEvalOrigin(l);e=cloneCallSite(e);e.getEvalOrigin=function(){return l};return e}return e}function prepareStackTrace(e,r){if(l){p={};f={}}var n=e.name||"Error";var t=e.message||"";var o=n+": "+t;var i={nextPosition:null,curPosition:null};var a=[];for(var u=r.length-1;u>=0;u--){a.push("\n at "+wrapCallSite(r[u],i));i.nextPosition=i.curPosition}i.curPosition=i.nextPosition=null;return o+a.reverse().join("")}function getErrorSource(e){var r=/\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(e.stack);if(r){var n=r[1];var t=+r[2];var o=+r[3];var a=p[n];if(!a&&i&&i.existsSync(n)){try{a=i.readFileSync(n,"utf8")}catch(e){a=""}}if(a){var u=a.split(/(?:\r\n|\r|\n)/)[t-1];if(u){return n+":"+t+"\n"+u+"\n"+new Array(o).join(" ")+"^"}}}return null}function printErrorAndExit(e){var r=getErrorSource(e);var n=globalProcessStderr();if(n&&n._handle&&n._handle.setBlocking){n._handle.setBlocking(true)}if(r){console.error();console.error(r)}console.error(e.stack);globalProcessExit(1)}function shimEmitUncaughtException(){var e=process.emit;process.emit=function(r){if(r==="uncaughtException"){var n=arguments[1]&&arguments[1].stack;var t=this.listeners(r).length>0;if(n&&!t){return printErrorAndExit(arguments[1])}}return e.apply(this,arguments)}}var S=h.slice(0);var _=d.slice(0);r.wrapCallSite=wrapCallSite;r.getErrorSource=getErrorSource;r.mapSourcePosition=mapSourcePosition;r.retrieveSourceMap=v;r.install=function(r){r=r||{};if(r.environment){c=r.environment;if(["node","browser","auto"].indexOf(c)===-1){throw new Error("environment "+c+" was unknown. Available options are {auto, browser, node}")}}if(r.retrieveFile){if(r.overrideRetrieveFile){h.length=0}h.unshift(r.retrieveFile)}if(r.retrieveSourceMap){if(r.overrideRetrieveSourceMap){d.length=0}d.unshift(r.retrieveSourceMap)}if(r.hookRequire&&!isInBrowser()){var n=dynamicRequire(e,"module");var t=n.prototype._compile;if(!t.__sourceMapSupport){n.prototype._compile=function(e,r){p[r]=e;f[r]=undefined;return t.call(this,e,r)};n.prototype._compile.__sourceMapSupport=true}}if(!l){l="emptyCacheBetweenOperations"in r?r.emptyCacheBetweenOperations:false}if(!u){u=true;Error.prepareStackTrace=prepareStackTrace}if(!s){var o="handleUncaughtExceptions"in r?r.handleUncaughtExceptions:true;try{var i=dynamicRequire(e,"worker_threads");if(i.isMainThread===false){o=false}}catch(e){}if(o&&hasGlobalProcessEventEmitter()){s=true;shimEmitUncaughtException()}}};r.resetRetrieveHandlers=function(){h.length=0;d.length=0;h=S.slice(0);d=_.slice(0);v=handlerExec(d);m=handlerExec(h)}},147:e=>{"use strict";e.exports=require("fs")},17:e=>{"use strict";e.exports=require("path")}};var r={};function __webpack_require__(n){var t=r[n];if(t!==undefined){return t.exports}var o=r[n]={id:n,loaded:false,exports:{}};var i=true;try{e[n](o,o.exports,__webpack_require__);i=false}finally{if(i)delete r[n]}o.loaded=true;return o.exports}(()=>{__webpack_require__.nmd=e=>{e.paths=[];if(!e.children)e.children=[];return e}})();if(typeof __webpack_require__!=="undefined")__webpack_require__.ab=__dirname+"/";var n={};(()=>{__webpack_require__(284).install()})();module.exports=n})(); -------------------------------------------------------------------------------- /dist/licenses.txt: -------------------------------------------------------------------------------- 1 | @actions/core 2 | MIT 3 | The MIT License (MIT) 4 | 5 | Copyright 2019 GitHub 6 | 7 | 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: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | 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. 12 | 13 | @actions/github 14 | MIT 15 | The MIT License (MIT) 16 | 17 | Copyright 2019 GitHub 18 | 19 | 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: 20 | 21 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 22 | 23 | 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. 24 | 25 | @actions/http-client 26 | MIT 27 | Actions Http Client for Node.js 28 | 29 | Copyright (c) GitHub, Inc. 30 | 31 | All rights reserved. 32 | 33 | MIT License 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 36 | associated documentation files (the "Software"), to deal in the Software without restriction, 37 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 38 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 39 | subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 44 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 45 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 46 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 47 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | 50 | @octokit/auth-token 51 | MIT 52 | The MIT License 53 | 54 | Copyright (c) 2019 Octokit contributors 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining a copy 57 | of this software and associated documentation files (the "Software"), to deal 58 | in the Software without restriction, including without limitation the rights 59 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 60 | copies of the Software, and to permit persons to whom the Software is 61 | furnished to do so, subject to the following conditions: 62 | 63 | The above copyright notice and this permission notice shall be included in 64 | all copies or substantial portions of the Software. 65 | 66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 67 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 68 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 69 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 70 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 71 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 72 | THE SOFTWARE. 73 | 74 | 75 | @octokit/core 76 | MIT 77 | The MIT License 78 | 79 | Copyright (c) 2019 Octokit contributors 80 | 81 | Permission is hereby granted, free of charge, to any person obtaining a copy 82 | of this software and associated documentation files (the "Software"), to deal 83 | in the Software without restriction, including without limitation the rights 84 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 85 | copies of the Software, and to permit persons to whom the Software is 86 | furnished to do so, subject to the following conditions: 87 | 88 | The above copyright notice and this permission notice shall be included in 89 | all copies or substantial portions of the Software. 90 | 91 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 92 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 93 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 94 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 95 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 96 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 97 | THE SOFTWARE. 98 | 99 | 100 | @octokit/endpoint 101 | MIT 102 | The MIT License 103 | 104 | Copyright (c) 2018 Octokit contributors 105 | 106 | Permission is hereby granted, free of charge, to any person obtaining a copy 107 | of this software and associated documentation files (the "Software"), to deal 108 | in the Software without restriction, including without limitation the rights 109 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 110 | copies of the Software, and to permit persons to whom the Software is 111 | furnished to do so, subject to the following conditions: 112 | 113 | The above copyright notice and this permission notice shall be included in 114 | all copies or substantial portions of the Software. 115 | 116 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 117 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 118 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 119 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 120 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 121 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 122 | THE SOFTWARE. 123 | 124 | 125 | @octokit/graphql 126 | MIT 127 | The MIT License 128 | 129 | Copyright (c) 2018 Octokit contributors 130 | 131 | Permission is hereby granted, free of charge, to any person obtaining a copy 132 | of this software and associated documentation files (the "Software"), to deal 133 | in the Software without restriction, including without limitation the rights 134 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 135 | copies of the Software, and to permit persons to whom the Software is 136 | furnished to do so, subject to the following conditions: 137 | 138 | The above copyright notice and this permission notice shall be included in 139 | all copies or substantial portions of the Software. 140 | 141 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 142 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 143 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 144 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 145 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 146 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 147 | THE SOFTWARE. 148 | 149 | 150 | @octokit/plugin-paginate-rest 151 | MIT 152 | MIT License Copyright (c) 2019 Octokit contributors 153 | 154 | 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: 155 | 156 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 157 | 158 | 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. 159 | 160 | 161 | @octokit/plugin-rest-endpoint-methods 162 | MIT 163 | MIT License Copyright (c) 2019 Octokit contributors 164 | 165 | 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: 166 | 167 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 168 | 169 | 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. 170 | 171 | 172 | @octokit/request 173 | MIT 174 | The MIT License 175 | 176 | Copyright (c) 2018 Octokit contributors 177 | 178 | Permission is hereby granted, free of charge, to any person obtaining a copy 179 | of this software and associated documentation files (the "Software"), to deal 180 | in the Software without restriction, including without limitation the rights 181 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 182 | copies of the Software, and to permit persons to whom the Software is 183 | furnished to do so, subject to the following conditions: 184 | 185 | The above copyright notice and this permission notice shall be included in 186 | all copies or substantial portions of the Software. 187 | 188 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 189 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 190 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 191 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 192 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 193 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 194 | THE SOFTWARE. 195 | 196 | 197 | @octokit/request-error 198 | MIT 199 | The MIT License 200 | 201 | Copyright (c) 2019 Octokit contributors 202 | 203 | Permission is hereby granted, free of charge, to any person obtaining a copy 204 | of this software and associated documentation files (the "Software"), to deal 205 | in the Software without restriction, including without limitation the rights 206 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 207 | copies of the Software, and to permit persons to whom the Software is 208 | furnished to do so, subject to the following conditions: 209 | 210 | The above copyright notice and this permission notice shall be included in 211 | all copies or substantial portions of the Software. 212 | 213 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 214 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 215 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 216 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 217 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 218 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 219 | THE SOFTWARE. 220 | 221 | 222 | @vercel/ncc 223 | MIT 224 | Copyright 2018 ZEIT, Inc. 225 | 226 | 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: 227 | 228 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 229 | 230 | 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. 231 | 232 | before-after-hook 233 | Apache-2.0 234 | Apache License 235 | Version 2.0, January 2004 236 | http://www.apache.org/licenses/ 237 | 238 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 239 | 240 | 1. Definitions. 241 | 242 | "License" shall mean the terms and conditions for use, reproduction, 243 | and distribution as defined by Sections 1 through 9 of this document. 244 | 245 | "Licensor" shall mean the copyright owner or entity authorized by 246 | the copyright owner that is granting the License. 247 | 248 | "Legal Entity" shall mean the union of the acting entity and all 249 | other entities that control, are controlled by, or are under common 250 | control with that entity. For the purposes of this definition, 251 | "control" means (i) the power, direct or indirect, to cause the 252 | direction or management of such entity, whether by contract or 253 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 254 | outstanding shares, or (iii) beneficial ownership of such entity. 255 | 256 | "You" (or "Your") shall mean an individual or Legal Entity 257 | exercising permissions granted by this License. 258 | 259 | "Source" form shall mean the preferred form for making modifications, 260 | including but not limited to software source code, documentation 261 | source, and configuration files. 262 | 263 | "Object" form shall mean any form resulting from mechanical 264 | transformation or translation of a Source form, including but 265 | not limited to compiled object code, generated documentation, 266 | and conversions to other media types. 267 | 268 | "Work" shall mean the work of authorship, whether in Source or 269 | Object form, made available under the License, as indicated by a 270 | copyright notice that is included in or attached to the work 271 | (an example is provided in the Appendix below). 272 | 273 | "Derivative Works" shall mean any work, whether in Source or Object 274 | form, that is based on (or derived from) the Work and for which the 275 | editorial revisions, annotations, elaborations, or other modifications 276 | represent, as a whole, an original work of authorship. For the purposes 277 | of this License, Derivative Works shall not include works that remain 278 | separable from, or merely link (or bind by name) to the interfaces of, 279 | the Work and Derivative Works thereof. 280 | 281 | "Contribution" shall mean any work of authorship, including 282 | the original version of the Work and any modifications or additions 283 | to that Work or Derivative Works thereof, that is intentionally 284 | submitted to Licensor for inclusion in the Work by the copyright owner 285 | or by an individual or Legal Entity authorized to submit on behalf of 286 | the copyright owner. For the purposes of this definition, "submitted" 287 | means any form of electronic, verbal, or written communication sent 288 | to the Licensor or its representatives, including but not limited to 289 | communication on electronic mailing lists, source code control systems, 290 | and issue tracking systems that are managed by, or on behalf of, the 291 | Licensor for the purpose of discussing and improving the Work, but 292 | excluding communication that is conspicuously marked or otherwise 293 | designated in writing by the copyright owner as "Not a Contribution." 294 | 295 | "Contributor" shall mean Licensor and any individual or Legal Entity 296 | on behalf of whom a Contribution has been received by Licensor and 297 | subsequently incorporated within the Work. 298 | 299 | 2. Grant of Copyright License. Subject to the terms and conditions of 300 | this License, each Contributor hereby grants to You a perpetual, 301 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 302 | copyright license to reproduce, prepare Derivative Works of, 303 | publicly display, publicly perform, sublicense, and distribute the 304 | Work and such Derivative Works in Source or Object form. 305 | 306 | 3. Grant of Patent License. Subject to the terms and conditions of 307 | this License, each Contributor hereby grants to You a perpetual, 308 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 309 | (except as stated in this section) patent license to make, have made, 310 | use, offer to sell, sell, import, and otherwise transfer the Work, 311 | where such license applies only to those patent claims licensable 312 | by such Contributor that are necessarily infringed by their 313 | Contribution(s) alone or by combination of their Contribution(s) 314 | with the Work to which such Contribution(s) was submitted. If You 315 | institute patent litigation against any entity (including a 316 | cross-claim or counterclaim in a lawsuit) alleging that the Work 317 | or a Contribution incorporated within the Work constitutes direct 318 | or contributory patent infringement, then any patent licenses 319 | granted to You under this License for that Work shall terminate 320 | as of the date such litigation is filed. 321 | 322 | 4. Redistribution. You may reproduce and distribute copies of the 323 | Work or Derivative Works thereof in any medium, with or without 324 | modifications, and in Source or Object form, provided that You 325 | meet the following conditions: 326 | 327 | (a) You must give any other recipients of the Work or 328 | Derivative Works a copy of this License; and 329 | 330 | (b) You must cause any modified files to carry prominent notices 331 | stating that You changed the files; and 332 | 333 | (c) You must retain, in the Source form of any Derivative Works 334 | that You distribute, all copyright, patent, trademark, and 335 | attribution notices from the Source form of the Work, 336 | excluding those notices that do not pertain to any part of 337 | the Derivative Works; and 338 | 339 | (d) If the Work includes a "NOTICE" text file as part of its 340 | distribution, then any Derivative Works that You distribute must 341 | include a readable copy of the attribution notices contained 342 | within such NOTICE file, excluding those notices that do not 343 | pertain to any part of the Derivative Works, in at least one 344 | of the following places: within a NOTICE text file distributed 345 | as part of the Derivative Works; within the Source form or 346 | documentation, if provided along with the Derivative Works; or, 347 | within a display generated by the Derivative Works, if and 348 | wherever such third-party notices normally appear. The contents 349 | of the NOTICE file are for informational purposes only and 350 | do not modify the License. You may add Your own attribution 351 | notices within Derivative Works that You distribute, alongside 352 | or as an addendum to the NOTICE text from the Work, provided 353 | that such additional attribution notices cannot be construed 354 | as modifying the License. 355 | 356 | You may add Your own copyright statement to Your modifications and 357 | may provide additional or different license terms and conditions 358 | for use, reproduction, or distribution of Your modifications, or 359 | for any such Derivative Works as a whole, provided Your use, 360 | reproduction, and distribution of the Work otherwise complies with 361 | the conditions stated in this License. 362 | 363 | 5. Submission of Contributions. Unless You explicitly state otherwise, 364 | any Contribution intentionally submitted for inclusion in the Work 365 | by You to the Licensor shall be under the terms and conditions of 366 | this License, without any additional terms or conditions. 367 | Notwithstanding the above, nothing herein shall supersede or modify 368 | the terms of any separate license agreement you may have executed 369 | with Licensor regarding such Contributions. 370 | 371 | 6. Trademarks. This License does not grant permission to use the trade 372 | names, trademarks, service marks, or product names of the Licensor, 373 | except as required for reasonable and customary use in describing the 374 | origin of the Work and reproducing the content of the NOTICE file. 375 | 376 | 7. Disclaimer of Warranty. Unless required by applicable law or 377 | agreed to in writing, Licensor provides the Work (and each 378 | Contributor provides its Contributions) on an "AS IS" BASIS, 379 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 380 | implied, including, without limitation, any warranties or conditions 381 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 382 | PARTICULAR PURPOSE. You are solely responsible for determining the 383 | appropriateness of using or redistributing the Work and assume any 384 | risks associated with Your exercise of permissions under this License. 385 | 386 | 8. Limitation of Liability. In no event and under no legal theory, 387 | whether in tort (including negligence), contract, or otherwise, 388 | unless required by applicable law (such as deliberate and grossly 389 | negligent acts) or agreed to in writing, shall any Contributor be 390 | liable to You for damages, including any direct, indirect, special, 391 | incidental, or consequential damages of any character arising as a 392 | result of this License or out of the use or inability to use the 393 | Work (including but not limited to damages for loss of goodwill, 394 | work stoppage, computer failure or malfunction, or any and all 395 | other commercial damages or losses), even if such Contributor 396 | has been advised of the possibility of such damages. 397 | 398 | 9. Accepting Warranty or Additional Liability. While redistributing 399 | the Work or Derivative Works thereof, You may choose to offer, 400 | and charge a fee for, acceptance of support, warranty, indemnity, 401 | or other liability obligations and/or rights consistent with this 402 | License. However, in accepting such obligations, You may act only 403 | on Your own behalf and on Your sole responsibility, not on behalf 404 | of any other Contributor, and only if You agree to indemnify, 405 | defend, and hold each Contributor harmless for any liability 406 | incurred by, or claims asserted against, such Contributor by reason 407 | of your accepting any such warranty or additional liability. 408 | 409 | END OF TERMS AND CONDITIONS 410 | 411 | APPENDIX: How to apply the Apache License to your work. 412 | 413 | To apply the Apache License to your work, attach the following 414 | boilerplate notice, with the fields enclosed by brackets "{}" 415 | replaced with your own identifying information. (Don't include 416 | the brackets!) The text should be enclosed in the appropriate 417 | comment syntax for the file format. We also recommend that a 418 | file or class name and description of purpose be included on the 419 | same "printed page" as the copyright notice for easier 420 | identification within third-party archives. 421 | 422 | Copyright 2018 Gregor Martynus and other contributors. 423 | 424 | Licensed under the Apache License, Version 2.0 (the "License"); 425 | you may not use this file except in compliance with the License. 426 | You may obtain a copy of the License at 427 | 428 | http://www.apache.org/licenses/LICENSE-2.0 429 | 430 | Unless required by applicable law or agreed to in writing, software 431 | distributed under the License is distributed on an "AS IS" BASIS, 432 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 433 | See the License for the specific language governing permissions and 434 | limitations under the License. 435 | 436 | 437 | deprecation 438 | ISC 439 | The ISC License 440 | 441 | Copyright (c) Gregor Martynus and contributors 442 | 443 | Permission to use, copy, modify, and/or distribute this software for any 444 | purpose with or without fee is hereby granted, provided that the above 445 | copyright notice and this permission notice appear in all copies. 446 | 447 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 448 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 449 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 450 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 451 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 452 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 453 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 454 | 455 | 456 | dequal 457 | MIT 458 | The MIT License (MIT) 459 | 460 | Copyright (c) Luke Edwards (lukeed.com) 461 | 462 | Permission is hereby granted, free of charge, to any person obtaining a copy 463 | of this software and associated documentation files (the "Software"), to deal 464 | in the Software without restriction, including without limitation the rights 465 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 466 | copies of the Software, and to permit persons to whom the Software is 467 | furnished to do so, subject to the following conditions: 468 | 469 | The above copyright notice and this permission notice shall be included in 470 | all copies or substantial portions of the Software. 471 | 472 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 473 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 474 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 475 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 476 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 477 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 478 | THE SOFTWARE. 479 | 480 | 481 | is-plain-object 482 | MIT 483 | The MIT License (MIT) 484 | 485 | Copyright (c) 2014-2017, Jon Schlinkert. 486 | 487 | Permission is hereby granted, free of charge, to any person obtaining a copy 488 | of this software and associated documentation files (the "Software"), to deal 489 | in the Software without restriction, including without limitation the rights 490 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 491 | copies of the Software, and to permit persons to whom the Software is 492 | furnished to do so, subject to the following conditions: 493 | 494 | The above copyright notice and this permission notice shall be included in 495 | all copies or substantial portions of the Software. 496 | 497 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 498 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 499 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 500 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 501 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 502 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 503 | THE SOFTWARE. 504 | 505 | 506 | issue-regex 507 | MIT 508 | MIT License 509 | 510 | Copyright (c) Sindre Sorhus (sindresorhus.com) 511 | 512 | 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: 513 | 514 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 515 | 516 | 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. 517 | 518 | 519 | lodash.uniqby 520 | MIT 521 | Copyright jQuery Foundation and other contributors 522 | 523 | Based on Underscore.js, copyright Jeremy Ashkenas, 524 | DocumentCloud and Investigative Reporters & Editors 525 | 526 | This software consists of voluntary contributions made by many 527 | individuals. For exact contribution history, see the revision history 528 | available at https://github.com/lodash/lodash 529 | 530 | The following license applies to all parts of this software except as 531 | documented below: 532 | 533 | ==== 534 | 535 | Permission is hereby granted, free of charge, to any person obtaining 536 | a copy of this software and associated documentation files (the 537 | "Software"), to deal in the Software without restriction, including 538 | without limitation the rights to use, copy, modify, merge, publish, 539 | distribute, sublicense, and/or sell copies of the Software, and to 540 | permit persons to whom the Software is furnished to do so, subject to 541 | the following conditions: 542 | 543 | The above copyright notice and this permission notice shall be 544 | included in all copies or substantial portions of the Software. 545 | 546 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 547 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 548 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 549 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 550 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 551 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 552 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 553 | 554 | ==== 555 | 556 | Copyright and related rights for sample code are waived via CC0. Sample 557 | code is defined as all source code displayed within the prose of the 558 | documentation. 559 | 560 | CC0: http://creativecommons.org/publicdomain/zero/1.0/ 561 | 562 | ==== 563 | 564 | Files located in the node_modules and vendor directories are externally 565 | maintained libraries used by this software which have their own 566 | licenses; we recommend you read them, as their terms may differ from the 567 | terms above. 568 | 569 | 570 | node-fetch 571 | MIT 572 | The MIT License (MIT) 573 | 574 | Copyright (c) 2016 David Frank 575 | 576 | Permission is hereby granted, free of charge, to any person obtaining a copy 577 | of this software and associated documentation files (the "Software"), to deal 578 | in the Software without restriction, including without limitation the rights 579 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 580 | copies of the Software, and to permit persons to whom the Software is 581 | furnished to do so, subject to the following conditions: 582 | 583 | The above copyright notice and this permission notice shall be included in all 584 | copies or substantial portions of the Software. 585 | 586 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 587 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 588 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 589 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 590 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 591 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 592 | SOFTWARE. 593 | 594 | 595 | 596 | once 597 | ISC 598 | The ISC License 599 | 600 | Copyright (c) Isaac Z. Schlueter and Contributors 601 | 602 | Permission to use, copy, modify, and/or distribute this software for any 603 | purpose with or without fee is hereby granted, provided that the above 604 | copyright notice and this permission notice appear in all copies. 605 | 606 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 607 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 608 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 609 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 610 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 611 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 612 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 613 | 614 | 615 | tr46 616 | MIT 617 | 618 | tunnel 619 | MIT 620 | The MIT License (MIT) 621 | 622 | Copyright (c) 2012 Koichi Kobayashi 623 | 624 | Permission is hereby granted, free of charge, to any person obtaining a copy 625 | of this software and associated documentation files (the "Software"), to deal 626 | in the Software without restriction, including without limitation the rights 627 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 628 | copies of the Software, and to permit persons to whom the Software is 629 | furnished to do so, subject to the following conditions: 630 | 631 | The above copyright notice and this permission notice shall be included in 632 | all copies or substantial portions of the Software. 633 | 634 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 635 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 636 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 637 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 638 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 639 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 640 | THE SOFTWARE. 641 | 642 | 643 | universal-user-agent 644 | ISC 645 | # [ISC License](https://spdx.org/licenses/ISC) 646 | 647 | Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m) 648 | 649 | 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. 650 | 651 | 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. 652 | 653 | 654 | uuid 655 | MIT 656 | The MIT License (MIT) 657 | 658 | Copyright (c) 2010-2020 Robert Kieffer and other contributors 659 | 660 | 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: 661 | 662 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 663 | 664 | 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. 665 | 666 | 667 | webidl-conversions 668 | BSD-2-Clause 669 | # The BSD 2-Clause License 670 | 671 | Copyright (c) 2014, Domenic Denicola 672 | All rights reserved. 673 | 674 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 675 | 676 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 677 | 678 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 679 | 680 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 681 | 682 | 683 | whatwg-url 684 | MIT 685 | The MIT License (MIT) 686 | 687 | Copyright (c) 2015–2016 Sebastian Mayr 688 | 689 | Permission is hereby granted, free of charge, to any person obtaining a copy 690 | of this software and associated documentation files (the "Software"), to deal 691 | in the Software without restriction, including without limitation the rights 692 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 693 | copies of the Software, and to permit persons to whom the Software is 694 | furnished to do so, subject to the following conditions: 695 | 696 | The above copyright notice and this permission notice shall be included in 697 | all copies or substantial portions of the Software. 698 | 699 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 700 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 701 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 702 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 703 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 704 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 705 | THE SOFTWARE. 706 | 707 | 708 | wrappy 709 | ISC 710 | The ISC License 711 | 712 | Copyright (c) Isaac Z. Schlueter and Contributors 713 | 714 | Permission to use, copy, modify, and/or distribute this software for any 715 | purpose with or without fee is hereby granted, provided that the above 716 | copyright notice and this permission notice appear in all copies. 717 | 718 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 719 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 720 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 721 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 722 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 723 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 724 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 725 | --------------------------------------------------------------------------------