├── .github ├── CODEOWNERS ├── FUNDING.yml ├── card-labeler.yml ├── pr-labeler.yml ├── workflows │ ├── project-card-moved.yml │ ├── broken-link-check.yml │ ├── issue-opened.yml │ ├── toc.yml │ ├── pr-opened.yml │ ├── sync-workflows.yml │ ├── add-release-tag.yml │ ├── add-test-tag.yml │ ├── update-dependencies.yml │ ├── pr-updated.yml │ └── ci.yml ├── labeler.yml ├── workflow-settings.json ├── no-response.yml ├── stale.yml ├── pull_request_template.md ├── release-drafter.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── config.yml ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── .lintstagedrc ├── .commitlintrc ├── _config.yml ├── .gitignore ├── jest.setup.ts ├── .huskyrc ├── .releasegarc ├── src ├── constant.ts ├── types.ts ├── main.ts ├── process.ts └── utils │ ├── misc.ts │ └── command.ts ├── .editorconfig ├── jest.config.js ├── LICENSE ├── package.json ├── action.yml ├── .eslintrc ├── tsconfig.json ├── README.ja.md ├── README.md └── __tests__ ├── process.test.ts ├── utils └── command.test.ts └── fixtures ├── pulls.list2.json └── pulls.list1.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @technote-space 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://paypal.me/technote0space -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,jsx,ts,tsx}": "yarn lint:fix" 3 | } -------------------------------------------------------------------------------- /.commitlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@commitlint/config-conventional" 4 | ] 5 | } -------------------------------------------------------------------------------- /.github/card-labeler.yml: -------------------------------------------------------------------------------- 1 | Backlog: 2 | 'In progress': 3 | - 'Status: In Progress' 4 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect 2 | 3 | # Google Analytics 4 | google_analytics: UA-78163306-3 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vscode 3 | /node_modules 4 | /coverage 5 | /lib 6 | /.work 7 | .eslintcache 8 | .env 9 | -------------------------------------------------------------------------------- /jest.setup.ts: -------------------------------------------------------------------------------- 1 | import { setupGlobal } from '@technote-space/github-action-test-helper'; 2 | 3 | setupGlobal(); 4 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 4 | "pre-commit": "lint-staged" 5 | } 6 | } -------------------------------------------------------------------------------- /.releasegarc: -------------------------------------------------------------------------------- 1 | { 2 | "inputs": { 3 | "OUTPUT_BUILD_INFO_FILENAME": "build.json", 4 | "TEST_TAG_PREFIX": "test/", 5 | "CLEAN_TEST_TAG": "true" 6 | } 7 | } -------------------------------------------------------------------------------- /src/constant.ts: -------------------------------------------------------------------------------- 1 | export const TARGET_EVENTS = { 2 | 'pull_request': [ 3 | 'opened', 4 | 'reopened', 5 | 'synchronize', 6 | 'closed', 7 | ], 8 | 'push': '*', 9 | }; 10 | export const REMOTE_NAME = 'get-diff-action'; 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.github/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | 'Type: Feature': ['feature/*', 'feat/*'] 2 | 'Type: Bug': fix/* 3 | 'Type: Maintenance': ['patch/*', 'chore/*'] 4 | 'Type: Release': release/* 5 | 'Type: Refactoring': ['refactor/*', 'refactoring/*'] 6 | 'Type: Documentation': ['docs/*', 'doc/*'] 7 | -------------------------------------------------------------------------------- /.github/workflows/project-card-moved.yml: -------------------------------------------------------------------------------- 1 | on: 2 | project_card: 3 | types: [created, moved] 4 | 5 | name: Project Card Event 6 | 7 | jobs: 8 | triage: 9 | name: Auto card labeler 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 3 12 | steps: 13 | - uses: technote-space/auto-card-labeler@v1 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | moduleFileExtensions: ['js', 'ts'], 4 | setupFiles: ['/jest.setup.ts'], 5 | testEnvironment: 'node', 6 | testMatch: ['**/*.test.ts'], 7 | testRunner: 'jest-circus/runner', 8 | transform: { 9 | '^.+\\.ts$': 'ts-jest', 10 | }, 11 | verbose: true, 12 | coverageDirectory: '/coverage', 13 | }; 14 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | javascript: 2 | - '**/*.js' 3 | typescript: 4 | - '**/*.ts' 5 | php: 6 | - '**/*.php' 7 | python: 8 | - '**/*.py' 9 | 10 | 'Type: Testing': 11 | - '**/tests/*' 12 | - '**/test/*' 13 | - '**/__tests__/*' 14 | 15 | 'Type: Documentation': 16 | - '**/*.md' 17 | 18 | 'Type: CI/CD': 19 | - '.github/workflows/*.yml' 20 | - '.circleci/*' 21 | - '.travis.yml' 22 | -------------------------------------------------------------------------------- /.github/workflow-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "EXCLUDE_MESSAGES": [ 3 | "update package version", 4 | "update packages", 5 | "update wp version", 6 | "trigger workflow", 7 | "update TOC" 8 | ], 9 | "PROJECT": "Backlog", 10 | "ISSUE_COLUMN": "To do", 11 | "PR_COLUMN": "In progress", 12 | "PR_BODY_TITLE": "## Changes", 13 | "TOC_FOLDING": "1", 14 | "TOC_MAX_HEADER_LEVEL": "3", 15 | "TOC_TITLE": "Details", 16 | "BRANCH_PREFIX": "release/" 17 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type FileDiffResult = Readonly<{ insertions: number; deletions: number; lines: number }>; 2 | export type FileResult = Readonly<{ file: string; filterIgnored: boolean; prefixMatched: boolean; suffixMatched: boolean }>; 3 | export type DiffResult = FileDiffResult & FileResult; 4 | export type PullRequestParams = Readonly<{ 5 | base: { 6 | ref: string; 7 | sha: string; 8 | }; 9 | head: { 10 | ref: string; 11 | }; 12 | }>; 13 | export type DiffInfo = Readonly<{ 14 | base: string; 15 | head: string; 16 | }>; 17 | -------------------------------------------------------------------------------- /.github/workflows/broken-link-check.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: 19 7 28 * * 4 | repository_dispatch: 5 | types: [check-link] 6 | workflow_dispatch: 7 | 8 | name: Broken Link Check 9 | jobs: 10 | check: 11 | name: Broken Link Check 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 10 14 | steps: 15 | - uses: technote-space/auto-cancel-redundant-job@v1 16 | with: 17 | EXCLUDE_MERGED: 'true' 18 | - name: Broken Link Check 19 | uses: technote-space/broken-link-checker-action@v2 20 | -------------------------------------------------------------------------------- /.github/workflows/issue-opened.yml: -------------------------------------------------------------------------------- 1 | on: 2 | issues: 3 | types: [opened] 4 | 5 | name: Issue opened 6 | 7 | jobs: 8 | assign: 9 | name: Assign issues to project 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 3 12 | steps: 13 | - uses: technote-space/load-config-action@v1 14 | with: 15 | CONFIG_FILENAME: workflow-settings.json 16 | - uses: technote-space/create-project-card-action@v1 17 | with: 18 | PROJECT: ${{ env.PROJECT }} 19 | COLUMN: ${{ env.ISSUE_COLUMN }} 20 | 21 | assignAuthor: 22 | name: Assign author to issue 23 | runs-on: ubuntu-latest 24 | timeout-minutes: 3 25 | steps: 26 | - uses: technote-space/assign-author@v1 27 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 14 5 | # Label requiring a response 6 | responseRequiredLabel: "Status: More Information Needed" 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 180 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 30 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - "Priority: Critical" 8 | - "Type: Security" 9 | # Label to use when marking an issue as stale 10 | staleLabel: "Status: Abandoned" 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.github/workflows/toc.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | types: [opened, synchronize, reopened, closed] 4 | 5 | name: TOC Generator 6 | 7 | jobs: 8 | toc: 9 | if: github.event.pull_request.head.user.id == github.event.pull_request.base.user.id 10 | name: TOC Generator 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 3 13 | steps: 14 | - uses: technote-space/load-config-action@v1 15 | with: 16 | CONFIG_FILENAME: workflow-settings.json 17 | - uses: technote-space/toc-generator@v2 18 | with: 19 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 20 | TARGET_BRANCH_PREFIX: ${{ env.BRANCH_PREFIX }} 21 | FOLDING: ${{ env.TOC_FOLDING }} 22 | MAX_HEADER_LEVEL: ${{ env.TOC_MAX_HEADER_LEVEL }} 23 | TOC_TITLE: ${{ env.TOC_TITLE }} 24 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description: 概要 2 | 3 | 4 | 5 | ## Changes: 変更内容 6 | 7 | 8 | 9 | 10 | 11 | 12 | ## Expected Impact: 影響範囲 13 | 14 | 15 | 16 | ## Operating Requirements: 動作要件 17 | 18 | 19 | 20 | ## Additional context: 補足 21 | 22 | 23 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Config for https://github.com/apps/release-drafter 2 | name-template: 'v$NEXT_PATCH_VERSION' 3 | tag-template: 'v$NEXT_PATCH_VERSION' 4 | categories: 5 | - title: ':rocket: Features' 6 | labels: 7 | - 'Type: Feature' 8 | - 'Type: Refactoring' 9 | - title: ':bug: Bug Fixes' 10 | labels: 11 | - 'Type: Bug' 12 | - 'Type: Security' 13 | - title: ':wrench: Maintenance' 14 | labels: 15 | - 'Type: Maintenance' 16 | - 'Type: CI/CD' 17 | - title: ':green_book: Docs' 18 | labels: 19 | - 'Type: Documentation' 20 | - title: ':white_check_mark: Tested' 21 | labels: 22 | - 'Type: Testing' 23 | - title: ':sparkles: All Changes' 24 | labels: 25 | - 'Type: Release' 26 | exclude-labels: 27 | - 'dependencies' 28 | template: | 29 | ## What’s Changed 30 | 31 | $CHANGES 32 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import {resolve} from 'path'; 2 | import {setFailed} from '@actions/core'; 3 | import {Context} from '@actions/github/lib/context'; 4 | import {isTargetEvent} from '@technote-space/filter-github-action'; 5 | import {Logger, ContextHelper} from '@technote-space/github-action-helper'; 6 | import {execute} from './process'; 7 | import {TARGET_EVENTS} from './constant'; 8 | 9 | const run = async(): Promise => { 10 | const logger = new Logger(); 11 | const context = new Context(); 12 | ContextHelper.showActionInfo(resolve(__dirname, '..'), logger, context); 13 | 14 | if (!isTargetEvent(TARGET_EVENTS, context)) { 15 | logger.info('This is not target event.'); 16 | await execute(logger, context, true); 17 | return; 18 | } 19 | 20 | await execute(logger, context); 21 | }; 22 | 23 | run().catch(error => { 24 | console.log(error); 25 | setFailed(error.message); 26 | }); 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: 'technote-space' 7 | 8 | --- 9 | 10 | ## Please describe your suggestion: 提案の概要 11 | 12 | 13 | 14 | ## Describe the solution you'd like: 考えうる解決方法 15 | 16 | 17 | 18 | ## Describe alternatives you've considered: 考えうる代替案 19 | 20 | 21 | 22 | ## Additional context: 補足 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/pr-opened.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request_target: 3 | types: [opened] 4 | 5 | name: Pull Request opened 6 | 7 | jobs: 8 | assignToProject: 9 | name: Assign PullRequest to Project 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 3 12 | steps: 13 | - uses: technote-space/load-config-action@v1 14 | with: 15 | CONFIG_FILENAME: workflow-settings.json 16 | - uses: technote-space/create-project-card-action@v1 17 | with: 18 | PROJECT: ${{ env.PROJECT }} 19 | COLUMN: ${{ env.PR_COLUMN }} 20 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 21 | 22 | assignAuthor: 23 | name: Assign author to PR 24 | runs-on: ubuntu-latest 25 | timeout-minutes: 3 26 | steps: 27 | - uses: technote-space/assign-author@v1 28 | 29 | addLabelsByBranch: 30 | name: PR Labeler 31 | runs-on: ubuntu-latest 32 | timeout-minutes: 3 33 | steps: 34 | - uses: technote-space/pr-labeler-action@v4 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: 'technote-space' 7 | 8 | --- 9 | 10 | ## Describe the bug: バグの概要 11 | 12 | 13 | 14 | ## To Reproduce: 再現手順 15 | Steps to reproduce the behavior: 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | ## Expected behavior: 期待する動作 22 | 23 | 24 | 25 | ## Screenshots: スクリーンショット 26 | 27 | 28 | 29 | ## Operating environment: バグが発生した環境 30 | - Version of software 31 | - OS: [e.g. Windows10] 32 | - Browser: [e.g. Chrome, Safari] 33 | - etc. 34 | 35 | ## Additional context: 補足 36 | 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Technote 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/sync-workflows.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: 0 16 * * 4 4 | repository_dispatch: 5 | types: [sync-workflows] 6 | workflow_dispatch: 7 | 8 | name: Sync workflows 9 | jobs: 10 | release: 11 | name: Sync workflows 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 5 14 | steps: 15 | - name: Sync workflows 16 | uses: technote-space/create-pr-action@v2 17 | with: 18 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 19 | EXECUTE_COMMANDS: | 20 | rm -rdf .github/workflows/.tmp 21 | mkdir -p .github/workflows/.tmp 22 | git clone --depth=1 https://github.com/technote-space/github-actions-workflows.git .github/workflows/.tmp/workflows 23 | 24 | bash .github/workflows/.tmp/workflows/gh-actions/copy.sh 25 | sed -i 's/cron:.\+$/cron: 0 3 * * 2,6/' .github/workflows/update-dependencies.yml 26 | 27 | rm -rdf .github/workflows/.tmp 28 | COMMIT_MESSAGE: 'chore: sync workflows' 29 | PR_BRANCH_PREFIX: chore/ 30 | PR_BRANCH_NAME: 'chore-sync-workflows' 31 | PR_TITLE: 'chore: sync workflows' 32 | ONLY_DEFAULT_BRANCH: true 33 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for request-info - https://github.com/behaviorbot/request-info 2 | 3 | # *Required* Comment to reply with 4 | requestInfoReplyComment: > 5 | :clap: We would appreciate it if you could provide us with more info about this issue/pr! 6 | 7 | # *OPTIONAL* default titles to check against for lack of descriptiveness 8 | # MUST BE ALL LOWERCASE 9 | requestInfoDefaultTitles: 10 | - update readme.md 11 | - updates 12 | - update 13 | 14 | # *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given 15 | requestInfoLabelToAdd: "Status: More Information Needed" 16 | 17 | 18 | 19 | 20 | # Configuration for welcome - https://github.com/behaviorbot/welcome 21 | 22 | # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome 23 | 24 | # Comment to be posted to on first time issues 25 | newIssueWelcomeComment: > 26 | :raised_hands: Thanks for opening your first issue here! Be sure to follow the issue template! 27 | 28 | # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome 29 | 30 | # Comment to be posted to on PRs from first time contributors in your repository 31 | newPRWelcomeComment: > 32 | :raised_hands: Thanks for opening this pull request! Please check out our contributing guidelines. 33 | 34 | # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge 35 | 36 | # Comment to be posted to on pull requests merged by a first time user 37 | firstPRMergeComment: > 38 | :tada: Congrats on merging your first pull request! We here at behaviorbot are proud of you! 39 | 40 | 41 | 42 | # Configuration for todo - https://github.com/jasonetco/todo 43 | todo: 44 | - label: "Type: Todo" -------------------------------------------------------------------------------- /.github/workflows/add-release-tag.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | branches: 4 | - master 5 | - main 6 | - develop/v* 7 | types: [closed] 8 | 9 | name: Add release tag 10 | 11 | jobs: 12 | tag: 13 | name: Add release tag 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 3 16 | if: github.event.pull_request.merged == true && github.event.pull_request.head.user.id == github.event.pull_request.base.user.id && startsWith(github.head_ref, 'release/') 17 | steps: 18 | - uses: technote-space/load-config-action@v1 19 | with: 20 | CONFIG_FILENAME: workflow-settings.json 21 | - name: Get version 22 | uses: technote-space/get-next-version-action@v1 23 | with: 24 | EXCLUDE_MESSAGES: ${{ env.EXCLUDE_MESSAGES }} 25 | if: "! startsWith(github.head_ref, 'release/v')" 26 | - name: Get version 27 | run: echo "::set-env name=NEXT_VERSION::${HEAD_REF#release/}" 28 | env: 29 | HEAD_REF: ${{ github.head_ref }} 30 | if: startsWith(github.head_ref, 'release/v') 31 | - uses: actions/github-script@0.4.0 32 | with: 33 | github-token: ${{ secrets.ACCESS_TOKEN }} 34 | script: | 35 | github.git.createRef({ 36 | owner: context.repo.owner, 37 | repo: context.repo.repo, 38 | ref: `refs/tags/${process.env.NEXT_VERSION}`, 39 | sha: context.sha 40 | }) 41 | if: env.NEXT_VERSION 42 | - uses: actions/github-script@0.4.0 43 | with: 44 | github-token: ${{ secrets.ACCESS_TOKEN }} 45 | script: | 46 | github.git.createRef({ 47 | owner: context.repo.owner, 48 | repo: context.repo.repo, 49 | ref: `refs/heads/release/next-${process.env.NEXT_VERSION}`, 50 | sha: context.sha 51 | }) 52 | if: env.NEXT_VERSION 53 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | [issues]: https://github.com/technote-space/get-diff-action/issues 3 | [fork]: https://github.com/technote-space/get-diff-action/fork 4 | [pr]: https://github.com/technote-space/get-diff-action/compare 5 | [eslint]: https://eslint.org/ 6 | [jest]: https://jestjs.io/ 7 | [code-of-conduct]: CODE_OF_CONDUCT.md 8 | 9 | When contributing to this repository, please first discuss the change you wish to make via [issue][issues] with the owners of this repository before making a change. 10 | 11 | Please note we have a [Contributor Code of Conduct][code-of-conduct], please follow it in all your interactions with the project. 12 | 13 | ## Submitting a pull request 14 | 15 | 1. [Fork][fork] and clone the repository 16 | 1. Make sure the tests pass on your machine: `yarn test`, which contains 17 | - [`ESLint`][eslint] 18 | - [`Jest`][jest] 19 | 1. Create a new branch: `git checkout -b my-branch-name` 20 | 1. Make your change, add tests, and make sure the tests still pass. 21 | 1. Push to your fork and [submit a pull request][pr]. 22 | 1. Pat your self on the back and wait for your pull request to be reviewed and merged. 23 | 24 | Here are a few things you can do that will increase the likelihood of your pull request being accepted: 25 | - Write and update tests. 26 | - Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. 27 | - Write a [good commit message](https://github.com/erlang/otp/wiki/writing-good-commit-messages). 28 | 29 | Work in Progress pull request are also welcome to get feedback early on, or if there is something blocked you. 30 | 31 | ## Resources 32 | 33 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 34 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) 35 | - [GitHub Help](https://help.github.com) 36 | -------------------------------------------------------------------------------- /src/process.ts: -------------------------------------------------------------------------------- 1 | import {exportVariable, getInput, setOutput} from '@actions/core' ; 2 | import {Context} from '@actions/github/lib/context'; 3 | import {Logger} from '@technote-space/github-action-helper'; 4 | import {getGitDiff, getDiffFiles, sumResults} from './utils/command'; 5 | import {DiffResult} from './types'; 6 | 7 | export const dumpDiffs = (diffs: DiffResult[], logger: Logger): void => { 8 | logger.startProcess('Dump diffs'); 9 | console.log(diffs); 10 | logger.endProcess(); 11 | }; 12 | 13 | export const setResult = (diffs: DiffResult[], skipped: boolean, logger: Logger): void => { 14 | const insertions = sumResults(diffs, item => item.insertions); 15 | const deletions = sumResults(diffs, item => item.deletions); 16 | const getValue = (setting): string => skipped ? getInput(`${setting.name.toUpperCase()}_DEFAULT`) : setting.value(); 17 | const settings = [ 18 | {name: 'diff', value: () => getDiffFiles(diffs, false), envNameSuffix: ''}, 19 | {name: 'filtered_diff', value: () => getDiffFiles(diffs, true)}, 20 | {name: 'count', value: () => diffs.length}, 21 | {name: 'insertions', value: () => insertions}, 22 | {name: 'deletions', value: () => deletions}, 23 | {name: 'lines', value: () => insertions + deletions}, 24 | ]; 25 | 26 | logger.startProcess('Dump output'); 27 | settings.forEach(setting => { 28 | const result = String(getValue(setting)); 29 | setOutput(setting.name, result); 30 | const envName = getInput('SET_ENV_NAME' + (setting.envNameSuffix ?? `_${setting.name.toUpperCase()}`)); 31 | if (envName) { 32 | exportVariable(envName, result); 33 | } 34 | console.log(`${setting.name}: ${result}`); 35 | }); 36 | logger.endProcess(); 37 | }; 38 | 39 | export const execute = async(logger: Logger, context: Context, skipped = false): Promise => { 40 | const _diff = skipped ? [] : await getGitDiff(logger, context); 41 | dumpDiffs(_diff, logger); 42 | setResult(_diff, skipped, logger); 43 | }; 44 | -------------------------------------------------------------------------------- /.github/workflows/add-test-tag.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | types: [synchronize] 4 | 5 | name: Add test tag 6 | 7 | jobs: 8 | tag: 9 | name: Add test tag 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 3 12 | if: github.event.pull_request.head.user.id == github.event.pull_request.base.user.id && startsWith(github.head_ref, 'release/') 13 | steps: 14 | - uses: technote-space/load-config-action@v1 15 | with: 16 | CONFIG_FILENAME: workflow-settings.json 17 | - uses: actions/checkout@v2 18 | - uses: technote-space/get-git-comment-action@v1 19 | - name: Get version 20 | uses: technote-space/get-next-version-action@v1 21 | with: 22 | EXCLUDE_MESSAGES: ${{ env.EXCLUDE_MESSAGES }} 23 | if: "! startsWith(github.head_ref, 'release/v') && (contains(env.COMMIT_MESSAGE, 'chore: update dependencies') || contains(env.COMMIT_MESSAGE, 'chore: update npm dependencies'))" 24 | - name: Get version 25 | run: echo "::set-env name=NEXT_VERSION::${HEAD_REF#release/}" 26 | env: 27 | HEAD_REF: ${{ github.head_ref }} 28 | if: "startsWith(github.head_ref, 'release/v') && (contains(env.COMMIT_MESSAGE, 'chore: update dependencies') || contains(env.COMMIT_MESSAGE, 'chore: update npm dependencies'))" 29 | - name: Get tag name 30 | run: echo "::set-env name=TAG_NAME::${NEXT_VERSION}.${RUN_ID}" 31 | env: 32 | HEAD_REF: ${{ github.head_ref }} 33 | RUN_ID: ${{ github.run_id }} 34 | if: env.NEXT_VERSION 35 | - uses: actions/github-script@0.4.0 36 | with: 37 | github-token: ${{ secrets.ACCESS_TOKEN }} 38 | script: | 39 | github.git.createRef({ 40 | owner: context.repo.owner, 41 | repo: context.repo.repo, 42 | ref: `refs/tags/test/${process.env.TAG_NAME}`, 43 | sha: context.payload.pull_request.head.sha 44 | }) 45 | if: env.TAG_NAME 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@technote-space/get-diff-action", 3 | "version": "3.0.1", 4 | "description": "GitHub actions to get git diff.", 5 | "author": { 6 | "name": "Technote", 7 | "email": "technote.space@gmail.com", 8 | "url": "https://technote.space" 9 | }, 10 | "license": "MIT", 11 | "keywords": [ 12 | "github", 13 | "github actions" 14 | ], 15 | "homepage": "https://github.com/technote-space/get-diff-action", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/technote-space/get-diff-action.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/technote-space/get-diff-action/issues" 22 | }, 23 | "files": [ 24 | "lib", 25 | "action.yml" 26 | ], 27 | "dependencies": { 28 | "@actions/core": "^1.2.4", 29 | "@actions/github": "^4.0.0", 30 | "@technote-space/filter-github-action": "^0.5.1", 31 | "@technote-space/github-action-helper": "^3.3.1" 32 | }, 33 | "devDependencies": { 34 | "@commitlint/cli": "^9.1.2", 35 | "@commitlint/config-conventional": "^9.1.2", 36 | "@technote-space/github-action-test-helper": "^0.5.6", 37 | "@technote-space/release-github-actions-cli": "^1.6.9", 38 | "@types/jest": "^26.0.10", 39 | "@types/node": "^14.6.0", 40 | "@typescript-eslint/eslint-plugin": "^3.9.1", 41 | "@typescript-eslint/parser": "^3.9.1", 42 | "eslint": "^7.7.0", 43 | "husky": "^4.2.5", 44 | "jest": "^26.4.0", 45 | "jest-circus": "^26.4.0", 46 | "lint-staged": "^10.2.11", 47 | "nock": "^13.0.4", 48 | "ts-jest": "^26.2.0", 49 | "typescript": "^3.9.7" 50 | }, 51 | "publishConfig": { 52 | "access": "public" 53 | }, 54 | "scripts": { 55 | "build": "tsc", 56 | "test": "yarn lint && yarn cover", 57 | "lint": "eslint 'src/**/*.ts' '__tests__/**/*.ts' --cache", 58 | "lint:fix": "eslint --fix 'src/**/*.ts' '__tests__/**/*.ts'", 59 | "cover": "jest --coverage", 60 | "update": "npx npm-check-updates -u && yarn install && yarn upgrade && yarn audit", 61 | "release": "yarn release-ga --test" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/utils/misc.ts: -------------------------------------------------------------------------------- 1 | import {Context} from '@actions/github/lib/context'; 2 | import {Octokit} from '@technote-space/github-action-helper/dist/types'; 3 | import {Utils, ApiHelper} from '@technote-space/github-action-helper'; 4 | import {PullRequestParams, DiffInfo} from '../types'; 5 | 6 | export const escape = (items: string[]): string[] => items.map(item => { 7 | // eslint-disable-next-line no-useless-escape 8 | if (!/^[A-Za-z0-9_\/-]+$/.test(item)) { 9 | item = '\'' + item.replace(/'/g, '\'\\\'\'') + '\''; 10 | item = item.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning 11 | .replace(/\\'''/g, '\\\''); // remove non-escaped single-quote if there are enclosed between 2 escaped 12 | } 13 | return item; 14 | }); 15 | 16 | export const getDiffInfoForPR = (pull: PullRequestParams, context: Context): DiffInfo => ({ 17 | base: context.payload.action === 'closed' ? pull.base.sha : Utils.normalizeRef(pull.base.ref), 18 | head: context.payload.action === 'closed' ? context.sha : Utils.normalizeRef(context.ref), 19 | }); 20 | 21 | export const isDefaultBranch = async(octokit: Octokit, context: Context): Promise => await (new ApiHelper(octokit, context)).getDefaultBranch() === Utils.getBranch(context); 22 | 23 | export const getDiffInfoForPush = async(octokit: Octokit, context: Context): Promise => { 24 | if (Utils.isTagRef(context)) { 25 | return {base: '', head: ''}; 26 | } 27 | 28 | if (!await isDefaultBranch(octokit, context)) { 29 | const pull = await (new ApiHelper(octokit, context)).findPullRequest(context.ref); 30 | if (pull) { 31 | return { 32 | base: Utils.normalizeRef(pull.base.ref), 33 | head: `refs/pull/${pull.number}/merge`, 34 | }; 35 | } 36 | } 37 | 38 | if (/^0+$/.test(context.payload.before)) { 39 | return { 40 | base: Utils.normalizeRef(await (new ApiHelper(octokit, context)).getDefaultBranch()), 41 | head: context.payload.after, 42 | }; 43 | } 44 | 45 | return { 46 | base: context.payload.before, 47 | head: context.payload.after, 48 | }; 49 | }; 50 | 51 | export const getDiffInfo = async(octokit: Octokit, context: Context): Promise => context.payload.pull_request ? getDiffInfoForPR({ 52 | base: context.payload.pull_request.base, 53 | head: context.payload.pull_request.head, 54 | }, context) : await getDiffInfoForPush(octokit, context); 55 | -------------------------------------------------------------------------------- /.github/workflows/update-dependencies.yml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | - cron: 0 3 * * 2,6 4 | pull_request: 5 | types: [opened, reopened, closed] 6 | repository_dispatch: 7 | types: [update-deps] 8 | workflow_dispatch: 9 | 10 | name: Update dependencies 11 | jobs: 12 | update: 13 | name: Update npm dependencies 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 10 16 | if: "! startsWith(github.head_ref, 'release/v')" 17 | steps: 18 | - name: Set running flag 19 | run: echo "::set-env name=RUNNING1::" 20 | - name: Set running flag 21 | run: echo "::set-env name=RUNNING1::1" 22 | if: github.event.pull_request.head.user.id == github.event.pull_request.base.user.id 23 | - uses: technote-space/load-config-action@v1 24 | if: env.RUNNING1 25 | with: 26 | CONFIG_FILENAME: workflow-settings.json 27 | - uses: technote-space/auto-cancel-redundant-job@v1 28 | if: env.RUNNING1 29 | with: 30 | EXCLUDE_MERGED: 'true' 31 | - name: Update dependencies 32 | if: env.RUNNING1 33 | id: update_deps 34 | uses: technote-space/create-pr-action@v2 35 | with: 36 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 37 | EXECUTE_COMMANDS: | 38 | npx npm-check-updates -u --packageFile package.json 39 | yarn install 40 | yarn upgrade 41 | yarn audit 42 | COMMIT_MESSAGE: 'chore: update npm dependencies' 43 | PR_DEFAULT_BRANCH_PREFIX: release/ 44 | PR_DEFAULT_BRANCH_NAME: next-${CURRENT_VERSION} 45 | PR_DEFAULT_BRANCH_TITLE: 'feat: release' 46 | TARGET_BRANCH_PREFIX: release/ 47 | AUTO_MERGE_THRESHOLD_DAYS: 14 48 | 49 | - name: Set running flag 50 | run: echo "::set-env name=RUNNING2::" 51 | - name: Set running flag 52 | run: echo "::set-env name=RUNNING2::1" 53 | if: env.RUNNING1 && steps.update_deps.outputs.result != 'succeeded' && github.event_name == 'pull_request' && github.event.action != 'closed' && startsWith(github.head_ref, 'release/') 54 | - uses: actions/checkout@v2 55 | if: env.RUNNING2 56 | - name: Set running flag 57 | run: | 58 | if [[ ! -f package.json ]] || [[ $(< package.json jq -r '.version == null') == 'true' ]]; then 59 | echo "::set-env name=RUNNING2::" 60 | fi 61 | - name: Get version 62 | uses: technote-space/get-next-version-action@v1 63 | with: 64 | EXCLUDE_MESSAGES: ${{ env.EXCLUDE_MESSAGES }} 65 | if: "env.RUNNING2 && ! startsWith(github.head_ref, 'release/v')" 66 | - name: Get version 67 | run: echo "::set-env name=NEXT_VERSION::${HEAD_REF#release/}" 68 | env: 69 | HEAD_REF: ${{ github.head_ref }} 70 | if: env.RUNNING2 && startsWith(github.head_ref, 'release/v') 71 | - name: Check package version 72 | uses: technote-space/package-version-check-action@v1 73 | with: 74 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 75 | BRANCH_PREFIX: release/ 76 | NEXT_VERSION: ${{ env.NEXT_VERSION }} 77 | if: env.NEXT_VERSION 78 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/ja/articles/metadata-syntax-for-github-actions 2 | name: Get Diff Action 3 | 4 | description: GitHub actions to get git diff. 5 | 6 | author: technote-space 7 | 8 | inputs: 9 | GITHUB_TOKEN: 10 | description: Secret GitHub API token used to make API requests. 11 | default: ${{ github.token }} 12 | required: true 13 | DOT: 14 | description: Dot. 15 | default: '...' 16 | required: true 17 | DIFF_FILTER: 18 | description: Diff filter. 19 | default: 'AMRC' 20 | required: true 21 | SEPARATOR: 22 | description: Separator of diffs. 23 | default: ' ' 24 | required: true 25 | PREFIX_FILTER: 26 | description: Prefix filter. 27 | required: false 28 | PREFIX_FILTER_FLAGS: 29 | description: RegExp flags of prefix filter. 30 | default: '' 31 | required: false 32 | SUFFIX_FILTER: 33 | description: Suffix filter. 34 | required: false 35 | SUFFIX_FILTER_FLAGS: 36 | description: RegExp flags of suffix filter. 37 | default: 'i' 38 | required: false 39 | FILES: 40 | description: Specific files. 41 | required: false 42 | SUMMARY_INCLUDE_FILES: 43 | description: Whether the summary contains the result of specific files. 44 | required: false 45 | ABSOLUTE: 46 | description: Get as absolute path. 47 | required: false 48 | SET_ENV_NAME: 49 | description: Env name (diff). 50 | default: GIT_DIFF 51 | required: false 52 | SET_ENV_NAME_FILTERED_DIFF: 53 | description: Env name (filtered_diff). 54 | default: GIT_DIFF_FILTERED 55 | required: false 56 | SET_ENV_NAME_COUNT: 57 | description: Env name (count). 58 | required: false 59 | SET_ENV_NAME_INSERTIONS: 60 | description: Env name (insertions). 61 | required: false 62 | SET_ENV_NAME_DELETIONS: 63 | description: Env name (deletions). 64 | required: false 65 | SET_ENV_NAME_LINES: 66 | description: Env name (lines). 67 | required: false 68 | DIFF_DEFAULT: 69 | description: Default value (diff). 70 | required: false 71 | FILTERED_DIFF_DEFAULT: 72 | description: Default value (filtered_diff). 73 | required: false 74 | COUNT_DEFAULT: 75 | description: Default value (count). 76 | default: '0' 77 | required: false 78 | INSERTIONS_DEFAULT: 79 | description: Default value (insertions). 80 | default: '0' 81 | required: false 82 | DELETIONS_DEFAULT: 83 | description: Default value (deletions). 84 | default: '0' 85 | required: false 86 | LINES_DEFAULT: 87 | description: Default value (lines). 88 | default: '0' 89 | required: false 90 | 91 | outputs: 92 | diff: 93 | description: The results of diff file names. 94 | filtered_diff: 95 | description: The results of diff file names. 96 | count: 97 | description: The number of diff files. 98 | insertions: 99 | description: The number of insertions lines. 100 | deletions: 101 | description: The number of deletions lines. 102 | lines: 103 | description: The number of diff lines. 104 | 105 | branding: 106 | # https://feathericons.com/ 107 | icon: 'file' 108 | color: 'orange' 109 | 110 | runs: 111 | using: node12 112 | main: lib/main.js 113 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:@typescript-eslint/recommended", 5 | "plugin:@typescript-eslint/eslint-recommended" 6 | ], 7 | "plugins": [ 8 | "@typescript-eslint" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "parserOptions": { 12 | "sourceType": "module", 13 | "ecmaVersion": 2018 14 | }, 15 | "env": { 16 | "node": true, 17 | "jest": true, 18 | "es6": true, 19 | "browser": true 20 | }, 21 | "settings": { 22 | "react": { 23 | "version": "latest" 24 | } 25 | }, 26 | "rules": { 27 | "camelcase": [ 28 | "error", 29 | { 30 | "properties": "always" 31 | } 32 | ], 33 | "require-jsdoc": [ 34 | "error", 35 | { 36 | "require": { 37 | "FunctionDeclaration": true, 38 | "MethodDefinition": true, 39 | "ClassDeclaration": true 40 | } 41 | } 42 | ], 43 | "valid-jsdoc": [ 44 | "error", 45 | { 46 | "requireReturn": false, 47 | "preferType": { 48 | "String": "string", 49 | "Object": "object", 50 | "Number": "number", 51 | "Function": "function", 52 | "Void": "void" 53 | } 54 | } 55 | ], 56 | "quotes": [ 57 | "error", 58 | "single", 59 | "avoid-escape" 60 | ], 61 | "key-spacing": [ 62 | "error", 63 | { 64 | "singleLine": { 65 | "beforeColon": false, 66 | "afterColon": true 67 | }, 68 | "multiLine": { 69 | "beforeColon": false, 70 | "afterColon": true 71 | } 72 | } 73 | ], 74 | "no-magic-numbers": [ 75 | "error", 76 | { 77 | "ignoreArrayIndexes": true 78 | } 79 | ], 80 | "eqeqeq": "error", 81 | "block-scoped-var": "error", 82 | "complexity": [ 83 | "error", 84 | { 85 | "maximum": 20 86 | } 87 | ], 88 | "curly": "error", 89 | "default-case": "error", 90 | "dot-location": [ 91 | "error", 92 | "property" 93 | ], 94 | "guard-for-in": "error", 95 | "no-eval": "error", 96 | "block-spacing": "error", 97 | "brace-style": "error", 98 | "comma-spacing": [ 99 | "error", 100 | { 101 | "before": false, 102 | "after": true 103 | } 104 | ], 105 | "id-length": [ 106 | "error", 107 | { 108 | "min": 2, 109 | "properties": "never", 110 | "exceptions": [ 111 | "$" 112 | ] 113 | } 114 | ], 115 | "indent": [ 116 | "error", 117 | 2, 118 | { 119 | "SwitchCase": 1 120 | } 121 | ], 122 | "space-before-function-paren": [ 123 | "error", 124 | "never" 125 | ], 126 | "space-before-blocks": "error", 127 | "prefer-const": "error", 128 | "no-var": "error", 129 | "arrow-body-style": "off", 130 | "arrow-spacing": "error", 131 | "strict": [ 132 | "error" 133 | ], 134 | "no-warning-comments": [ 135 | "warn", 136 | { 137 | "terms": [ 138 | "todo", 139 | "fixme", 140 | "hack" 141 | ], 142 | "location": "anywhere" 143 | } 144 | ], 145 | "semi": [ 146 | "error" 147 | ] 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at technote.space@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/workflows/pr-updated.yml: -------------------------------------------------------------------------------- 1 | on: pull_request_target 2 | 3 | name: Pull Request updated 4 | 5 | jobs: 6 | triage: 7 | name: Pull Request Labeler 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 3 10 | if: "! startsWith(github.head_ref, 'release/')" 11 | steps: 12 | - uses: actions/labeler@v2 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | 16 | history: 17 | name: Pull Request Body 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 3 20 | if: github.event.pull_request.head.user.id == github.event.pull_request.base.user.id 21 | steps: 22 | - uses: technote-space/load-config-action@v1 23 | with: 24 | CONFIG_FILENAME: workflow-settings.json 25 | - uses: technote-space/pr-commit-body-action@v1 26 | with: 27 | EXCLUDE_MESSAGES: ${{ env.EXCLUDE_MESSAGES }} 28 | TITLE: ${{ env.PR_BODY_TITLE }} 29 | LINK_ISSUE_KEYWORD: ${{ (startsWith(github.head_ref, 'release/') && 'closes') || '' }} 30 | FILTER_PR: true 31 | 32 | manageRelease: 33 | name: Manage release 34 | runs-on: ubuntu-latest 35 | timeout-minutes: 3 36 | if: "github.event.pull_request.head.user.id == github.event.pull_request.base.user.id && startsWith(github.head_ref, 'release/') && ! startsWith(github.head_ref, 'release/v')" 37 | steps: 38 | - uses: technote-space/load-config-action@v1 39 | with: 40 | CONFIG_FILENAME: workflow-settings.json 41 | - uses: technote-space/release-type-action@v1 42 | with: 43 | EXCLUDE_MESSAGES: ${{ env.EXCLUDE_MESSAGES }} 44 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 45 | 46 | checkVersion: 47 | name: Check package version 48 | runs-on: ubuntu-latest 49 | timeout-minutes: 3 50 | if: "github.event.action == 'synchronize' && github.event.pull_request.head.user.id == github.event.pull_request.base.user.id && startsWith(github.head_ref, 'release/')" 51 | steps: 52 | - uses: technote-space/load-config-action@v1 53 | with: 54 | CONFIG_FILENAME: workflow-settings.json 55 | - name: Set running flag 56 | run: echo "::set-env name=RUNNING::1" 57 | - uses: actions/checkout@v2 58 | with: 59 | ref: ${{ github.head_ref }} 60 | - name: Set running flag 61 | run: | 62 | if [[ ! -f package.json ]] || [[ $(< package.json jq -r '.version == null') == 'true' ]]; then 63 | echo "::set-env name=RUNNING::" 64 | fi 65 | 66 | - name: Get version 67 | uses: technote-space/get-next-version-action@v1 68 | with: 69 | EXCLUDE_MESSAGES: ${{ env.EXCLUDE_MESSAGES }} 70 | if: "env.RUNNING && ! startsWith(github.head_ref, 'release/v')" 71 | - name: Get version 72 | run: echo "::set-env name=NEXT_VERSION::${HEAD_REF#release/}" 73 | env: 74 | HEAD_REF: ${{ github.head_ref }} 75 | if: env.RUNNING && startsWith(github.head_ref, 'release/v') 76 | - name: Check package version 77 | uses: technote-space/package-version-check-action@v1 78 | with: 79 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 80 | BRANCH_PREFIX: release/ 81 | NEXT_VERSION: ${{ env.NEXT_VERSION }} 82 | if: env.NEXT_VERSION 83 | 84 | checkPublish: 85 | name: Check publish 86 | runs-on: ubuntu-latest 87 | timeout-minutes: 3 88 | if: "github.event.pull_request.head.user.id == github.event.pull_request.base.user.id && startsWith(github.head_ref, 'release/')" 89 | steps: 90 | - name: Set running flag 91 | run: echo "::set-env name=RUNNING::1" 92 | - name: Set running flag 93 | run: | 94 | if [ -z "$NPM_AUTH_TOKEN" ]; then 95 | echo "::set-env name=RUNNING::" 96 | fi 97 | env: 98 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 99 | 100 | - uses: actions/checkout@v2 101 | with: 102 | ref: ${{ github.head_ref }} 103 | if: env.RUNNING 104 | - uses: technote-space/can-npm-publish-action@v1 105 | if: env.RUNNING 106 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es6", 6 | /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 7 | "module": "commonjs", 8 | /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 9 | // "allowJs": true, /* Allow javascript files to be compiled. */ 10 | // "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 12 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | "outDir": "./lib", 17 | /* Redirect output structure to the directory. */ 18 | "rootDir": "./src", 19 | /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 20 | // "composite": true, /* Enable project compilation */ 21 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 22 | // "removeComments": true, /* Do not emit comments to output. */ 23 | // "noEmit": true, /* Do not emit outputs. */ 24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 27 | 28 | /* Strict Type-Checking Options */ 29 | "strict": true, 30 | /* Enable all strict type-checking options. */ 31 | "noImplicitAny": false, 32 | /* Raise error on expressions and declarations with an implied 'any' type. */ 33 | // "strictNullChecks": true, /* Enable strict null checks. */ 34 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 35 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 36 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 37 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 38 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 39 | 40 | /* Additional Checks */ 41 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 42 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 43 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 44 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 45 | 46 | /* Module Resolution Options */ 47 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 48 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 51 | // "typeRoots": [], /* List of folders to include type definitions from. */ 52 | // "types": [], /* Type declaration files to be included in compilation. */ 53 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 54 | "esModuleInterop": true 55 | /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 56 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 57 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 58 | 59 | /* Source Map Options */ 60 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 61 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 62 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 63 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 64 | 65 | /* Experimental Options */ 66 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 67 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 68 | }, 69 | "exclude": [ 70 | "node_modules", 71 | "__tests__", 72 | "jest.setup.ts" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /src/utils/command.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import {getInput} from '@actions/core' ; 3 | import {Context} from '@actions/github/lib/context'; 4 | import {Logger, Command, Utils, GitHelper} from '@technote-space/github-action-helper'; 5 | import {escape, getDiffInfo} from './misc'; 6 | import {FileDiffResult, FileResult, DiffResult, DiffInfo} from '../types'; 7 | import {REMOTE_NAME} from '../constant'; 8 | 9 | const command = new Command(new Logger()); 10 | const getRawInput = (name: string): string => process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; 11 | const getDot = (): string => getInput('DOT', {required: true}); 12 | const getFilter = (): string => getInput('DIFF_FILTER', {required: true}); 13 | const getSeparator = (): string => getRawInput('SEPARATOR'); 14 | const getPrefix = (): string[] => Utils.getArrayInput('PREFIX_FILTER', undefined, ''); 15 | const getSuffix = (): string[] => Utils.getArrayInput('SUFFIX_FILTER', undefined, ''); 16 | const getPrefixRegExpFlags = (): string => getInput('PREFIX_FILTER_FLAGS') || ''; 17 | const getSuffixRegExpFlags = (): string => getInput('SUFFIX_FILTER_FLAGS') || ''; 18 | const getFiles = (): string[] => Utils.getArrayInput('FILES', undefined, ''); 19 | const getWorkspace = (): string => Utils.getBoolValue(getInput('ABSOLUTE')) ? (Utils.getWorkspace() + '/') : ''; 20 | const getSummaryIncludeFilesFlag = (): boolean => Utils.getBoolValue(getInput('SUMMARY_INCLUDE_FILES')); 21 | const isFilterIgnored = (item: string, files: string[]): boolean => !!(files.length && files.includes(path.basename(item))); 22 | const isPrefixMatched = (item: string, prefix: string[]): boolean => !prefix.length || !prefix.every(prefix => !Utils.getPrefixRegExp(prefix, getPrefixRegExpFlags()).test(item)); 23 | const isSuffixMatched = (item: string, suffix: string[]): boolean => !suffix.length || !suffix.every(suffix => !Utils.getSuffixRegExp(suffix, getSuffixRegExpFlags()).test(item)); 24 | const toAbsolute = (item: string, workspace: string): string => workspace + item; 25 | 26 | const getCompareRef = (ref: string): string => Utils.isRef(ref) ? Utils.getLocalRefspec(ref, REMOTE_NAME) : ref; 27 | 28 | export const getFileDiff = async(file: FileResult, diffInfo: DiffInfo, dot: string): Promise => { 29 | const stdout = (await command.execAsync({ 30 | command: 'git diff', 31 | args: [ 32 | `${getCompareRef(diffInfo.base)}${dot}${getCompareRef(diffInfo.head)}`, 33 | '--shortstat', 34 | '-w', 35 | file.file, 36 | ], 37 | cwd: Utils.getWorkspace(), 38 | })).stdout; 39 | 40 | if ('' === stdout) { 41 | return {insertions: 0, deletions: 0, lines: 0}; 42 | } 43 | 44 | const insertions = Number.parseInt((stdout.match(/ (\d+) insertions?\(/) ?? ['', '0'])[1]); 45 | const deletions = Number.parseInt((stdout.match(/ (\d+) deletions?\(/) ?? ['', '0'])[1]); 46 | return {insertions, deletions, lines: insertions + deletions}; 47 | }; 48 | 49 | export const getGitDiff = async(logger: Logger, context: Context): Promise> => { 50 | if (!Utils.isCloned(Utils.getWorkspace())) { 51 | logger.warn('Please checkout before call this action.'); 52 | return []; 53 | } 54 | 55 | const dot = getDot(); 56 | const files = getFiles(); 57 | const prefix = getPrefix(); 58 | const suffix = getSuffix(); 59 | const workspace = getWorkspace(); 60 | const diffInfo = await getDiffInfo(Utils.getOctokit(), context); 61 | 62 | if (diffInfo.base === diffInfo.head) { 63 | return []; 64 | } 65 | 66 | const helper = new GitHelper(logger); 67 | helper.useOrigin(REMOTE_NAME); 68 | 69 | const refs = [Utils.normalizeRef(context.ref)]; 70 | if (Utils.isRef(diffInfo.base)) { 71 | refs.push(diffInfo.base); 72 | } 73 | if (Utils.isRef(diffInfo.head)) { 74 | refs.push(diffInfo.head); 75 | } 76 | await helper.fetchOrigin(Utils.getWorkspace(), context, [ 77 | '--no-tags', 78 | '--no-recurse-submodules', 79 | '--depth=10000', 80 | ], Utils.uniqueArray(refs).map(ref => Utils.getRefspec(ref, REMOTE_NAME))); 81 | 82 | return (await Utils.split((await command.execAsync({ 83 | command: 'git diff', 84 | args: [ 85 | `${getCompareRef(diffInfo.base)}${dot}${getCompareRef(diffInfo.head)}`, 86 | '--diff-filter=' + getFilter(), 87 | '--name-only', 88 | ], 89 | cwd: Utils.getWorkspace(), 90 | suppressError: true, 91 | })).stdout) 92 | .map(item => ({ 93 | file: item, 94 | filterIgnored: isFilterIgnored(item, files), 95 | prefixMatched: isPrefixMatched(item, prefix), 96 | suffixMatched: isSuffixMatched(item, suffix), 97 | })) 98 | .filter(item => item.filterIgnored || (item.prefixMatched && item.suffixMatched)) 99 | .map(async item => ({...item, ...await getFileDiff(item, diffInfo, dot)})) 100 | .reduce(async(prev, item) => { 101 | const acc = await prev; 102 | return acc.concat(await item); 103 | }, Promise.resolve([] as Array))) 104 | .map(item => ({...item, file: toAbsolute(item.file, workspace)})); 105 | }; 106 | 107 | export const getDiffFiles = (diffs: FileResult[], filter: boolean): string => escape(diffs.filter(item => !filter || item.prefixMatched && item.suffixMatched).map(item => item.file)).join(getSeparator()); 108 | export const sumResults = (diffs: DiffResult[], map: (item: DiffResult) => number): number => getSummaryIncludeFilesFlag() ? 109 | diffs.map(map).reduce((acc, val) => acc + val, 0) : // eslint-disable-line no-magic-numbers 110 | diffs.filter(item => !item.filterIgnored).map(map).reduce((acc, val) => acc + val, 0); // eslint-disable-line no-magic-numbers 111 | -------------------------------------------------------------------------------- /README.ja.md: -------------------------------------------------------------------------------- 1 | # Get Diff Action 2 | 3 | [![CI Status](https://github.com/technote-space/get-diff-action/workflows/CI/badge.svg)](https://github.com/technote-space/get-diff-action/actions) 4 | [![codecov](https://codecov.io/gh/technote-space/get-diff-action/branch/master/graph/badge.svg)](https://codecov.io/gh/technote-space/get-diff-action) 5 | [![CodeFactor](https://www.codefactor.io/repository/github/technote-space/get-diff-action/badge)](https://www.codefactor.io/repository/github/technote-space/get-diff-action) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/technote-space/get-diff-action/blob/master/LICENSE) 7 | 8 | *Read this in other languages: [English](README.md), [日本語](README.ja.md).* 9 | 10 | これは `git diff` を取得するための GitHub Actions です。 11 | 12 | ## Table of Contents 13 | 14 | 15 | 16 |
17 | Details 18 | 19 | - [スクリーンショット](#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88) 20 | - [使用方法](#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95) 21 | - [動作](#%E5%8B%95%E4%BD%9C) 22 | - [出力](#%E5%87%BA%E5%8A%9B) 23 | - [Action イベント詳細](#action-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E8%A9%B3%E7%B4%B0) 24 | - [対象イベント](#%E5%AF%BE%E8%B1%A1%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88) 25 | - [補足](#%E8%A3%9C%E8%B6%B3) 26 | - [FROM, TO](#from-to) 27 | - [Author](#author) 28 | 29 |
30 | 31 | 32 | ## スクリーンショット 33 | 1. Workflow の例 34 | ![Example workflow](https://raw.githubusercontent.com/technote-space/get-diff-action/images/workflow.png) 35 | 1. スキップ 36 | ![Skip](https://raw.githubusercontent.com/technote-space/get-diff-action/images/skip.png) 37 | 38 | ## 使用方法 39 | ```yaml 40 | on: pull_request 41 | name: CI 42 | jobs: 43 | eslint: 44 | name: ESLint 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v2 48 | - uses: technote-space/get-diff-action@v3 49 | with: 50 | PREFIX_FILTER: | 51 | src 52 | __tests__ 53 | SUFFIX_FILTER: .ts 54 | - name: Install Package dependencies 55 | run: yarn install 56 | if: env.GIT_DIFF 57 | - name: Check code style 58 | # 差分があるソースコードだけチェック 59 | run: yarn eslint ${{ env.GIT_DIFF }} 60 | if: env.GIT_DIFF 61 | 62 | phpmd: 63 | name: PHPMD 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v2 67 | - uses: technote-space/get-diff-action@v3 68 | id: git_diff 69 | with: 70 | SUFFIX_FILTER: .php 71 | SEPARATOR: ',' 72 | - name: Install composer 73 | run: composer install 74 | if: steps.git_diff.outputs.diff 75 | - name: Check code style 76 | # 差分があるソースコードだけチェック 77 | run: vendor/bin/phpmd ${{ steps.git_diff.outputs.diff }} ansi phpmd.xml 78 | if: steps.git_diff.outputs.diff 79 | ``` 80 | 81 | 以下のソースコードに差分がない場合、この Workflow はコードのスタイルチェックをスキップします。 82 | - `src/**/*.ts` 83 | - `__tests__/**/*.ts` 84 | 85 | ## 動作 86 | 1. `git diff` を取得 87 | 88 | ```shell script 89 | git diff ${FROM}${DOT}${TO} '--diff-filter=${DIFF_FILTER}' --name-only 90 | ``` 91 | 92 | 例:(default) 93 | ```yaml 94 | DOT: '...' 95 | DIFF_FILTER: 'AMRC' 96 | ``` 97 | => 98 | ```shell script 99 | git diff ${FROM}...${TO} '--diff-filter=AMRC' --name-only 100 | ``` 101 | => 102 | ``` 103 | .github/workflows/ci.yml 104 | __tests__/utils/command.test.ts 105 | package.json 106 | src/main.ts 107 | src/utils/command.ts 108 | yarn.lock 109 | ``` 110 | 111 | [${FROM}, ${TO}](#from-to) 112 | 113 | 1. `PREFIX_FILTER` や `SUFFIX_FILTER` オプションによるフィルタ 114 | 115 | 例: 116 | ```yaml 117 | SUFFIX_FILTER: .ts 118 | PREFIX_FILTER: src/ 119 | ``` 120 | => 121 | ``` 122 | src/main.ts 123 | src/utils/command.ts 124 | ``` 125 | 126 | 1. `ABSOLUTE` オプションがtrue場合に絶対パスに変換 (default: false) 127 | 128 | 例: 129 | ``` 130 | /home/runner/work/my-repo-name/my-repo-name/src/main.ts 131 | /home/runner/work/my-repo-name/my-repo-name/src/utils/command.ts 132 | ``` 133 | 134 | 1. `SEPARATOR` オプションの値で結合 135 | 136 | 例:(default: false) 137 | ```yaml 138 | SEPARATOR: ' ' 139 | ``` 140 | => 141 | ``` 142 | /home/runner/work/my-repo-name/my-repo-name/src/main.ts /home/runner/work/my-repo-name/my-repo-name/src/utils/command.ts 143 | ``` 144 | 145 | ## 出力 146 | | name | description | e.g. | 147 | |:---:|:---|:---:| 148 | |diff|差分のあるファイルの結果
`SET_ENV_NAME`(default: `GIT_DIFF`) が設定されている場合、その名前で環境変数が設定されます|`src/main.ts src/utils/command.ts`| 149 | |count|差分のあるファイル数
`SET_ENV_NAME_COUNT`(default: `''`) が設定されている場合、その名前で環境変数が設定されます|`100`| 150 | |insertions|追加された行数
`SET_ENV_NAME_INSERTIONS`(default: `''`) が設定されている場合、その名前で環境変数が設定されます|`100`| 151 | |deletions|削除された行数
`SET_ENV_NAME_DELETIONS`(default: `''`) が設定されている場合、その名前で環境変数が設定されます|`100`| 152 | |lines|追加された行数と削除された行数の和
`SET_ENV_NAME_LINES`(default: `''`) が設定されている場合、その名前で環境変数が設定されます|`200`| 153 | 154 | ## Action イベント詳細 155 | ### 対象イベント 156 | | eventName | action | 157 | |:---:|:---:| 158 | |pull_request|opened, reopened, synchronize, closed| 159 | |push|*| 160 | 161 | もしこれ以外のイベントで呼ばれた場合、結果は空になります。 162 | 163 | ## 補足 164 | ### FROM, TO 165 | | condition | FROM | TO | 166 | |:---:|:---:|:---:| 167 | | tag push |---|---| 168 | | pull request | pull.base.ref (e.g. master) | context.ref (e.g. refs/pull/123/merge) | 169 | | push (has related pull request) | pull.base.ref (e.g. master) | `refs/pull/${pull.number}/merge` (e.g. refs/pull/123/merge) | 170 | | context.payload.before = '000...000' | default branch (e.g. master) | context.payload.after | 171 | | else | context.payload.before | context.payload.after | 172 | 173 | ## Author 174 | [GitHub (Technote)](https://github.com/technote-space) 175 | [Blog](https://technote.space) 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Get Diff Action 2 | 3 | [![CI Status](https://github.com/technote-space/get-diff-action/workflows/CI/badge.svg)](https://github.com/technote-space/get-diff-action/actions) 4 | [![codecov](https://codecov.io/gh/technote-space/get-diff-action/branch/master/graph/badge.svg)](https://codecov.io/gh/technote-space/get-diff-action) 5 | [![CodeFactor](https://www.codefactor.io/repository/github/technote-space/get-diff-action/badge)](https://www.codefactor.io/repository/github/technote-space/get-diff-action) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/technote-space/get-diff-action/blob/master/LICENSE) 7 | 8 | *Read this in other languages: [English](README.md), [日本語](README.ja.md).* 9 | 10 | GitHub actions to get git diff. 11 | 12 | ## Table of Contents 13 | 14 | 15 | 16 |
17 | Details 18 | 19 | - [Screenshots](#screenshots) 20 | - [Usage](#usage) 21 | - [Behavior](#behavior) 22 | - [Outputs](#outputs) 23 | - [Action event details](#action-event-details) 24 | - [Target events](#target-events) 25 | - [Addition](#addition) 26 | - [FROM, TO](#from-to) 27 | - [Author](#author) 28 | 29 |
30 | 31 | 32 | ## Screenshots 33 | 1. Example workflow 34 | ![Example workflow](https://raw.githubusercontent.com/technote-space/get-diff-action/images/workflow.png) 35 | 1. Skip 36 | ![Skip](https://raw.githubusercontent.com/technote-space/get-diff-action/images/skip.png) 37 | 38 | ## Usage 39 | ```yaml 40 | on: pull_request 41 | name: CI 42 | jobs: 43 | eslint: 44 | name: ESLint 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v2 48 | - uses: technote-space/get-diff-action@v3 49 | with: 50 | PREFIX_FILTER: | 51 | src 52 | __tests__ 53 | SUFFIX_FILTER: .ts 54 | - name: Install Package dependencies 55 | run: yarn install 56 | if: env.GIT_DIFF 57 | - name: Check code style 58 | # Check only the source codes that have differences 59 | run: yarn eslint ${{ env.GIT_DIFF }} 60 | if: env.GIT_DIFF 61 | 62 | phpmd: 63 | name: PHPMD 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v2 67 | - uses: technote-space/get-diff-action@v3 68 | id: git_diff 69 | with: 70 | SUFFIX_FILTER: .php 71 | SEPARATOR: ',' 72 | - name: Install composer 73 | run: composer install 74 | if: steps.git_diff.outputs.diff 75 | - name: Check code style 76 | # Check only the source codes that have differences 77 | run: vendor/bin/phpmd ${{ steps.git_diff.outputs.diff }} ansi phpmd.xml 78 | if: steps.git_diff.outputs.diff 79 | ``` 80 | 81 | If there is no difference in the source code below, this workflow will skip the code style check 82 | - `src/**/*.ts` 83 | - `__tests__/**/*.ts` 84 | 85 | ## Behavior 86 | 1. Get git diff 87 | 88 | ```shell script 89 | git diff ${FROM}${DOT}${TO} '--diff-filter=${DIFF_FILTER}' --name-only 90 | ``` 91 | 92 | e.g. (default) 93 | ```yaml 94 | DOT: '...' 95 | DIFF_FILTER: 'AMRC' 96 | ``` 97 | => 98 | ```shell script 99 | git diff ${FROM}...${TO} '--diff-filter=AMRC' --name-only 100 | ``` 101 | => 102 | ``` 103 | .github/workflows/ci.yml 104 | __tests__/utils/command.test.ts 105 | package.json 106 | src/main.ts 107 | src/utils/command.ts 108 | yarn.lock 109 | ``` 110 | 111 | [${FROM}, ${TO}](#from-to) 112 | 113 | 1. Filtered by `PREFIX_FILTER` or `SUFFIX_FILTER` option 114 | 115 | e.g. 116 | ```yaml 117 | SUFFIX_FILTER: .ts 118 | PREFIX_FILTER: src/ 119 | ``` 120 | => 121 | ``` 122 | src/main.ts 123 | src/utils/command.ts 124 | ``` 125 | 126 | 1. Mapped to absolute if `ABSOLUTE` option is true (default: false) 127 | 128 | e.g. 129 | ``` 130 | /home/runner/work/my-repo-name/my-repo-name/src/main.ts 131 | /home/runner/work/my-repo-name/my-repo-name/src/utils/command.ts 132 | ``` 133 | 134 | 1. Combined by `SEPARATOR` option 135 | 136 | e.g. (default) 137 | ```yaml 138 | SEPARATOR: ' ' 139 | ``` 140 | => 141 | ``` 142 | /home/runner/work/my-repo-name/my-repo-name/src/main.ts /home/runner/work/my-repo-name/my-repo-name/src/utils/command.ts 143 | ``` 144 | 145 | ## Outputs 146 | | name | description | e.g. | 147 | |:---:|:---|:---:| 148 | |diff|The results of diff file names.
If inputs `SET_ENV_NAME`(default: `GIT_DIFF`) is set, an environment variable is set with that name.|`src/main.ts src/utils/command.ts`| 149 | |count|The number of diff files.
If inputs `SET_ENV_NAME_COUNT`(default: `''`) is set, an environment variable is set with that name.|`100`| 150 | |insertions|The number of insertions lines.
If inputs `SET_ENV_NAME_INSERTIONS`(default: `''`) is set, an environment variable is set with that name.|`100`| 151 | |deletions|The number of deletions lines.
If inputs `SET_ENV_NAME_DELETIONS`(default: `''`) is set, an environment variable is set with that name.|`100`| 152 | |lines|The number of diff lines.
If inputs `SET_ENV_NAME_LINES`(default: `''`) is set, an environment variable is set with that name.|`200`| 153 | 154 | ## Action event details 155 | ### Target events 156 | | eventName | action | 157 | |:---:|:---:| 158 | |pull_request|opened, reopened, synchronize, closed| 159 | |push|*| 160 | 161 | If called on any other event, the result will be empty. 162 | 163 | ## Addition 164 | ### FROM, TO 165 | | condition | FROM | TO | 166 | |:---:|:---:|:---:| 167 | | tag push |---|---| 168 | | pull request | pull.base.ref (e.g. master) | context.ref (e.g. refs/pull/123/merge) | 169 | | push (has related pull request) | pull.base.ref (e.g. master) | `refs/pull/${pull.number}/merge` (e.g. refs/pull/123/merge) | 170 | | context.payload.before = '000...000' | default branch (e.g. master) | context.payload.after | 171 | | else | context.payload.before | context.payload.after | 172 | 173 | ## Author 174 | [GitHub (Technote)](https://github.com/technote-space) 175 | [Blog](https://technote.space) 176 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | name: CI 4 | 5 | jobs: 6 | eslint: 7 | name: ESLint 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 5 10 | env: 11 | LINT: 1 12 | steps: 13 | - uses: technote-space/auto-cancel-redundant-job@v1 14 | with: 15 | EXCLUDE_MERGED: 'true' 16 | - name: Set running flag 17 | run: echo "::set-env name=RUNNING::1" 18 | - uses: actions/checkout@v2 19 | - uses: technote-space/get-git-comment-action@v1 20 | - uses: technote-space/get-diff-action@gh-actions 21 | with: 22 | PREFIX_FILTER: | 23 | src/ 24 | __tests__/ 25 | SUFFIX_FILTER: | 26 | .js 27 | .ts 28 | FILES: .eslintrc 29 | if: "! contains(env.COMMIT_MESSAGE, '[skip ci]') && ! contains(env.COMMIT_MESSAGE, '[ci skip]')" 30 | - name: Set running flag 31 | run: echo "::set-env name=RUNNING::" 32 | if: "! env.GIT_DIFF" 33 | 34 | - name: Get Yarn Cache Directory 35 | id: yarn-cache 36 | run: echo "::set-output name=dir::$(yarn cache dir)" 37 | if: env.RUNNING 38 | - name: Cache node dependencies 39 | uses: actions/cache@v1 40 | with: 41 | path: ${{ steps.yarn-cache.outputs.dir }} 42 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 43 | restore-keys: | 44 | ${{ runner.os }}-yarn- 45 | if: env.RUNNING 46 | - name: Install Package dependencies 47 | run: yarn install 48 | if: env.RUNNING 49 | - name: Check code style 50 | run: yarn eslint ${{ env.GIT_DIFF_FILTERED }} 51 | if: env.RUNNING && env.GIT_DIFF_FILTERED 52 | - name: Check code style 53 | run: yarn lint 54 | if: env.RUNNING && !env.GIT_DIFF_FILTERED 55 | 56 | cover: 57 | name: Coverage 58 | needs: eslint 59 | runs-on: ${{matrix.os}} 60 | timeout-minutes: 10 61 | strategy: 62 | matrix: 63 | os: [ubuntu-latest, ubuntu-16.04, macos-latest] 64 | steps: 65 | - name: Set running flag 66 | run: echo "::set-env name=RUNNING::1" 67 | - uses: actions/checkout@v2 68 | - uses: technote-space/get-git-comment-action@v1 69 | - uses: technote-space/get-diff-action@gh-actions 70 | with: 71 | PREFIX_FILTER: | 72 | src/ 73 | __tests__/ 74 | SUFFIX_FILTER: | 75 | .js 76 | .ts 77 | .snap 78 | FILES: | 79 | yarn.lock 80 | jest.config.js 81 | if: "! contains(env.COMMIT_MESSAGE, '[skip ci]') && ! contains(env.COMMIT_MESSAGE, '[ci skip]')" 82 | - name: Set running flag 83 | run: echo "::set-env name=RUNNING::" 84 | if: "! env.GIT_DIFF" 85 | - name: Set running flag 86 | if: "matrix.os == 'ubuntu-latest' && ! startsWith(github.ref, 'refs/tags/') && github.event.base_ref == format('refs/heads/{0}', github.event.repository.default_branch)" 87 | run: echo "::set-env name=RUNNING::1" 88 | - name: Set running flag 89 | if: "matrix.os == 'ubuntu-latest' && ! startsWith(github.ref, 'refs/tags/') && startsWith(github.base_ref, 'refs/heads/develop/v')" 90 | run: echo "::set-env name=RUNNING::1" 91 | - name: Set running flag 92 | if: matrix.os == 'ubuntu-latest' && startsWith(github.ref, 'refs/tags/v') 93 | run: echo "::set-env name=RUNNING::1" 94 | - name: Set running flag 95 | run: | 96 | if [[ ! -f package.json ]] || ! < package.json jq -r '.scripts | keys[]' | grep -qe '^cover$'; then 97 | echo "::set-env name=RUNNING::" 98 | fi 99 | 100 | - name: Get Yarn Cache Directory 101 | id: yarn-cache 102 | run: echo "::set-output name=dir::$(yarn cache dir)" 103 | if: env.RUNNING 104 | - name: Cache node dependencies 105 | uses: actions/cache@v1 106 | with: 107 | path: ${{ steps.yarn-cache.outputs.dir }} 108 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 109 | restore-keys: | 110 | ${{ runner.os }}-yarn- 111 | if: env.RUNNING 112 | - name: Install Package dependencies 113 | run: yarn install 114 | if: env.RUNNING 115 | - name: Run tests 116 | run: yarn cover 117 | if: env.RUNNING 118 | - name: Codecov 119 | run: | 120 | if [ -n "$CODECOV_TOKEN" ]; then 121 | curl -s https://codecov.io/bash | bash -s -- -t $CODECOV_TOKEN -f $COVERAGE_FILE 122 | fi 123 | env: 124 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 125 | COVERAGE_FILE: ./coverage/lcov.info 126 | if: env.RUNNING && matrix.os == 'ubuntu-latest' 127 | 128 | release: 129 | name: Release GitHub Actions 130 | needs: cover 131 | runs-on: ubuntu-latest 132 | timeout-minutes: 5 133 | if: startsWith(github.ref, 'refs/tags/') 134 | steps: 135 | - uses: actions/checkout@v2 136 | - name: Get Yarn Cache Directory 137 | id: yarn-cache 138 | run: echo "::set-output name=dir::$(yarn cache dir)" 139 | - name: Cache node dependencies 140 | uses: actions/cache@v1 141 | with: 142 | path: ${{ steps.yarn-cache.outputs.dir }} 143 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 144 | restore-keys: | 145 | ${{ runner.os }}-yarn- 146 | 147 | - name: Release GitHub Actions 148 | uses: technote-space/release-github-actions@v6 149 | with: 150 | OUTPUT_BUILD_INFO_FILENAME: build.json 151 | TEST_TAG_PREFIX: test/ 152 | ORIGINAL_TAG_PREFIX: original/ 153 | CLEAN_TEST_TAG: true 154 | 155 | package: 156 | name: Publish Package 157 | needs: cover 158 | runs-on: ubuntu-latest 159 | timeout-minutes: 5 160 | if: startsWith(github.ref, 'refs/tags/v') 161 | strategy: 162 | matrix: 163 | target: ['npm', 'gpr'] 164 | steps: 165 | - name: Set running flag 166 | run: echo "::set-env name=RUNNING::1" 167 | - name: Set running flag 168 | run: | 169 | if [ -z "$NPM_AUTH_TOKEN" ]; then 170 | echo "::set-env name=RUNNING::" 171 | fi 172 | env: 173 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 174 | - uses: actions/checkout@v2 175 | if: env.RUNNING 176 | - name: Set running flag 177 | run: npx can-npm-publish || echo "::set-env name=RUNNING::" 178 | if: env.RUNNING && matrix.target == 'npm' 179 | - name: Set running flag 180 | run: | 181 | LATEST=`npm view . version` 2> /dev/null || : 182 | CURRENT=`cat package.json | jq -r .version` 183 | if [ "$LATEST" = "$CURRENT" ]; then 184 | echo "::set-env name=RUNNING::" 185 | fi 186 | env: 187 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 188 | if: env.RUNNING && matrix.target == 'gpr' 189 | 190 | - name: Setup Node.js 191 | uses: actions/setup-node@v1 192 | with: 193 | node-version: 12 194 | registry-url: https://registry.npmjs.org/ 195 | if: env.RUNNING && matrix.target == 'npm' 196 | - name: Setup Node.js 197 | uses: actions/setup-node@v1 198 | with: 199 | node-version: 12 200 | registry-url: https://npm.pkg.github.com 201 | if: env.RUNNING && matrix.target == 'gpr' 202 | - name: Get Yarn Cache Directory 203 | id: yarn-cache 204 | run: echo "::set-output name=dir::$(yarn cache dir)" 205 | if: env.RUNNING 206 | - name: Cache node dependencies 207 | uses: actions/cache@v1 208 | with: 209 | path: ${{ steps.yarn-cache.outputs.dir }} 210 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 211 | restore-keys: | 212 | ${{ runner.os }}-yarn- 213 | if: env.RUNNING 214 | - name: Check package version 215 | uses: technote-space/package-version-check-action@v1 216 | with: 217 | COMMIT_DISABLED: 1 218 | if: env.RUNNING 219 | - name: Install Package dependencies 220 | run: yarn install 221 | if: env.RUNNING 222 | - name: Build 223 | run: yarn build 224 | if: env.RUNNING 225 | - name: Publish 226 | run: | 227 | npm config set //registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN 228 | npm publish 229 | env: 230 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 231 | if: env.RUNNING && matrix.target == 'npm' 232 | - name: Publish 233 | run: | 234 | npm publish 235 | env: 236 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 237 | if: env.RUNNING && matrix.target == 'gpr' 238 | 239 | publishRelease: 240 | name: Create Release 241 | needs: [release, package] 242 | runs-on: ubuntu-latest 243 | timeout-minutes: 5 244 | steps: 245 | - name: Get version 246 | run: echo "::set-env name=TAG_NAME::${HEAD_REF#refs/tags/}" 247 | env: 248 | HEAD_REF: ${{ github.ref }} 249 | - name: Create Release 250 | id: drafter 251 | uses: technote-space/release-drafter@v6 252 | with: 253 | GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} 254 | DRAFT: false 255 | NAME: ${{ env.TAG_NAME }} 256 | TAG: ${{ env.TAG_NAME }} 257 | - uses: 8398a7/action-slack@v2 258 | with: 259 | status: ${{ job.status }} 260 | text: ${{ format('<{0}>', steps.drafter.outputs.html_url) }} 261 | env: 262 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 263 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 264 | if: success() && env.SLACK_WEBHOOK_URL 265 | 266 | slack: 267 | name: Slack 268 | needs: publishRelease 269 | runs-on: ubuntu-latest 270 | timeout-minutes: 3 271 | if: always() 272 | steps: 273 | - uses: technote-space/workflow-conclusion-action@v1 274 | - uses: 8398a7/action-slack@v2 275 | with: 276 | status: failure 277 | env: 278 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 279 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 280 | if: env.WORKFLOW_CONCLUSION == 'failure' && env.SLACK_WEBHOOK_URL 281 | -------------------------------------------------------------------------------- /__tests__/process.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-magic-numbers */ 2 | import path from 'path'; 3 | import { 4 | testEnv, 5 | spyOnStdout, 6 | stdoutCalledWith, 7 | spyOnSpawn, 8 | testChildProcess, 9 | setChildProcessParams, 10 | execCalledWith, 11 | testFs, 12 | generateContext, 13 | getLogStdout, 14 | } from '@technote-space/github-action-test-helper'; 15 | import {Logger} from '@technote-space/github-action-helper'; 16 | import {dumpDiffs, setResult, execute} from '../src/process'; 17 | 18 | const rootDir = path.resolve(__dirname, '..'); 19 | const diffs = [ 20 | { 21 | file: 'test1', 22 | insertions: 1, 23 | deletions: 100, 24 | lines: 101, 25 | filterIgnored: false, 26 | prefixMatched: true, 27 | suffixMatched: true, 28 | }, 29 | { 30 | file: 'test2', 31 | insertions: 2, 32 | deletions: 200, 33 | lines: 202, 34 | filterIgnored: false, 35 | prefixMatched: true, 36 | suffixMatched: true, 37 | }, 38 | { 39 | file: 'test4', 40 | insertions: 4, 41 | deletions: 400, 42 | lines: 404, 43 | filterIgnored: true, 44 | prefixMatched: true, 45 | suffixMatched: false, 46 | }, 47 | ]; 48 | const setExists = testFs(true); 49 | const logger = new Logger(); 50 | const prContext = generateContext({ 51 | owner: 'hello', 52 | repo: 'world', 53 | event: 'pull_request', 54 | ref: 'refs/pull/55/merge', 55 | }, { 56 | payload: { 57 | number: 11, 58 | 'pull_request': { 59 | number: 11, 60 | id: 21031067, 61 | head: { 62 | ref: 'feature/new-feature', 63 | }, 64 | base: { 65 | ref: 'master', 66 | }, 67 | title: 'title', 68 | 'html_url': 'test url', 69 | }, 70 | }, 71 | }); 72 | 73 | describe('dumpDiffs', () => { 74 | testEnv(rootDir); 75 | 76 | it('should dump output', () => { 77 | const mockStdout = spyOnStdout(); 78 | 79 | dumpDiffs(diffs, logger); 80 | 81 | stdoutCalledWith(mockStdout, [ 82 | '::group::Dump diffs', 83 | getLogStdout([ 84 | { 85 | 'file': 'test1', 86 | 'insertions': 1, 87 | 'deletions': 100, 88 | 'lines': 101, 89 | 'filterIgnored': false, 90 | 'prefixMatched': true, 91 | 'suffixMatched': true, 92 | }, 93 | { 94 | 'file': 'test2', 95 | 'insertions': 2, 96 | 'deletions': 200, 97 | 'lines': 202, 98 | 'filterIgnored': false, 99 | 'prefixMatched': true, 100 | 'suffixMatched': true, 101 | }, 102 | { 103 | 'file': 'test4', 104 | 'insertions': 4, 105 | 'deletions': 400, 106 | 'lines': 404, 107 | 'filterIgnored': true, 108 | 'prefixMatched': true, 109 | 'suffixMatched': false, 110 | }, 111 | ]), 112 | '::endgroup::', 113 | ]); 114 | }); 115 | }); 116 | 117 | describe('setResult', () => { 118 | testEnv(rootDir); 119 | 120 | it('should set result', () => { 121 | const mockStdout = spyOnStdout(); 122 | 123 | setResult(diffs, false, logger); 124 | 125 | stdoutCalledWith(mockStdout, [ 126 | '::group::Dump output', 127 | '::set-output name=diff::test1 test2 test4', 128 | '::set-env name=GIT_DIFF::test1 test2 test4', 129 | '"diff: test1 test2 test4"', 130 | '::set-output name=filtered_diff::test1 test2', 131 | '::set-env name=GIT_DIFF_FILTERED::test1 test2', 132 | '"filtered_diff: test1 test2"', 133 | '::set-output name=count::3', 134 | '"count: 3"', 135 | '::set-output name=insertions::3', 136 | '"insertions: 3"', 137 | '::set-output name=deletions::300', 138 | '"deletions: 300"', 139 | '::set-output name=lines::303', 140 | '"lines: 303"', 141 | '::endgroup::', 142 | ]); 143 | }); 144 | 145 | it('should set result without env', () => { 146 | process.env.INPUT_SET_ENV_NAME = ''; 147 | process.env.INPUT_SET_ENV_NAME_COUNT = 'FILE_COUNT'; 148 | process.env.INPUT_SET_ENV_NAME_INSERTIONS = 'INSERTIONS'; 149 | process.env.INPUT_SET_ENV_NAME_DELETIONS = 'DELETIONS'; 150 | process.env.INPUT_SET_ENV_NAME_LINES = 'LINES'; 151 | const mockStdout = spyOnStdout(); 152 | 153 | setResult(diffs, false, logger); 154 | 155 | stdoutCalledWith(mockStdout, [ 156 | '::group::Dump output', 157 | '::set-output name=diff::test1 test2 test4', 158 | '"diff: test1 test2 test4"', 159 | '::set-output name=filtered_diff::test1 test2', 160 | '::set-env name=GIT_DIFF_FILTERED::test1 test2', 161 | '"filtered_diff: test1 test2"', 162 | '::set-output name=count::3', 163 | '::set-env name=FILE_COUNT::3', 164 | '"count: 3"', 165 | '::set-output name=insertions::3', 166 | '::set-env name=INSERTIONS::3', 167 | '"insertions: 3"', 168 | '::set-output name=deletions::300', 169 | '::set-env name=DELETIONS::300', 170 | '"deletions: 300"', 171 | '::set-output name=lines::303', 172 | '::set-env name=LINES::303', 173 | '"lines: 303"', 174 | '::endgroup::', 175 | ]); 176 | }); 177 | }); 178 | 179 | describe('execute', () => { 180 | testEnv(rootDir); 181 | testChildProcess(); 182 | 183 | it('should execute', async() => { 184 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 185 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 186 | 187 | const mockExec = spyOnSpawn(); 188 | const mockStdout = spyOnStdout(); 189 | setChildProcessParams({ 190 | stdout: (command: string): string => { 191 | if (command.startsWith('git diff')) { 192 | if (command.includes('shortstat')) { 193 | return '1 file changed, 25 insertions(+), 4 deletions(-)'; 194 | } 195 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 196 | } 197 | return ''; 198 | }, 199 | }); 200 | 201 | await execute(logger, prContext); 202 | 203 | execCalledWith(mockExec, [ 204 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 205 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/pull/55/merge:refs/pull/55/merge\' \'refs/heads/master:refs/remotes/get-diff-action/master\' || :', 206 | 'git diff \'get-diff-action/master...pull/55/merge\' \'--diff-filter=AMRC\' --name-only || :', 207 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'package.json\'', 208 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'abc/composer.json\'', 209 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'README.md\'', 210 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'src/main.ts\'', 211 | ]); 212 | stdoutCalledWith(mockStdout, [ 213 | '[command]git remote add get-diff-action', 214 | '[command]git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/pull/55/merge:refs/pull/55/merge\' \'refs/heads/master:refs/remotes/get-diff-action/master\'', 215 | '[command]git diff \'get-diff-action/master...pull/55/merge\' \'--diff-filter=AMRC\' --name-only', 216 | ' >> package.json', 217 | ' >> abc/composer.json', 218 | ' >> README.md', 219 | ' >> src/main.ts', 220 | '[command]git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'package.json\'', 221 | ' >> 1 file changed, 25 insertions(+), 4 deletions(-)', 222 | '[command]git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'abc/composer.json\'', 223 | ' >> 1 file changed, 25 insertions(+), 4 deletions(-)', 224 | '[command]git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'README.md\'', 225 | ' >> 1 file changed, 25 insertions(+), 4 deletions(-)', 226 | '[command]git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'src/main.ts\'', 227 | ' >> 1 file changed, 25 insertions(+), 4 deletions(-)', 228 | '::group::Dump diffs', 229 | getLogStdout([ 230 | { 231 | 'file': 'package.json', 232 | 'filterIgnored': false, 233 | 'prefixMatched': true, 234 | 'suffixMatched': true, 235 | 'insertions': 25, 236 | 'deletions': 4, 237 | 'lines': 29, 238 | }, 239 | { 240 | 'file': 'abc/composer.json', 241 | 'filterIgnored': false, 242 | 'prefixMatched': true, 243 | 'suffixMatched': true, 244 | 'insertions': 25, 245 | 'deletions': 4, 246 | 'lines': 29, 247 | }, 248 | { 249 | 'file': 'README.md', 250 | 'filterIgnored': false, 251 | 'prefixMatched': true, 252 | 'suffixMatched': true, 253 | 'insertions': 25, 254 | 'deletions': 4, 255 | 'lines': 29, 256 | }, 257 | { 258 | 'file': 'src/main.ts', 259 | 'filterIgnored': false, 260 | 'prefixMatched': true, 261 | 'suffixMatched': true, 262 | 'insertions': 25, 263 | 'deletions': 4, 264 | 'lines': 29, 265 | }, 266 | ]), 267 | '::endgroup::', 268 | '::group::Dump output', 269 | '::set-output name=diff::\'package.json\' \'abc/composer.json\' \'README.md\' \'src/main.ts\'', 270 | '::set-env name=GIT_DIFF::\'package.json\' \'abc/composer.json\' \'README.md\' \'src/main.ts\'', 271 | '"diff: \'package.json\' \'abc/composer.json\' \'README.md\' \'src/main.ts\'"', 272 | '::set-output name=filtered_diff::\'package.json\' \'abc/composer.json\' \'README.md\' \'src/main.ts\'', 273 | '::set-env name=GIT_DIFF_FILTERED::\'package.json\' \'abc/composer.json\' \'README.md\' \'src/main.ts\'', 274 | '"filtered_diff: \'package.json\' \'abc/composer.json\' \'README.md\' \'src/main.ts\'"', 275 | '::set-output name=count::4', 276 | '"count: 4"', 277 | '::set-output name=insertions::100', 278 | '"insertions: 100"', 279 | '::set-output name=deletions::16', 280 | '"deletions: 16"', 281 | '::set-output name=lines::116', 282 | '"lines: 116"', 283 | '::endgroup::', 284 | ]); 285 | }); 286 | 287 | it('should execute (no diff)', async() => { 288 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 289 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 290 | process.env.INPUT_PREFIX_FILTER = 'test/'; 291 | 292 | const mockExec = spyOnSpawn(); 293 | const mockStdout = spyOnStdout(); 294 | setChildProcessParams({ 295 | stdout: (command: string): string => { 296 | if (command.startsWith('git diff')) { 297 | if (command.includes('shortstat')) { 298 | return '1 file changed, 25 insertions(+), 4 deletions(-)'; 299 | } 300 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 301 | } 302 | return ''; 303 | }, 304 | }); 305 | 306 | await execute(logger, prContext); 307 | 308 | execCalledWith(mockExec, [ 309 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 310 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/pull/55/merge:refs/pull/55/merge\' \'refs/heads/master:refs/remotes/get-diff-action/master\' || :', 311 | 'git diff \'get-diff-action/master...pull/55/merge\' \'--diff-filter=AMRC\' --name-only || :', 312 | ]); 313 | stdoutCalledWith(mockStdout, [ 314 | '[command]git remote add get-diff-action', 315 | '[command]git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/pull/55/merge:refs/pull/55/merge\' \'refs/heads/master:refs/remotes/get-diff-action/master\'', 316 | '[command]git diff \'get-diff-action/master...pull/55/merge\' \'--diff-filter=AMRC\' --name-only', 317 | ' >> package.json', 318 | ' >> abc/composer.json', 319 | ' >> README.md', 320 | ' >> src/main.ts', 321 | '::group::Dump diffs', 322 | '[]', 323 | '::endgroup::', 324 | '::group::Dump output', 325 | '::set-output name=diff::', 326 | '::set-env name=GIT_DIFF::', 327 | '"diff: "', 328 | '::set-output name=filtered_diff::', 329 | '::set-env name=GIT_DIFF_FILTERED::', 330 | '"filtered_diff: "', 331 | '::set-output name=count::0', 332 | '"count: 0"', 333 | '::set-output name=insertions::0', 334 | '"insertions: 0"', 335 | '::set-output name=deletions::0', 336 | '"deletions: 0"', 337 | '::set-output name=lines::0', 338 | '"lines: 0"', 339 | '::endgroup::', 340 | ]); 341 | }); 342 | 343 | it('should execute empty', async() => { 344 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 345 | 346 | const mockExec = spyOnSpawn(); 347 | const mockStdout = spyOnStdout(); 348 | 349 | await execute(logger, prContext, true); 350 | 351 | execCalledWith(mockExec, []); 352 | stdoutCalledWith(mockStdout, [ 353 | '::group::Dump diffs', 354 | '[]', 355 | '::endgroup::', 356 | '::group::Dump output', 357 | '::set-output name=diff::', 358 | '::set-env name=GIT_DIFF::', 359 | '"diff: "', 360 | '::set-output name=filtered_diff::', 361 | '::set-env name=GIT_DIFF_FILTERED::', 362 | '"filtered_diff: "', 363 | '::set-output name=count::0', 364 | '"count: 0"', 365 | '::set-output name=insertions::0', 366 | '"insertions: 0"', 367 | '::set-output name=deletions::0', 368 | '"deletions: 0"', 369 | '::set-output name=lines::0', 370 | '"lines: 0"', 371 | '::endgroup::', 372 | ]); 373 | }); 374 | 375 | it('should execute empty with default value', async() => { 376 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 377 | process.env.INPUT_DIFF_DEFAULT = '1'; 378 | process.env.INPUT_FILTERED_DIFF_DEFAULT = '2'; 379 | process.env.INPUT_COUNT_DEFAULT = '3'; 380 | process.env.INPUT_INSERTIONS_DEFAULT = '4'; 381 | process.env.INPUT_DELETIONS_DEFAULT = '5'; 382 | process.env.INPUT_LINES_DEFAULT = '6'; 383 | 384 | const mockExec = spyOnSpawn(); 385 | const mockStdout = spyOnStdout(); 386 | 387 | await execute(logger, prContext, true); 388 | 389 | execCalledWith(mockExec, []); 390 | stdoutCalledWith(mockStdout, [ 391 | '::group::Dump diffs', 392 | '[]', 393 | '::endgroup::', 394 | '::group::Dump output', 395 | '::set-output name=diff::1', 396 | '::set-env name=GIT_DIFF::1', 397 | '"diff: 1"', 398 | '::set-output name=filtered_diff::2', 399 | '::set-env name=GIT_DIFF_FILTERED::2', 400 | '"filtered_diff: 2"', 401 | '::set-output name=count::3', 402 | '"count: 3"', 403 | '::set-output name=insertions::4', 404 | '"insertions: 4"', 405 | '::set-output name=deletions::5', 406 | '"deletions: 5"', 407 | '::set-output name=lines::6', 408 | '"lines: 6"', 409 | '::endgroup::', 410 | ]); 411 | }); 412 | 413 | it('should not execute if not cloned', async() => { 414 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 415 | 416 | const mockExec = spyOnSpawn(); 417 | const mockStdout = spyOnStdout(); 418 | setChildProcessParams({ 419 | stdout: (command: string): string => { 420 | if (command.startsWith('git diff')) { 421 | if (command.includes('shortstat')) { 422 | return '1 file changed, 25 insertions(+), 4 deletions(-)'; 423 | } 424 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 425 | } 426 | return ''; 427 | }, 428 | }); 429 | setExists(false); 430 | 431 | await execute(logger, prContext); 432 | 433 | execCalledWith(mockExec, []); 434 | stdoutCalledWith(mockStdout, [ 435 | '::warning::Please checkout before call this action.', 436 | '::group::Dump diffs', 437 | '[]', 438 | '::endgroup::', 439 | '::group::Dump output', 440 | '::set-output name=diff::', 441 | '::set-env name=GIT_DIFF::', 442 | '"diff: "', 443 | '::set-output name=filtered_diff::', 444 | '::set-env name=GIT_DIFF_FILTERED::', 445 | '"filtered_diff: "', 446 | '::set-output name=count::0', 447 | '"count: 0"', 448 | '::set-output name=insertions::0', 449 | '"insertions: 0"', 450 | '::set-output name=deletions::0', 451 | '"deletions: 0"', 452 | '::set-output name=lines::0', 453 | '"lines: 0"', 454 | '::endgroup::', 455 | ]); 456 | }); 457 | }); 458 | -------------------------------------------------------------------------------- /__tests__/utils/command.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-magic-numbers */ 2 | import nock from 'nock'; 3 | import path, {resolve} from 'path'; 4 | import { 5 | generateContext, 6 | testEnv, 7 | spyOnSpawn, 8 | testChildProcess, 9 | execCalledWith, 10 | setChildProcessParams, 11 | testFs, 12 | disableNetConnect, 13 | getApiFixture, 14 | } from '@technote-space/github-action-test-helper'; 15 | import {Logger} from '@technote-space/github-action-helper'; 16 | import {getGitDiff, getFileDiff, getDiffFiles, sumResults} from '../../src/utils/command'; 17 | 18 | const rootDir = path.resolve(__dirname, '../..'); 19 | const fixtureRootDir = resolve(__dirname, '..', 'fixtures'); 20 | const defaultFileResult = {filterIgnored: false, prefixMatched: true, suffixMatched: true}; 21 | const diffs = [ 22 | {file: 'test1', insertions: 1, deletions: 100, lines: 101, ...defaultFileResult}, 23 | {file: 'test2', insertions: 2, deletions: 200, lines: 202, ...defaultFileResult}, 24 | { 25 | file: 'test4', 26 | insertions: 4, 27 | deletions: 400, 28 | lines: 404, 29 | filterIgnored: true, 30 | prefixMatched: true, 31 | suffixMatched: false, 32 | }, 33 | ]; 34 | const emptyDiff = {insertions: 0, deletions: 0, lines: 0, ...defaultFileResult}; 35 | const logger = new Logger(); 36 | const prContext = generateContext({ 37 | owner: 'hello', 38 | repo: 'world', 39 | event: 'pull_request', 40 | ref: 'refs/pull/55/merge', 41 | action: 'synchronize', 42 | }, { 43 | payload: { 44 | number: 11, 45 | 'pull_request': { 46 | number: 11, 47 | id: 21031067, 48 | head: { 49 | ref: 'feature/new-feature', 50 | sha: 'head-sha', 51 | }, 52 | base: { 53 | ref: 'master', 54 | sha: 'base-sha', 55 | }, 56 | title: 'title', 57 | 'html_url': 'test url', 58 | }, 59 | }, 60 | }); 61 | const pushContext = generateContext({ 62 | owner: 'hello', 63 | repo: 'world', 64 | event: 'push', 65 | ref: 'refs/heads/master', 66 | sha: 'sha', 67 | }, { 68 | payload: { 69 | before: 'before-sha', 70 | after: 'after-sha', 71 | repository: { 72 | 'default_branch': 'master', 73 | }, 74 | }, 75 | }); 76 | testFs(true); 77 | 78 | describe('getGitDiff', () => { 79 | testEnv(rootDir); 80 | testChildProcess(); 81 | disableNetConnect(nock); 82 | 83 | it('should get git diff (pull request)', async() => { 84 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 85 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 86 | process.env.INPUT_SUFFIX_FILTER = 'json\nmd\nts'; 87 | 88 | const mockExec = spyOnSpawn(); 89 | setChildProcessParams({ 90 | stdout: (command: string): string => { 91 | if (command.startsWith('git diff')) { 92 | return 'package.json\nabc/composer.JSON\nREADME.md\nsrc/main.ts'; 93 | } 94 | return ''; 95 | }, 96 | }); 97 | 98 | expect(await getGitDiff(logger, prContext)).toEqual([ 99 | {file: 'package.json', ...emptyDiff}, 100 | {file: 'abc/composer.JSON', ...emptyDiff}, 101 | {file: 'README.md', ...emptyDiff}, 102 | {file: 'src/main.ts', ...emptyDiff}, 103 | ]); 104 | execCalledWith(mockExec, [ 105 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 106 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/pull/55/merge:refs/pull/55/merge\' \'refs/heads/master:refs/remotes/get-diff-action/master\' || :', 107 | 'git diff \'get-diff-action/master...pull/55/merge\' \'--diff-filter=AMRC\' --name-only || :', 108 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'package.json\'', 109 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'abc/composer.JSON\'', 110 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'README.md\'', 111 | 'git diff \'get-diff-action/master...pull/55/merge\' --shortstat -w \'src/main.ts\'', 112 | ]); 113 | }); 114 | 115 | it('should get git diff (pull request closed)', async() => { 116 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 117 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 118 | process.env.INPUT_SUFFIX_FILTER = 'json\nmd\nts'; 119 | process.env.INPUT_SUFFIX_FILTER_FLAGS = ''; 120 | 121 | const mockExec = spyOnSpawn(); 122 | setChildProcessParams({ 123 | stdout: (command: string): string => { 124 | if (command.startsWith('git diff')) { 125 | return 'package.json\nabc/composer.JSON\nREADME.md\nsrc/main.ts'; 126 | } 127 | return ''; 128 | }, 129 | }); 130 | 131 | expect(await getGitDiff(logger, generateContext({ 132 | owner: 'hello', 133 | repo: 'world', 134 | event: 'pull_request', 135 | ref: 'master', 136 | sha: 'sha', 137 | action: 'closed', 138 | }, { 139 | payload: { 140 | number: 11, 141 | 'pull_request': { 142 | number: 11, 143 | id: 21031067, 144 | head: { 145 | ref: 'feature/new-feature', 146 | sha: 'head-sha', 147 | }, 148 | base: { 149 | ref: 'master', 150 | sha: 'base-sha', 151 | }, 152 | title: 'title', 153 | 'html_url': 'test url', 154 | }, 155 | }, 156 | }))).toEqual([ 157 | {file: 'package.json', ...emptyDiff}, 158 | {file: 'README.md', ...emptyDiff}, 159 | {file: 'src/main.ts', ...emptyDiff}, 160 | ]); 161 | execCalledWith(mockExec, [ 162 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 163 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/heads/master:refs/remotes/get-diff-action/master\' || :', 164 | 'git diff \'base-sha...sha\' \'--diff-filter=AMRC\' --name-only || :', 165 | 'git diff \'base-sha...sha\' --shortstat -w \'package.json\'', 166 | 'git diff \'base-sha...sha\' --shortstat -w \'README.md\'', 167 | 'git diff \'base-sha...sha\' --shortstat -w \'src/main.ts\'', 168 | ]); 169 | }); 170 | 171 | it('should get git diff (push, default branch)', async() => { 172 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 173 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 174 | 175 | const mockExec = spyOnSpawn(); 176 | setChildProcessParams({ 177 | stdout: (command: string): string => { 178 | if (command.startsWith('git diff')) { 179 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 180 | } 181 | return ''; 182 | }, 183 | }); 184 | 185 | nock('https://api.github.com') 186 | .persist() 187 | .get('/repos/hello/world/commits/before-sha/pulls') 188 | .reply(200, () => getApiFixture(fixtureRootDir, 'pulls.list1')); 189 | 190 | expect(await getGitDiff(logger, pushContext)).toEqual([ 191 | {file: 'package.json', ...emptyDiff}, 192 | {file: 'abc/composer.json', ...emptyDiff}, 193 | {file: 'README.md', ...emptyDiff}, 194 | {file: 'src/main.ts', ...emptyDiff}, 195 | ]); 196 | execCalledWith(mockExec, [ 197 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 198 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/heads/master:refs/remotes/get-diff-action/master\' || :', 199 | 'git diff \'before-sha...after-sha\' \'--diff-filter=AMRC\' --name-only || :', 200 | 'git diff \'before-sha...after-sha\' --shortstat -w \'package.json\'', 201 | 'git diff \'before-sha...after-sha\' --shortstat -w \'abc/composer.json\'', 202 | 'git diff \'before-sha...after-sha\' --shortstat -w \'README.md\'', 203 | 'git diff \'before-sha...after-sha\' --shortstat -w \'src/main.ts\'', 204 | ]); 205 | }); 206 | 207 | it('should get git diff (push, found pr)', async() => { 208 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 209 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 210 | 211 | const mockExec = spyOnSpawn(); 212 | setChildProcessParams({ 213 | stdout: (command: string): string => { 214 | if (command.startsWith('git diff')) { 215 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 216 | } 217 | return ''; 218 | }, 219 | }); 220 | 221 | nock('https://api.github.com') 222 | .persist() 223 | .get('/repos/hello/world/pulls?head=hello%3Atest') 224 | .reply(200, () => getApiFixture(fixtureRootDir, 'pulls.list1')); 225 | 226 | expect(await getGitDiff(logger, Object.assign({}, pushContext, { 227 | ref: 'refs/heads/test', 228 | }))).toEqual([ 229 | {file: 'package.json', ...emptyDiff}, 230 | {file: 'abc/composer.json', ...emptyDiff}, 231 | {file: 'README.md', ...emptyDiff}, 232 | {file: 'src/main.ts', ...emptyDiff}, 233 | ]); 234 | execCalledWith(mockExec, [ 235 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 236 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/heads/test:refs/remotes/get-diff-action/test\' \'refs/heads/master:refs/remotes/get-diff-action/master\' \'refs/pull/1347/merge:refs/pull/1347/merge\' || :', 237 | 'git diff \'get-diff-action/master...pull/1347/merge\' \'--diff-filter=AMRC\' --name-only || :', 238 | 'git diff \'get-diff-action/master...pull/1347/merge\' --shortstat -w \'package.json\'', 239 | 'git diff \'get-diff-action/master...pull/1347/merge\' --shortstat -w \'abc/composer.json\'', 240 | 'git diff \'get-diff-action/master...pull/1347/merge\' --shortstat -w \'README.md\'', 241 | 'git diff \'get-diff-action/master...pull/1347/merge\' --shortstat -w \'src/main.ts\'', 242 | ]); 243 | }); 244 | 245 | it('should get git diff (push, not found pr)', async() => { 246 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 247 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 248 | 249 | const mockExec = spyOnSpawn(); 250 | setChildProcessParams({ 251 | stdout: (command: string): string => { 252 | if (command.startsWith('git diff')) { 253 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 254 | } 255 | return ''; 256 | }, 257 | }); 258 | 259 | nock('https://api.github.com') 260 | .persist() 261 | .get('/repos/hello/world/pulls?head=hello%3Atest') 262 | .reply(200, () => []); 263 | 264 | expect(await getGitDiff(logger, Object.assign({}, pushContext, { 265 | ref: 'refs/heads/test', 266 | }))).toEqual([ 267 | {file: 'package.json', ...emptyDiff}, 268 | {file: 'abc/composer.json', ...emptyDiff}, 269 | {file: 'README.md', ...emptyDiff}, 270 | {file: 'src/main.ts', ...emptyDiff}, 271 | ]); 272 | execCalledWith(mockExec, [ 273 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 274 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/heads/test:refs/remotes/get-diff-action/test\' || :', 275 | 'git diff \'before-sha...after-sha\' \'--diff-filter=AMRC\' --name-only || :', 276 | 'git diff \'before-sha...after-sha\' --shortstat -w \'package.json\'', 277 | 'git diff \'before-sha...after-sha\' --shortstat -w \'abc/composer.json\'', 278 | 'git diff \'before-sha...after-sha\' --shortstat -w \'README.md\'', 279 | 'git diff \'before-sha...after-sha\' --shortstat -w \'src/main.ts\'', 280 | ]); 281 | }); 282 | 283 | it('should get git diff (push tag)', async() => { 284 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 285 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 286 | 287 | const mockExec = spyOnSpawn(); 288 | setChildProcessParams({ 289 | stdout: (command: string): string => { 290 | if (command.startsWith('git diff')) { 291 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 292 | } 293 | return ''; 294 | }, 295 | }); 296 | 297 | expect(await getGitDiff(logger, Object.assign({}, pushContext, { 298 | ref: 'refs/tags/v1.2.3', 299 | }))).toEqual([]); 300 | execCalledWith(mockExec, []); 301 | }); 302 | 303 | it('should get git diff (push, create new branch)', async() => { 304 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 305 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 306 | 307 | const mockExec = spyOnSpawn(); 308 | setChildProcessParams({ 309 | stdout: (command: string): string => { 310 | if (command.startsWith('git diff')) { 311 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts'; 312 | } 313 | return ''; 314 | }, 315 | }); 316 | 317 | nock('https://api.github.com') 318 | .persist() 319 | .get('/repos/hello/world/pulls?head=hello%3Atest') 320 | .reply(200, () => []); 321 | 322 | expect(await getGitDiff(logger, Object.assign({}, pushContext, { 323 | ref: 'refs/heads/test', 324 | payload: { 325 | before: '0000000000000000000000000000000000000000', 326 | after: 'after-sha', 327 | repository: { 328 | 'default_branch': 'master', 329 | }, 330 | }, 331 | }))).toEqual([ 332 | {file: 'package.json', ...emptyDiff}, 333 | {file: 'abc/composer.json', ...emptyDiff}, 334 | {file: 'README.md', ...emptyDiff}, 335 | {file: 'src/main.ts', ...emptyDiff}, 336 | ]); 337 | execCalledWith(mockExec, [ 338 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 339 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/heads/test:refs/remotes/get-diff-action/test\' \'refs/heads/master:refs/remotes/get-diff-action/master\' || :', 340 | 'git diff \'get-diff-action/master...after-sha\' \'--diff-filter=AMRC\' --name-only || :', 341 | 'git diff \'get-diff-action/master...after-sha\' --shortstat -w \'package.json\'', 342 | 'git diff \'get-diff-action/master...after-sha\' --shortstat -w \'abc/composer.json\'', 343 | 'git diff \'get-diff-action/master...after-sha\' --shortstat -w \'README.md\'', 344 | 'git diff \'get-diff-action/master...after-sha\' --shortstat -w \'src/main.ts\'', 345 | ]); 346 | }); 347 | 348 | it('should get git diff (env)', async() => { 349 | process.env.GITHUB_WORKSPACE = '/home/runner/work/my-repo-name/my-repo-name'; 350 | process.env.INPUT_GITHUB_TOKEN = 'test token'; 351 | process.env.INPUT_DOT = '..'; 352 | process.env.INPUT_DIFF_FILTER = 'AMD'; 353 | process.env.INPUT_FILES = 'package.json\ncomposer.json\nREADME2.md'; 354 | process.env.INPUT_PREFIX_FILTER = 'src/\n__tests__'; 355 | process.env.INPUT_SUFFIX_FILTER = '.ts\n.txt'; 356 | process.env.INPUT_ABSOLUTE = 'true'; 357 | process.env.INPUT_SET_ENV_NAME = ''; 358 | const mockExec = spyOnSpawn(); 359 | setChildProcessParams({ 360 | stdout: (command: string): string => { 361 | if (command.startsWith('git diff')) { 362 | return 'package.json\nabc/composer.json\nREADME.md\nsrc/main.ts\nsrc/test1.tts\nsrc/test/test2.txt\n__tests__/main.test.ts'; 363 | } 364 | return ''; 365 | }, 366 | }); 367 | 368 | expect(await getGitDiff(logger, prContext)).toEqual([ 369 | { 370 | file: process.env.GITHUB_WORKSPACE + '/package.json', ...emptyDiff, 371 | filterIgnored: true, 372 | prefixMatched: false, 373 | suffixMatched: false, 374 | }, 375 | { 376 | file: process.env.GITHUB_WORKSPACE + '/abc/composer.json', ...emptyDiff, 377 | filterIgnored: true, 378 | prefixMatched: false, 379 | suffixMatched: false, 380 | }, 381 | {file: process.env.GITHUB_WORKSPACE + '/src/main.ts', ...emptyDiff}, 382 | {file: process.env.GITHUB_WORKSPACE + '/src/test/test2.txt', ...emptyDiff}, 383 | {file: process.env.GITHUB_WORKSPACE + '/__tests__/main.test.ts', ...emptyDiff}, 384 | ]); 385 | execCalledWith(mockExec, [ 386 | 'git remote add get-diff-action \'https://octocat:test token@github.com/hello/world.git\' > /dev/null 2>&1 || :', 387 | 'git fetch --no-tags --no-recurse-submodules \'--depth=10000\' get-diff-action \'refs/pull/55/merge:refs/pull/55/merge\' \'refs/heads/master:refs/remotes/get-diff-action/master\' || :', 388 | 'git diff \'get-diff-action/master..pull/55/merge\' \'--diff-filter=AMD\' --name-only || :', 389 | 'git diff \'get-diff-action/master..pull/55/merge\' --shortstat -w \'package.json\'', 390 | 'git diff \'get-diff-action/master..pull/55/merge\' --shortstat -w \'abc/composer.json\'', 391 | 'git diff \'get-diff-action/master..pull/55/merge\' --shortstat -w \'src/main.ts\'', 392 | 'git diff \'get-diff-action/master..pull/55/merge\' --shortstat -w \'src/test/test2.txt\'', 393 | 'git diff \'get-diff-action/master..pull/55/merge\' --shortstat -w \'__tests__/main.test.ts\'', 394 | ]); 395 | }); 396 | }); 397 | 398 | describe('getFileDiff', () => { 399 | it('should get file diff 1', async() => { 400 | const mockExec = spyOnSpawn(); 401 | setChildProcessParams({ 402 | stdout: '1 file changed, 25 insertions(+), 4 deletions(-)', 403 | }); 404 | 405 | const diff = await getFileDiff({file: 'test.js', ...defaultFileResult}, { 406 | base: 'refs/heads/master', 407 | head: 'refs/pull/123/merge', 408 | }, '...'); 409 | 410 | expect(diff.insertions).toBe(25); 411 | expect(diff.deletions).toBe(4); 412 | expect(diff.lines).toBe(29); 413 | 414 | execCalledWith(mockExec, [ 415 | 'git diff \'get-diff-action/master...pull/123/merge\' --shortstat -w \'test.js\'', 416 | ]); 417 | }); 418 | 419 | it('should get file diff 2', async() => { 420 | const mockExec = spyOnSpawn(); 421 | setChildProcessParams({ 422 | stdout: '1 file changed, 1 insertion(+), 3 deletions(-)', 423 | }); 424 | 425 | const diff = await getFileDiff({file: 'test.js', ...defaultFileResult}, { 426 | base: 'refs/heads/master', 427 | head: 'refs/pull/123/merge', 428 | }, '...'); 429 | 430 | expect(diff.insertions).toBe(1); 431 | expect(diff.deletions).toBe(3); 432 | expect(diff.lines).toBe(4); 433 | 434 | execCalledWith(mockExec, [ 435 | 'git diff \'get-diff-action/master...pull/123/merge\' --shortstat -w \'test.js\'', 436 | ]); 437 | }); 438 | 439 | it('should get file diff 3', async() => { 440 | const mockExec = spyOnSpawn(); 441 | setChildProcessParams({ 442 | stdout: '1 file changed, 3 insertions(+)', 443 | }); 444 | 445 | const diff = await getFileDiff({file: 'test.js', ...defaultFileResult}, { 446 | base: 'refs/heads/master', 447 | head: 'refs/pull/123/merge', 448 | }, '...'); 449 | 450 | expect(diff.insertions).toBe(3); 451 | expect(diff.deletions).toBe(0); 452 | expect(diff.lines).toBe(3); 453 | 454 | execCalledWith(mockExec, [ 455 | 'git diff \'get-diff-action/master...pull/123/merge\' --shortstat -w \'test.js\'', 456 | ]); 457 | }); 458 | 459 | it('should return empty', async() => { 460 | const mockExec = spyOnSpawn(); 461 | setChildProcessParams({ 462 | stdout: '', 463 | }); 464 | 465 | const diff = await getFileDiff({file: 'test.js', ...defaultFileResult}, { 466 | base: 'refs/heads/master', 467 | head: 'refs/pull/123/merge', 468 | }, '...'); 469 | 470 | expect(diff.insertions).toBe(0); 471 | expect(diff.deletions).toBe(0); 472 | expect(diff.lines).toBe(0); 473 | 474 | execCalledWith(mockExec, [ 475 | 'git diff \'get-diff-action/master...pull/123/merge\' --shortstat -w \'test.js\'', 476 | ]); 477 | }); 478 | }); 479 | 480 | describe('getDiffFiles', () => { 481 | testEnv(rootDir); 482 | 483 | it('get git diff output 1', () => { 484 | expect(getDiffFiles([], false)).toEqual(''); 485 | expect(getDiffFiles([{file: 'test1', ...defaultFileResult}], false)).toEqual('test1'); 486 | expect(getDiffFiles([{file: 'test1', ...defaultFileResult}, {file: 'test2', ...defaultFileResult}], false)).toEqual('test1 test2'); 487 | expect(getDiffFiles([{file: 'test1', ...defaultFileResult}, {file: 'test2 test3', ...defaultFileResult}], false)).toEqual('test1 \'test2 test3\''); 488 | expect(getDiffFiles([{file: 'test1/test2.txt', ...defaultFileResult}], false)).toEqual('\'test1/test2.txt\''); 489 | }); 490 | 491 | it('get git diff output 2', () => { 492 | process.env.INPUT_SEPARATOR = '\n'; 493 | 494 | expect(getDiffFiles([], false)).toEqual(''); 495 | expect(getDiffFiles([{file: 'test1', ...defaultFileResult}], false)).toEqual('test1'); 496 | expect(getDiffFiles([{file: 'test1', ...defaultFileResult}, {file: 'test2', ...defaultFileResult}], false)).toEqual('test1\ntest2'); 497 | expect(getDiffFiles([{file: 'test1', ...defaultFileResult}, {file: 'test2 test3', ...defaultFileResult}], false)).toEqual('test1\n\'test2 test3\''); 498 | expect(getDiffFiles([{file: 'test1/test2.txt', ...defaultFileResult}], false)).toEqual('\'test1/test2.txt\''); 499 | }); 500 | 501 | it('get git diff output 3', () => { 502 | delete process.env.INPUT_SEPARATOR; 503 | process.env.INPUT_TEST = ''; 504 | 505 | expect(getDiffFiles([], false)).toEqual(''); 506 | }); 507 | 508 | it('get git diff output 4', () => { 509 | expect(getDiffFiles([], true)).toEqual(''); 510 | expect(getDiffFiles([{file: 'test1', ...defaultFileResult, prefixMatched: false}], true)).toEqual(''); 511 | expect(getDiffFiles([{ 512 | file: 'test1', ...defaultFileResult, 513 | prefixMatched: false, 514 | }, {file: 'test2', ...defaultFileResult}], true)).toEqual('test2'); 515 | expect(getDiffFiles([{ 516 | file: 'test1', ...defaultFileResult, 517 | prefixMatched: false, 518 | }, {file: 'test2 test3', ...defaultFileResult}], true)).toEqual('\'test2 test3\''); 519 | }); 520 | }); 521 | 522 | describe('sumResults', () => { 523 | testEnv(rootDir); 524 | 525 | it('should sum results', () => { 526 | expect(sumResults([], item => item.lines)).toBe(0); 527 | 528 | expect(sumResults(diffs, item => item.insertions)).toBe(3); 529 | expect(sumResults(diffs, item => item.deletions)).toBe(300); 530 | expect(sumResults(diffs, item => item.lines)).toBe(303); 531 | }); 532 | 533 | it('should sum results includes specific files', () => { 534 | process.env.INPUT_SUMMARY_INCLUDE_FILES = 'true'; 535 | expect(sumResults([], item => item.lines)).toBe(0); 536 | 537 | expect(sumResults(diffs, item => item.insertions)).toBe(7); 538 | expect(sumResults(diffs, item => item.deletions)).toBe(700); 539 | expect(sumResults(diffs, item => item.lines)).toBe(707); 540 | }); 541 | }); 542 | -------------------------------------------------------------------------------- /__tests__/fixtures/pulls.list2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347", 4 | "id": 1, 5 | "node_id": "MDExOlB1bGxSZXF1ZXN0MQ==", 6 | "html_url": "https://github.com/octocat/Hello-World/pull/1347", 7 | "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff", 8 | "patch_url": "https://github.com/octocat/Hello-World/pull/1347.patch", 9 | "issue_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347", 10 | "commits_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits", 11 | "review_comments_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments", 12 | "review_comment_url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}", 13 | "comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments", 14 | "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e", 15 | "number": 1347, 16 | "state": "open", 17 | "locked": true, 18 | "title": "Amazing new feature", 19 | "user": { 20 | "login": "octocat", 21 | "id": 1, 22 | "node_id": "MDQ6VXNlcjE=", 23 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 24 | "gravatar_id": "", 25 | "url": "https://api.github.com/users/octocat", 26 | "html_url": "https://github.com/octocat", 27 | "followers_url": "https://api.github.com/users/octocat/followers", 28 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 29 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 30 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 31 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 32 | "organizations_url": "https://api.github.com/users/octocat/orgs", 33 | "repos_url": "https://api.github.com/users/octocat/repos", 34 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 35 | "received_events_url": "https://api.github.com/users/octocat/received_events", 36 | "type": "User", 37 | "site_admin": false 38 | }, 39 | "body": "Please pull these awesome changes in!", 40 | "labels": [ 41 | { 42 | "id": 208045946, 43 | "node_id": "MDU6TGFiZWwyMDgwNDU5NDY=", 44 | "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug", 45 | "name": "bug", 46 | "description": "Something isn't working", 47 | "color": "f29513", 48 | "default": true 49 | } 50 | ], 51 | "milestone": { 52 | "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", 53 | "html_url": "https://github.com/octocat/Hello-World/milestones/v1.0", 54 | "labels_url": "https://api.github.com/repos/octocat/Hello-World/milestones/1/labels", 55 | "id": 1002604, 56 | "node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==", 57 | "number": 1, 58 | "state": "open", 59 | "title": "v1.0", 60 | "description": "Tracking milestone for version 1.0", 61 | "creator": { 62 | "login": "octocat", 63 | "id": 1, 64 | "node_id": "MDQ6VXNlcjE=", 65 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 66 | "gravatar_id": "", 67 | "url": "https://api.github.com/users/octocat", 68 | "html_url": "https://github.com/octocat", 69 | "followers_url": "https://api.github.com/users/octocat/followers", 70 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 71 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 72 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 73 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 74 | "organizations_url": "https://api.github.com/users/octocat/orgs", 75 | "repos_url": "https://api.github.com/users/octocat/repos", 76 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 77 | "received_events_url": "https://api.github.com/users/octocat/received_events", 78 | "type": "User", 79 | "site_admin": false 80 | }, 81 | "open_issues": 4, 82 | "closed_issues": 8, 83 | "created_at": "2011-04-10T20:09:31Z", 84 | "updated_at": "2014-03-03T18:58:10Z", 85 | "closed_at": "2013-02-12T13:22:01Z", 86 | "due_on": "2012-10-09T23:39:01Z" 87 | }, 88 | "active_lock_reason": "too heated", 89 | "created_at": "2011-01-26T19:01:12Z", 90 | "updated_at": "2011-01-26T19:01:12Z", 91 | "closed_at": "2011-01-26T19:01:12Z", 92 | "merged_at": "2011-01-26T19:01:12Z", 93 | "merge_commit_sha": "before-sha", 94 | "assignee": { 95 | "login": "octocat", 96 | "id": 1, 97 | "node_id": "MDQ6VXNlcjE=", 98 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 99 | "gravatar_id": "", 100 | "url": "https://api.github.com/users/octocat", 101 | "html_url": "https://github.com/octocat", 102 | "followers_url": "https://api.github.com/users/octocat/followers", 103 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 104 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 105 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 106 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 107 | "organizations_url": "https://api.github.com/users/octocat/orgs", 108 | "repos_url": "https://api.github.com/users/octocat/repos", 109 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 110 | "received_events_url": "https://api.github.com/users/octocat/received_events", 111 | "type": "User", 112 | "site_admin": false 113 | }, 114 | "assignees": [ 115 | { 116 | "login": "octocat", 117 | "id": 1, 118 | "node_id": "MDQ6VXNlcjE=", 119 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 120 | "gravatar_id": "", 121 | "url": "https://api.github.com/users/octocat", 122 | "html_url": "https://github.com/octocat", 123 | "followers_url": "https://api.github.com/users/octocat/followers", 124 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 125 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 126 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 127 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 128 | "organizations_url": "https://api.github.com/users/octocat/orgs", 129 | "repos_url": "https://api.github.com/users/octocat/repos", 130 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 131 | "received_events_url": "https://api.github.com/users/octocat/received_events", 132 | "type": "User", 133 | "site_admin": false 134 | }, 135 | { 136 | "login": "hubot", 137 | "id": 1, 138 | "node_id": "MDQ6VXNlcjE=", 139 | "avatar_url": "https://github.com/images/error/hubot_happy.gif", 140 | "gravatar_id": "", 141 | "url": "https://api.github.com/users/hubot", 142 | "html_url": "https://github.com/hubot", 143 | "followers_url": "https://api.github.com/users/hubot/followers", 144 | "following_url": "https://api.github.com/users/hubot/following{/other_user}", 145 | "gists_url": "https://api.github.com/users/hubot/gists{/gist_id}", 146 | "starred_url": "https://api.github.com/users/hubot/starred{/owner}{/repo}", 147 | "subscriptions_url": "https://api.github.com/users/hubot/subscriptions", 148 | "organizations_url": "https://api.github.com/users/hubot/orgs", 149 | "repos_url": "https://api.github.com/users/hubot/repos", 150 | "events_url": "https://api.github.com/users/hubot/events{/privacy}", 151 | "received_events_url": "https://api.github.com/users/hubot/received_events", 152 | "type": "User", 153 | "site_admin": true 154 | } 155 | ], 156 | "requested_reviewers": [ 157 | { 158 | "login": "other_user", 159 | "id": 1, 160 | "node_id": "MDQ6VXNlcjE=", 161 | "avatar_url": "https://github.com/images/error/other_user_happy.gif", 162 | "gravatar_id": "", 163 | "url": "https://api.github.com/users/other_user", 164 | "html_url": "https://github.com/other_user", 165 | "followers_url": "https://api.github.com/users/other_user/followers", 166 | "following_url": "https://api.github.com/users/other_user/following{/other_user}", 167 | "gists_url": "https://api.github.com/users/other_user/gists{/gist_id}", 168 | "starred_url": "https://api.github.com/users/other_user/starred{/owner}{/repo}", 169 | "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", 170 | "organizations_url": "https://api.github.com/users/other_user/orgs", 171 | "repos_url": "https://api.github.com/users/other_user/repos", 172 | "events_url": "https://api.github.com/users/other_user/events{/privacy}", 173 | "received_events_url": "https://api.github.com/users/other_user/received_events", 174 | "type": "User", 175 | "site_admin": false 176 | } 177 | ], 178 | "requested_teams": [ 179 | { 180 | "id": 1, 181 | "node_id": "MDQ6VGVhbTE=", 182 | "url": "https://api.github.com/teams/1", 183 | "html_url": "https://api.github.com/teams/justice-league", 184 | "name": "Justice League", 185 | "slug": "justice-league", 186 | "description": "A great team.", 187 | "privacy": "closed", 188 | "permission": "admin", 189 | "members_url": "https://api.github.com/teams/1/members{/member}", 190 | "repositories_url": "https://api.github.com/teams/1/repos", 191 | "parent": null 192 | } 193 | ], 194 | "head": { 195 | "label": "octocat:new-topic", 196 | "ref": "new-topic", 197 | "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", 198 | "user": { 199 | "login": "octocat", 200 | "id": 1, 201 | "node_id": "MDQ6VXNlcjE=", 202 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 203 | "gravatar_id": "", 204 | "url": "https://api.github.com/users/octocat", 205 | "html_url": "https://github.com/octocat", 206 | "followers_url": "https://api.github.com/users/octocat/followers", 207 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 208 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 209 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 210 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 211 | "organizations_url": "https://api.github.com/users/octocat/orgs", 212 | "repos_url": "https://api.github.com/users/octocat/repos", 213 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 214 | "received_events_url": "https://api.github.com/users/octocat/received_events", 215 | "type": "User", 216 | "site_admin": false 217 | }, 218 | "repo": { 219 | "id": 1296269, 220 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 221 | "name": "Hello-World", 222 | "full_name": "octocat/Hello-World", 223 | "owner": { 224 | "login": "octocat", 225 | "id": 1, 226 | "node_id": "MDQ6VXNlcjE=", 227 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 228 | "gravatar_id": "", 229 | "url": "https://api.github.com/users/octocat", 230 | "html_url": "https://github.com/octocat", 231 | "followers_url": "https://api.github.com/users/octocat/followers", 232 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 233 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 234 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 235 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 236 | "organizations_url": "https://api.github.com/users/octocat/orgs", 237 | "repos_url": "https://api.github.com/users/octocat/repos", 238 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 239 | "received_events_url": "https://api.github.com/users/octocat/received_events", 240 | "type": "User", 241 | "site_admin": false 242 | }, 243 | "private": false, 244 | "html_url": "https://github.com/octocat/Hello-World", 245 | "description": "This your first repo!", 246 | "fork": false, 247 | "url": "https://api.github.com/repos/octocat/Hello-World", 248 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 249 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 250 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 251 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 252 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 253 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 254 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 255 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 256 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 257 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 258 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 259 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 260 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 261 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 262 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 263 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 264 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 265 | "git_url": "git:github.com/octocat/Hello-World.git", 266 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 267 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 268 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 269 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 270 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 271 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 272 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 273 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 274 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 275 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 276 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 277 | "ssh_url": "git@github.com:octocat/Hello-World.git", 278 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 279 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 280 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 281 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 282 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 283 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 284 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 285 | "clone_url": "https://github.com/octocat/Hello-World.git", 286 | "mirror_url": "git:git.example.com/octocat/Hello-World", 287 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 288 | "svn_url": "https://svn.github.com/octocat/Hello-World", 289 | "homepage": "https://github.com", 290 | "language": null, 291 | "forks_count": 9, 292 | "stargazers_count": 80, 293 | "watchers_count": 80, 294 | "size": 108, 295 | "default_branch": "master", 296 | "open_issues_count": 0, 297 | "is_template": true, 298 | "topics": [ 299 | "octocat", 300 | "atom", 301 | "electron", 302 | "api" 303 | ], 304 | "has_issues": true, 305 | "has_projects": true, 306 | "has_wiki": true, 307 | "has_pages": false, 308 | "has_downloads": true, 309 | "archived": false, 310 | "disabled": false, 311 | "pushed_at": "2011-01-26T19:06:43Z", 312 | "created_at": "2011-01-26T19:01:12Z", 313 | "updated_at": "2011-01-26T19:14:43Z", 314 | "permissions": { 315 | "admin": false, 316 | "push": false, 317 | "pull": true 318 | }, 319 | "allow_rebase_merge": true, 320 | "template_repository": null, 321 | "allow_squash_merge": true, 322 | "allow_merge_commit": true, 323 | "subscribers_count": 42, 324 | "network_count": 0 325 | } 326 | }, 327 | "base": { 328 | "label": "octocat:master", 329 | "ref": "master", 330 | "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", 331 | "user": { 332 | "login": "octocat", 333 | "id": 1, 334 | "node_id": "MDQ6VXNlcjE=", 335 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 336 | "gravatar_id": "", 337 | "url": "https://api.github.com/users/octocat", 338 | "html_url": "https://github.com/octocat", 339 | "followers_url": "https://api.github.com/users/octocat/followers", 340 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 341 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 342 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 343 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 344 | "organizations_url": "https://api.github.com/users/octocat/orgs", 345 | "repos_url": "https://api.github.com/users/octocat/repos", 346 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 347 | "received_events_url": "https://api.github.com/users/octocat/received_events", 348 | "type": "User", 349 | "site_admin": false 350 | }, 351 | "repo": { 352 | "id": 1296269, 353 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 354 | "name": "Hello-World", 355 | "full_name": "octocat/Hello-World", 356 | "owner": { 357 | "login": "octocat", 358 | "id": 1, 359 | "node_id": "MDQ6VXNlcjE=", 360 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 361 | "gravatar_id": "", 362 | "url": "https://api.github.com/users/octocat", 363 | "html_url": "https://github.com/octocat", 364 | "followers_url": "https://api.github.com/users/octocat/followers", 365 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 366 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 367 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 368 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 369 | "organizations_url": "https://api.github.com/users/octocat/orgs", 370 | "repos_url": "https://api.github.com/users/octocat/repos", 371 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 372 | "received_events_url": "https://api.github.com/users/octocat/received_events", 373 | "type": "User", 374 | "site_admin": false 375 | }, 376 | "private": false, 377 | "html_url": "https://github.com/octocat/Hello-World", 378 | "description": "This your first repo!", 379 | "fork": false, 380 | "url": "https://api.github.com/repos/octocat/Hello-World", 381 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 382 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 383 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 384 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 385 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 386 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 387 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 388 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 389 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 390 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 391 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 392 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 393 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 394 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 395 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 396 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 397 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 398 | "git_url": "git:github.com/octocat/Hello-World.git", 399 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 400 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 401 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 402 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 403 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 404 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 405 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 406 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 407 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 408 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 409 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 410 | "ssh_url": "git@github.com:octocat/Hello-World.git", 411 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 412 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 413 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 414 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 415 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 416 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 417 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 418 | "clone_url": "https://github.com/octocat/Hello-World.git", 419 | "mirror_url": "git:git.example.com/octocat/Hello-World", 420 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 421 | "svn_url": "https://svn.github.com/octocat/Hello-World", 422 | "homepage": "https://github.com", 423 | "language": null, 424 | "forks_count": 9, 425 | "stargazers_count": 80, 426 | "watchers_count": 80, 427 | "size": 108, 428 | "default_branch": "master", 429 | "open_issues_count": 0, 430 | "is_template": true, 431 | "topics": [ 432 | "octocat", 433 | "atom", 434 | "electron", 435 | "api" 436 | ], 437 | "has_issues": true, 438 | "has_projects": true, 439 | "has_wiki": true, 440 | "has_pages": false, 441 | "has_downloads": true, 442 | "archived": false, 443 | "disabled": false, 444 | "pushed_at": "2011-01-26T19:06:43Z", 445 | "created_at": "2011-01-26T19:01:12Z", 446 | "updated_at": "2011-01-26T19:14:43Z", 447 | "permissions": { 448 | "admin": false, 449 | "push": false, 450 | "pull": true 451 | }, 452 | "allow_rebase_merge": true, 453 | "template_repository": null, 454 | "allow_squash_merge": true, 455 | "allow_merge_commit": true, 456 | "subscribers_count": 42, 457 | "network_count": 0 458 | } 459 | }, 460 | "_links": { 461 | "self": { 462 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347" 463 | }, 464 | "html": { 465 | "href": "https://github.com/octocat/Hello-World/pull/1347" 466 | }, 467 | "issue": { 468 | "href": "https://api.github.com/repos/octocat/Hello-World/issues/1347" 469 | }, 470 | "comments": { 471 | "href": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments" 472 | }, 473 | "review_comments": { 474 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments" 475 | }, 476 | "review_comment": { 477 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}" 478 | }, 479 | "commits": { 480 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits" 481 | }, 482 | "statuses": { 483 | "href": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e" 484 | } 485 | }, 486 | "author_association": "OWNER", 487 | "draft": false 488 | } 489 | ] 490 | -------------------------------------------------------------------------------- /__tests__/fixtures/pulls.list1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347", 4 | "id": 1, 5 | "node_id": "MDExOlB1bGxSZXF1ZXN0MQ==", 6 | "html_url": "https://github.com/octocat/Hello-World/pull/1347", 7 | "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff", 8 | "patch_url": "https://github.com/octocat/Hello-World/pull/1347.patch", 9 | "issue_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347", 10 | "commits_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits", 11 | "review_comments_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments", 12 | "review_comment_url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}", 13 | "comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments", 14 | "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e", 15 | "number": 1347, 16 | "state": "open", 17 | "locked": true, 18 | "title": "Amazing new feature", 19 | "user": { 20 | "login": "octocat", 21 | "id": 1, 22 | "node_id": "MDQ6VXNlcjE=", 23 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 24 | "gravatar_id": "", 25 | "url": "https://api.github.com/users/octocat", 26 | "html_url": "https://github.com/octocat", 27 | "followers_url": "https://api.github.com/users/octocat/followers", 28 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 29 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 30 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 31 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 32 | "organizations_url": "https://api.github.com/users/octocat/orgs", 33 | "repos_url": "https://api.github.com/users/octocat/repos", 34 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 35 | "received_events_url": "https://api.github.com/users/octocat/received_events", 36 | "type": "User", 37 | "site_admin": false 38 | }, 39 | "body": "Please pull these awesome changes in!", 40 | "labels": [ 41 | { 42 | "id": 208045946, 43 | "node_id": "MDU6TGFiZWwyMDgwNDU5NDY=", 44 | "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug", 45 | "name": "bug", 46 | "description": "Something isn't working", 47 | "color": "f29513", 48 | "default": true 49 | } 50 | ], 51 | "milestone": { 52 | "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", 53 | "html_url": "https://github.com/octocat/Hello-World/milestones/v1.0", 54 | "labels_url": "https://api.github.com/repos/octocat/Hello-World/milestones/1/labels", 55 | "id": 1002604, 56 | "node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==", 57 | "number": 1, 58 | "state": "open", 59 | "title": "v1.0", 60 | "description": "Tracking milestone for version 1.0", 61 | "creator": { 62 | "login": "octocat", 63 | "id": 1, 64 | "node_id": "MDQ6VXNlcjE=", 65 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 66 | "gravatar_id": "", 67 | "url": "https://api.github.com/users/octocat", 68 | "html_url": "https://github.com/octocat", 69 | "followers_url": "https://api.github.com/users/octocat/followers", 70 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 71 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 72 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 73 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 74 | "organizations_url": "https://api.github.com/users/octocat/orgs", 75 | "repos_url": "https://api.github.com/users/octocat/repos", 76 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 77 | "received_events_url": "https://api.github.com/users/octocat/received_events", 78 | "type": "User", 79 | "site_admin": false 80 | }, 81 | "open_issues": 4, 82 | "closed_issues": 8, 83 | "created_at": "2011-04-10T20:09:31Z", 84 | "updated_at": "2014-03-03T18:58:10Z", 85 | "closed_at": "2013-02-12T13:22:01Z", 86 | "due_on": "2012-10-09T23:39:01Z" 87 | }, 88 | "active_lock_reason": "too heated", 89 | "created_at": "2011-01-26T19:01:12Z", 90 | "updated_at": "2011-01-26T19:01:12Z", 91 | "closed_at": "2011-01-26T19:01:12Z", 92 | "merged_at": "2011-01-26T19:01:12Z", 93 | "merge_commit_sha": "e5bd3914e2e596debea16f433f57875b5b90bcd6", 94 | "assignee": { 95 | "login": "octocat", 96 | "id": 1, 97 | "node_id": "MDQ6VXNlcjE=", 98 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 99 | "gravatar_id": "", 100 | "url": "https://api.github.com/users/octocat", 101 | "html_url": "https://github.com/octocat", 102 | "followers_url": "https://api.github.com/users/octocat/followers", 103 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 104 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 105 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 106 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 107 | "organizations_url": "https://api.github.com/users/octocat/orgs", 108 | "repos_url": "https://api.github.com/users/octocat/repos", 109 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 110 | "received_events_url": "https://api.github.com/users/octocat/received_events", 111 | "type": "User", 112 | "site_admin": false 113 | }, 114 | "assignees": [ 115 | { 116 | "login": "octocat", 117 | "id": 1, 118 | "node_id": "MDQ6VXNlcjE=", 119 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 120 | "gravatar_id": "", 121 | "url": "https://api.github.com/users/octocat", 122 | "html_url": "https://github.com/octocat", 123 | "followers_url": "https://api.github.com/users/octocat/followers", 124 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 125 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 126 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 127 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 128 | "organizations_url": "https://api.github.com/users/octocat/orgs", 129 | "repos_url": "https://api.github.com/users/octocat/repos", 130 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 131 | "received_events_url": "https://api.github.com/users/octocat/received_events", 132 | "type": "User", 133 | "site_admin": false 134 | }, 135 | { 136 | "login": "hubot", 137 | "id": 1, 138 | "node_id": "MDQ6VXNlcjE=", 139 | "avatar_url": "https://github.com/images/error/hubot_happy.gif", 140 | "gravatar_id": "", 141 | "url": "https://api.github.com/users/hubot", 142 | "html_url": "https://github.com/hubot", 143 | "followers_url": "https://api.github.com/users/hubot/followers", 144 | "following_url": "https://api.github.com/users/hubot/following{/other_user}", 145 | "gists_url": "https://api.github.com/users/hubot/gists{/gist_id}", 146 | "starred_url": "https://api.github.com/users/hubot/starred{/owner}{/repo}", 147 | "subscriptions_url": "https://api.github.com/users/hubot/subscriptions", 148 | "organizations_url": "https://api.github.com/users/hubot/orgs", 149 | "repos_url": "https://api.github.com/users/hubot/repos", 150 | "events_url": "https://api.github.com/users/hubot/events{/privacy}", 151 | "received_events_url": "https://api.github.com/users/hubot/received_events", 152 | "type": "User", 153 | "site_admin": true 154 | } 155 | ], 156 | "requested_reviewers": [ 157 | { 158 | "login": "other_user", 159 | "id": 1, 160 | "node_id": "MDQ6VXNlcjE=", 161 | "avatar_url": "https://github.com/images/error/other_user_happy.gif", 162 | "gravatar_id": "", 163 | "url": "https://api.github.com/users/other_user", 164 | "html_url": "https://github.com/other_user", 165 | "followers_url": "https://api.github.com/users/other_user/followers", 166 | "following_url": "https://api.github.com/users/other_user/following{/other_user}", 167 | "gists_url": "https://api.github.com/users/other_user/gists{/gist_id}", 168 | "starred_url": "https://api.github.com/users/other_user/starred{/owner}{/repo}", 169 | "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", 170 | "organizations_url": "https://api.github.com/users/other_user/orgs", 171 | "repos_url": "https://api.github.com/users/other_user/repos", 172 | "events_url": "https://api.github.com/users/other_user/events{/privacy}", 173 | "received_events_url": "https://api.github.com/users/other_user/received_events", 174 | "type": "User", 175 | "site_admin": false 176 | } 177 | ], 178 | "requested_teams": [ 179 | { 180 | "id": 1, 181 | "node_id": "MDQ6VGVhbTE=", 182 | "url": "https://api.github.com/teams/1", 183 | "html_url": "https://api.github.com/teams/justice-league", 184 | "name": "Justice League", 185 | "slug": "justice-league", 186 | "description": "A great team.", 187 | "privacy": "closed", 188 | "permission": "admin", 189 | "members_url": "https://api.github.com/teams/1/members{/member}", 190 | "repositories_url": "https://api.github.com/teams/1/repos", 191 | "parent": null 192 | } 193 | ], 194 | "head": { 195 | "label": "octocat:new-topic", 196 | "ref": "new-topic", 197 | "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", 198 | "user": { 199 | "login": "octocat", 200 | "id": 1, 201 | "node_id": "MDQ6VXNlcjE=", 202 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 203 | "gravatar_id": "", 204 | "url": "https://api.github.com/users/octocat", 205 | "html_url": "https://github.com/octocat", 206 | "followers_url": "https://api.github.com/users/octocat/followers", 207 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 208 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 209 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 210 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 211 | "organizations_url": "https://api.github.com/users/octocat/orgs", 212 | "repos_url": "https://api.github.com/users/octocat/repos", 213 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 214 | "received_events_url": "https://api.github.com/users/octocat/received_events", 215 | "type": "User", 216 | "site_admin": false 217 | }, 218 | "repo": { 219 | "id": 1296269, 220 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 221 | "name": "Hello-World", 222 | "full_name": "octocat/Hello-World", 223 | "owner": { 224 | "login": "octocat", 225 | "id": 1, 226 | "node_id": "MDQ6VXNlcjE=", 227 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 228 | "gravatar_id": "", 229 | "url": "https://api.github.com/users/octocat", 230 | "html_url": "https://github.com/octocat", 231 | "followers_url": "https://api.github.com/users/octocat/followers", 232 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 233 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 234 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 235 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 236 | "organizations_url": "https://api.github.com/users/octocat/orgs", 237 | "repos_url": "https://api.github.com/users/octocat/repos", 238 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 239 | "received_events_url": "https://api.github.com/users/octocat/received_events", 240 | "type": "User", 241 | "site_admin": false 242 | }, 243 | "private": false, 244 | "html_url": "https://github.com/octocat/Hello-World", 245 | "description": "This your first repo!", 246 | "fork": false, 247 | "url": "https://api.github.com/repos/octocat/Hello-World", 248 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 249 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 250 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 251 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 252 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 253 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 254 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 255 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 256 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 257 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 258 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 259 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 260 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 261 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 262 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 263 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 264 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 265 | "git_url": "git:github.com/octocat/Hello-World.git", 266 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 267 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 268 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 269 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 270 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 271 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 272 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 273 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 274 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 275 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 276 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 277 | "ssh_url": "git@github.com:octocat/Hello-World.git", 278 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 279 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 280 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 281 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 282 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 283 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 284 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 285 | "clone_url": "https://github.com/octocat/Hello-World.git", 286 | "mirror_url": "git:git.example.com/octocat/Hello-World", 287 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 288 | "svn_url": "https://svn.github.com/octocat/Hello-World", 289 | "homepage": "https://github.com", 290 | "language": null, 291 | "forks_count": 9, 292 | "stargazers_count": 80, 293 | "watchers_count": 80, 294 | "size": 108, 295 | "default_branch": "master", 296 | "open_issues_count": 0, 297 | "is_template": true, 298 | "topics": [ 299 | "octocat", 300 | "atom", 301 | "electron", 302 | "api" 303 | ], 304 | "has_issues": true, 305 | "has_projects": true, 306 | "has_wiki": true, 307 | "has_pages": false, 308 | "has_downloads": true, 309 | "archived": false, 310 | "disabled": false, 311 | "pushed_at": "2011-01-26T19:06:43Z", 312 | "created_at": "2011-01-26T19:01:12Z", 313 | "updated_at": "2011-01-26T19:14:43Z", 314 | "permissions": { 315 | "admin": false, 316 | "push": false, 317 | "pull": true 318 | }, 319 | "allow_rebase_merge": true, 320 | "template_repository": null, 321 | "allow_squash_merge": true, 322 | "allow_merge_commit": true, 323 | "subscribers_count": 42, 324 | "network_count": 0 325 | } 326 | }, 327 | "base": { 328 | "label": "octocat:master", 329 | "ref": "master", 330 | "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", 331 | "user": { 332 | "login": "octocat", 333 | "id": 1, 334 | "node_id": "MDQ6VXNlcjE=", 335 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 336 | "gravatar_id": "", 337 | "url": "https://api.github.com/users/octocat", 338 | "html_url": "https://github.com/octocat", 339 | "followers_url": "https://api.github.com/users/octocat/followers", 340 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 341 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 342 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 343 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 344 | "organizations_url": "https://api.github.com/users/octocat/orgs", 345 | "repos_url": "https://api.github.com/users/octocat/repos", 346 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 347 | "received_events_url": "https://api.github.com/users/octocat/received_events", 348 | "type": "User", 349 | "site_admin": false 350 | }, 351 | "repo": { 352 | "id": 1296269, 353 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 354 | "name": "Hello-World", 355 | "full_name": "octocat/Hello-World", 356 | "owner": { 357 | "login": "octocat", 358 | "id": 1, 359 | "node_id": "MDQ6VXNlcjE=", 360 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 361 | "gravatar_id": "", 362 | "url": "https://api.github.com/users/octocat", 363 | "html_url": "https://github.com/octocat", 364 | "followers_url": "https://api.github.com/users/octocat/followers", 365 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 366 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 367 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 368 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 369 | "organizations_url": "https://api.github.com/users/octocat/orgs", 370 | "repos_url": "https://api.github.com/users/octocat/repos", 371 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 372 | "received_events_url": "https://api.github.com/users/octocat/received_events", 373 | "type": "User", 374 | "site_admin": false 375 | }, 376 | "private": false, 377 | "html_url": "https://github.com/octocat/Hello-World", 378 | "description": "This your first repo!", 379 | "fork": false, 380 | "url": "https://api.github.com/repos/octocat/Hello-World", 381 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 382 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 383 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 384 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 385 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 386 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 387 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 388 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 389 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 390 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 391 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 392 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 393 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 394 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 395 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 396 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 397 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 398 | "git_url": "git:github.com/octocat/Hello-World.git", 399 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 400 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 401 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 402 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 403 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 404 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 405 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 406 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 407 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 408 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 409 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 410 | "ssh_url": "git@github.com:octocat/Hello-World.git", 411 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 412 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 413 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 414 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 415 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 416 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 417 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 418 | "clone_url": "https://github.com/octocat/Hello-World.git", 419 | "mirror_url": "git:git.example.com/octocat/Hello-World", 420 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 421 | "svn_url": "https://svn.github.com/octocat/Hello-World", 422 | "homepage": "https://github.com", 423 | "language": null, 424 | "forks_count": 9, 425 | "stargazers_count": 80, 426 | "watchers_count": 80, 427 | "size": 108, 428 | "default_branch": "master", 429 | "open_issues_count": 0, 430 | "is_template": true, 431 | "topics": [ 432 | "octocat", 433 | "atom", 434 | "electron", 435 | "api" 436 | ], 437 | "has_issues": true, 438 | "has_projects": true, 439 | "has_wiki": true, 440 | "has_pages": false, 441 | "has_downloads": true, 442 | "archived": false, 443 | "disabled": false, 444 | "pushed_at": "2011-01-26T19:06:43Z", 445 | "created_at": "2011-01-26T19:01:12Z", 446 | "updated_at": "2011-01-26T19:14:43Z", 447 | "permissions": { 448 | "admin": false, 449 | "push": false, 450 | "pull": true 451 | }, 452 | "allow_rebase_merge": true, 453 | "template_repository": null, 454 | "allow_squash_merge": true, 455 | "allow_merge_commit": true, 456 | "subscribers_count": 42, 457 | "network_count": 0 458 | } 459 | }, 460 | "_links": { 461 | "self": { 462 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347" 463 | }, 464 | "html": { 465 | "href": "https://github.com/octocat/Hello-World/pull/1347" 466 | }, 467 | "issue": { 468 | "href": "https://api.github.com/repos/octocat/Hello-World/issues/1347" 469 | }, 470 | "comments": { 471 | "href": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments" 472 | }, 473 | "review_comments": { 474 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments" 475 | }, 476 | "review_comment": { 477 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}" 478 | }, 479 | "commits": { 480 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits" 481 | }, 482 | "statuses": { 483 | "href": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e" 484 | } 485 | }, 486 | "author_association": "OWNER", 487 | "draft": false 488 | }, 489 | { 490 | "url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347", 491 | "id": 1, 492 | "node_id": "MDExOlB1bGxSZXF1ZXN0MQ==", 493 | "html_url": "https://github.com/octocat/Hello-World/pull/1347", 494 | "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff", 495 | "patch_url": "https://github.com/octocat/Hello-World/pull/1347.patch", 496 | "issue_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347", 497 | "commits_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits", 498 | "review_comments_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments", 499 | "review_comment_url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}", 500 | "comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments", 501 | "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e", 502 | "number": 1347, 503 | "state": "open", 504 | "locked": true, 505 | "title": "Amazing new feature", 506 | "user": { 507 | "login": "octocat", 508 | "id": 1, 509 | "node_id": "MDQ6VXNlcjE=", 510 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 511 | "gravatar_id": "", 512 | "url": "https://api.github.com/users/octocat", 513 | "html_url": "https://github.com/octocat", 514 | "followers_url": "https://api.github.com/users/octocat/followers", 515 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 516 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 517 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 518 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 519 | "organizations_url": "https://api.github.com/users/octocat/orgs", 520 | "repos_url": "https://api.github.com/users/octocat/repos", 521 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 522 | "received_events_url": "https://api.github.com/users/octocat/received_events", 523 | "type": "User", 524 | "site_admin": false 525 | }, 526 | "body": "Please pull these awesome changes in!", 527 | "labels": [ 528 | { 529 | "id": 208045946, 530 | "node_id": "MDU6TGFiZWwyMDgwNDU5NDY=", 531 | "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug", 532 | "name": "bug", 533 | "description": "Something isn't working", 534 | "color": "f29513", 535 | "default": true 536 | } 537 | ], 538 | "milestone": { 539 | "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", 540 | "html_url": "https://github.com/octocat/Hello-World/milestones/v1.0", 541 | "labels_url": "https://api.github.com/repos/octocat/Hello-World/milestones/1/labels", 542 | "id": 1002604, 543 | "node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==", 544 | "number": 1, 545 | "state": "open", 546 | "title": "v1.0", 547 | "description": "Tracking milestone for version 1.0", 548 | "creator": { 549 | "login": "octocat", 550 | "id": 1, 551 | "node_id": "MDQ6VXNlcjE=", 552 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 553 | "gravatar_id": "", 554 | "url": "https://api.github.com/users/octocat", 555 | "html_url": "https://github.com/octocat", 556 | "followers_url": "https://api.github.com/users/octocat/followers", 557 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 558 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 559 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 560 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 561 | "organizations_url": "https://api.github.com/users/octocat/orgs", 562 | "repos_url": "https://api.github.com/users/octocat/repos", 563 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 564 | "received_events_url": "https://api.github.com/users/octocat/received_events", 565 | "type": "User", 566 | "site_admin": false 567 | }, 568 | "open_issues": 4, 569 | "closed_issues": 8, 570 | "created_at": "2011-04-10T20:09:31Z", 571 | "updated_at": "2014-03-03T18:58:10Z", 572 | "closed_at": "2013-02-12T13:22:01Z", 573 | "due_on": "2012-10-09T23:39:01Z" 574 | }, 575 | "active_lock_reason": "too heated", 576 | "created_at": "2011-01-26T19:01:12Z", 577 | "updated_at": "2011-01-26T19:01:12Z", 578 | "closed_at": "2011-01-26T19:01:12Z", 579 | "merged_at": "2011-01-26T19:01:12Z", 580 | "merge_commit_sha": "e5bd3914e2e596debea16f433f57875b5b90bcd6", 581 | "assignee": { 582 | "login": "octocat", 583 | "id": 1, 584 | "node_id": "MDQ6VXNlcjE=", 585 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 586 | "gravatar_id": "", 587 | "url": "https://api.github.com/users/octocat", 588 | "html_url": "https://github.com/octocat", 589 | "followers_url": "https://api.github.com/users/octocat/followers", 590 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 591 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 592 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 593 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 594 | "organizations_url": "https://api.github.com/users/octocat/orgs", 595 | "repos_url": "https://api.github.com/users/octocat/repos", 596 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 597 | "received_events_url": "https://api.github.com/users/octocat/received_events", 598 | "type": "User", 599 | "site_admin": false 600 | }, 601 | "assignees": [ 602 | { 603 | "login": "octocat", 604 | "id": 1, 605 | "node_id": "MDQ6VXNlcjE=", 606 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 607 | "gravatar_id": "", 608 | "url": "https://api.github.com/users/octocat", 609 | "html_url": "https://github.com/octocat", 610 | "followers_url": "https://api.github.com/users/octocat/followers", 611 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 612 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 613 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 614 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 615 | "organizations_url": "https://api.github.com/users/octocat/orgs", 616 | "repos_url": "https://api.github.com/users/octocat/repos", 617 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 618 | "received_events_url": "https://api.github.com/users/octocat/received_events", 619 | "type": "User", 620 | "site_admin": false 621 | }, 622 | { 623 | "login": "hubot", 624 | "id": 1, 625 | "node_id": "MDQ6VXNlcjE=", 626 | "avatar_url": "https://github.com/images/error/hubot_happy.gif", 627 | "gravatar_id": "", 628 | "url": "https://api.github.com/users/hubot", 629 | "html_url": "https://github.com/hubot", 630 | "followers_url": "https://api.github.com/users/hubot/followers", 631 | "following_url": "https://api.github.com/users/hubot/following{/other_user}", 632 | "gists_url": "https://api.github.com/users/hubot/gists{/gist_id}", 633 | "starred_url": "https://api.github.com/users/hubot/starred{/owner}{/repo}", 634 | "subscriptions_url": "https://api.github.com/users/hubot/subscriptions", 635 | "organizations_url": "https://api.github.com/users/hubot/orgs", 636 | "repos_url": "https://api.github.com/users/hubot/repos", 637 | "events_url": "https://api.github.com/users/hubot/events{/privacy}", 638 | "received_events_url": "https://api.github.com/users/hubot/received_events", 639 | "type": "User", 640 | "site_admin": true 641 | } 642 | ], 643 | "requested_reviewers": [ 644 | { 645 | "login": "other_user", 646 | "id": 1, 647 | "node_id": "MDQ6VXNlcjE=", 648 | "avatar_url": "https://github.com/images/error/other_user_happy.gif", 649 | "gravatar_id": "", 650 | "url": "https://api.github.com/users/other_user", 651 | "html_url": "https://github.com/other_user", 652 | "followers_url": "https://api.github.com/users/other_user/followers", 653 | "following_url": "https://api.github.com/users/other_user/following{/other_user}", 654 | "gists_url": "https://api.github.com/users/other_user/gists{/gist_id}", 655 | "starred_url": "https://api.github.com/users/other_user/starred{/owner}{/repo}", 656 | "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", 657 | "organizations_url": "https://api.github.com/users/other_user/orgs", 658 | "repos_url": "https://api.github.com/users/other_user/repos", 659 | "events_url": "https://api.github.com/users/other_user/events{/privacy}", 660 | "received_events_url": "https://api.github.com/users/other_user/received_events", 661 | "type": "User", 662 | "site_admin": false 663 | } 664 | ], 665 | "requested_teams": [ 666 | { 667 | "id": 1, 668 | "node_id": "MDQ6VGVhbTE=", 669 | "url": "https://api.github.com/teams/1", 670 | "html_url": "https://api.github.com/teams/justice-league", 671 | "name": "Justice League", 672 | "slug": "justice-league", 673 | "description": "A great team.", 674 | "privacy": "closed", 675 | "permission": "admin", 676 | "members_url": "https://api.github.com/teams/1/members{/member}", 677 | "repositories_url": "https://api.github.com/teams/1/repos", 678 | "parent": null 679 | } 680 | ], 681 | "head": { 682 | "label": "octocat:new-topic", 683 | "ref": "new-topic", 684 | "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", 685 | "user": { 686 | "login": "octocat", 687 | "id": 1, 688 | "node_id": "MDQ6VXNlcjE=", 689 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 690 | "gravatar_id": "", 691 | "url": "https://api.github.com/users/octocat", 692 | "html_url": "https://github.com/octocat", 693 | "followers_url": "https://api.github.com/users/octocat/followers", 694 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 695 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 696 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 697 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 698 | "organizations_url": "https://api.github.com/users/octocat/orgs", 699 | "repos_url": "https://api.github.com/users/octocat/repos", 700 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 701 | "received_events_url": "https://api.github.com/users/octocat/received_events", 702 | "type": "User", 703 | "site_admin": false 704 | }, 705 | "repo": { 706 | "id": 1296269, 707 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 708 | "name": "Hello-World", 709 | "full_name": "octocat/Hello-World", 710 | "owner": { 711 | "login": "octocat", 712 | "id": 1, 713 | "node_id": "MDQ6VXNlcjE=", 714 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 715 | "gravatar_id": "", 716 | "url": "https://api.github.com/users/octocat", 717 | "html_url": "https://github.com/octocat", 718 | "followers_url": "https://api.github.com/users/octocat/followers", 719 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 720 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 721 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 722 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 723 | "organizations_url": "https://api.github.com/users/octocat/orgs", 724 | "repos_url": "https://api.github.com/users/octocat/repos", 725 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 726 | "received_events_url": "https://api.github.com/users/octocat/received_events", 727 | "type": "User", 728 | "site_admin": false 729 | }, 730 | "private": false, 731 | "html_url": "https://github.com/octocat/Hello-World", 732 | "description": "This your first repo!", 733 | "fork": false, 734 | "url": "https://api.github.com/repos/octocat/Hello-World", 735 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 736 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 737 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 738 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 739 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 740 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 741 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 742 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 743 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 744 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 745 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 746 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 747 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 748 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 749 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 750 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 751 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 752 | "git_url": "git:github.com/octocat/Hello-World.git", 753 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 754 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 755 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 756 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 757 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 758 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 759 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 760 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 761 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 762 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 763 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 764 | "ssh_url": "git@github.com:octocat/Hello-World.git", 765 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 766 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 767 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 768 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 769 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 770 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 771 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 772 | "clone_url": "https://github.com/octocat/Hello-World.git", 773 | "mirror_url": "git:git.example.com/octocat/Hello-World", 774 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 775 | "svn_url": "https://svn.github.com/octocat/Hello-World", 776 | "homepage": "https://github.com", 777 | "language": null, 778 | "forks_count": 9, 779 | "stargazers_count": 80, 780 | "watchers_count": 80, 781 | "size": 108, 782 | "default_branch": "master", 783 | "open_issues_count": 0, 784 | "is_template": true, 785 | "topics": [ 786 | "octocat", 787 | "atom", 788 | "electron", 789 | "api" 790 | ], 791 | "has_issues": true, 792 | "has_projects": true, 793 | "has_wiki": true, 794 | "has_pages": false, 795 | "has_downloads": true, 796 | "archived": false, 797 | "disabled": false, 798 | "pushed_at": "2011-01-26T19:06:43Z", 799 | "created_at": "2011-01-26T19:01:12Z", 800 | "updated_at": "2011-01-26T19:14:43Z", 801 | "permissions": { 802 | "admin": false, 803 | "push": false, 804 | "pull": true 805 | }, 806 | "allow_rebase_merge": true, 807 | "template_repository": null, 808 | "allow_squash_merge": true, 809 | "allow_merge_commit": true, 810 | "subscribers_count": 42, 811 | "network_count": 0 812 | } 813 | }, 814 | "base": { 815 | "label": "octocat:master", 816 | "ref": "master", 817 | "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", 818 | "user": { 819 | "login": "octocat", 820 | "id": 1, 821 | "node_id": "MDQ6VXNlcjE=", 822 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 823 | "gravatar_id": "", 824 | "url": "https://api.github.com/users/octocat", 825 | "html_url": "https://github.com/octocat", 826 | "followers_url": "https://api.github.com/users/octocat/followers", 827 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 828 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 829 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 830 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 831 | "organizations_url": "https://api.github.com/users/octocat/orgs", 832 | "repos_url": "https://api.github.com/users/octocat/repos", 833 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 834 | "received_events_url": "https://api.github.com/users/octocat/received_events", 835 | "type": "User", 836 | "site_admin": false 837 | }, 838 | "repo": { 839 | "id": 1296269, 840 | "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", 841 | "name": "Hello-World", 842 | "full_name": "octocat/Hello-World", 843 | "owner": { 844 | "login": "octocat", 845 | "id": 1, 846 | "node_id": "MDQ6VXNlcjE=", 847 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 848 | "gravatar_id": "", 849 | "url": "https://api.github.com/users/octocat", 850 | "html_url": "https://github.com/octocat", 851 | "followers_url": "https://api.github.com/users/octocat/followers", 852 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 853 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 854 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 855 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 856 | "organizations_url": "https://api.github.com/users/octocat/orgs", 857 | "repos_url": "https://api.github.com/users/octocat/repos", 858 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 859 | "received_events_url": "https://api.github.com/users/octocat/received_events", 860 | "type": "User", 861 | "site_admin": false 862 | }, 863 | "private": false, 864 | "html_url": "https://github.com/octocat/Hello-World", 865 | "description": "This your first repo!", 866 | "fork": false, 867 | "url": "https://api.github.com/repos/octocat/Hello-World", 868 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 869 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 870 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 871 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 872 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 873 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 874 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 875 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 876 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 877 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 878 | "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments", 879 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 880 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 881 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 882 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 883 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 884 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 885 | "git_url": "git:github.com/octocat/Hello-World.git", 886 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 887 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 888 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 889 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 890 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 891 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 892 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 893 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 894 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", 895 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 896 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 897 | "ssh_url": "git@github.com:octocat/Hello-World.git", 898 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 899 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 900 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 901 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 902 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 903 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 904 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 905 | "clone_url": "https://github.com/octocat/Hello-World.git", 906 | "mirror_url": "git:git.example.com/octocat/Hello-World", 907 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 908 | "svn_url": "https://svn.github.com/octocat/Hello-World", 909 | "homepage": "https://github.com", 910 | "language": null, 911 | "forks_count": 9, 912 | "stargazers_count": 80, 913 | "watchers_count": 80, 914 | "size": 108, 915 | "default_branch": "master", 916 | "open_issues_count": 0, 917 | "is_template": true, 918 | "topics": [ 919 | "octocat", 920 | "atom", 921 | "electron", 922 | "api" 923 | ], 924 | "has_issues": true, 925 | "has_projects": true, 926 | "has_wiki": true, 927 | "has_pages": false, 928 | "has_downloads": true, 929 | "archived": false, 930 | "disabled": false, 931 | "pushed_at": "2011-01-26T19:06:43Z", 932 | "created_at": "2011-01-26T19:01:12Z", 933 | "updated_at": "2011-01-26T19:14:43Z", 934 | "permissions": { 935 | "admin": false, 936 | "push": false, 937 | "pull": true 938 | }, 939 | "allow_rebase_merge": true, 940 | "template_repository": null, 941 | "allow_squash_merge": true, 942 | "allow_merge_commit": true, 943 | "subscribers_count": 42, 944 | "network_count": 0 945 | } 946 | }, 947 | "_links": { 948 | "self": { 949 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347" 950 | }, 951 | "html": { 952 | "href": "https://github.com/octocat/Hello-World/pull/1347" 953 | }, 954 | "issue": { 955 | "href": "https://api.github.com/repos/octocat/Hello-World/issues/1347" 956 | }, 957 | "comments": { 958 | "href": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments" 959 | }, 960 | "review_comments": { 961 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments" 962 | }, 963 | "review_comment": { 964 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}" 965 | }, 966 | "commits": { 967 | "href": "https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits" 968 | }, 969 | "statuses": { 970 | "href": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e" 971 | } 972 | }, 973 | "author_association": "OWNER", 974 | "draft": false 975 | } 976 | ] --------------------------------------------------------------------------------