├── .dockerignore ├── .npmrc ├── .gitignore ├── index.js ├── scripts └── deploy.sh ├── .env.example ├── sonar-project.properties ├── .travis.yml ├── LICENSE ├── Dockerfile ├── lib └── delete-merged-branch.js ├── test ├── index.test.js ├── lib │ └── delete-merged-branch.test.js └── fixtures │ └── pull-request.closed ├── package.json ├── CODE_OF_CONDUCT.md ├── README.md └── CONTRIBUTING.md /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | 2 | //registry.npmjs.org/:_authToken = ${NPM_TOKEN} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | *.pem 4 | .env 5 | package-lock.json 6 | coverage 7 | .scannerwork -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const deleteMergedBranch = require('./lib/delete-merged-branch') 2 | 3 | module.exports = app => { 4 | app.log('Loaded delete-merged-branch GitHub Application') 5 | app.on(`pull_request.closed`, deleteMergedBranch) 6 | } 7 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | now="npx now --debug --token=$NOW_TOKEN" 3 | 4 | echo "$ now rm --safe --yes delete-merged-branch" 5 | $now rm --safe --yes delete-merged-branch 6 | 7 | echo "$ now --public" 8 | $now --public 9 | 10 | echo "$ now alias" 11 | $now alias 12 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # The ID of your GitHub App 2 | APP_ID= 3 | WEBHOOK_SECRET=development 4 | 5 | # Use `trace` to get verbose logging or `info` to show less 6 | LOG_LEVEL=debug 7 | 8 | # Go to https://smee.io/new set this to the URL that you are redirected to. 9 | WEBHOOK_PROXY_URL= 10 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | # must be unique in a given SonarQube instance 2 | sonar.projectKey=SvanBoxel_delete-merged-branch 3 | # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. 4 | sonar.projectName=Delete Merged Branch 5 | sonar.projectVersion=1.0 6 | 7 | 8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 9 | # This property is optional if sonar.modules is set. 10 | sonar.sources=. 11 | sonar.inclusions=lib/**/*.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - $HOME/.npm 5 | node_js: 6 | - lts/* 7 | 8 | stages: 9 | - test 10 | - name: release 11 | if: branch = master AND type IN (push) 12 | 13 | addons: 14 | sonarcloud: 15 | organization: "svanboxel-github" 16 | 17 | jobs: 18 | include: 19 | - stage: test 20 | script: 21 | - npm run test:ci 22 | - sonar-scanner 23 | - stage: release 24 | script: npm run semantic-release 25 | deploy: 26 | script: scripts/deploy.sh 27 | provider: script 28 | skip_cleanup: true 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2018, Sebass van Boxel 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10.11.0-alpine 2 | 3 | LABEL "com.github.actions.name"="Delete merged branch" 4 | LABEL "com.github.actions.description"="No more manually deleting merged branches, this lovely app does it for you." 5 | LABEL "com.github.actions.icon"="archive" 6 | LABEL "com.github.actions.color"="red" 7 | 8 | LABEL "repository"="https://github.com/SvanBoxel/delete-merged-branch" 9 | LABEL "homepage"="https://github.com/SvanBoxel" 10 | LABEL "maintainer"="svboxel@gmail.com" 11 | 12 | ENV PATH=$PATH:/app/node_modules/.bin 13 | 14 | WORKDIR /app 15 | # These are the only relevant files to the 'yarn install' step. Adding anything 16 | # more will invalidate the docker cache more often than necessary. Over 17 | # multiple docker builds this will improve build time. 18 | COPY package.json yarn.lock /app/ 19 | RUN yarn install --production 20 | COPY . . 21 | 22 | ENTRYPOINT ["probot"] 23 | CMD ["run", "/app/index.js"] 24 | -------------------------------------------------------------------------------- /lib/delete-merged-branch.js: -------------------------------------------------------------------------------- 1 | module.exports = async (context) => { 2 | const config = await context.config('delete-merged-branch-config.yml', { exclude: [] }) 3 | const headRepoId = context.payload.pull_request.head.repo.id 4 | const baseRepoId = context.payload.pull_request.base.repo.id 5 | 6 | const owner = context.payload.repository.owner.login 7 | const repo = context.payload.repository.name 8 | const branchName = context.payload.pull_request.head.ref 9 | const ref = `heads/${branchName}` 10 | 11 | if (headRepoId !== baseRepoId) { 12 | context.log.info(`Closing PR from fork. Keeping ${context.payload.pull_request.head.label}`) 13 | return 14 | } 15 | 16 | if (config.exclude.some((rule) => new RegExp(`^${rule.split('*').join('.*')}$`).test(branchName))) { 17 | context.log.info(`Branch ${branchName} excluded. Keeping ${context.payload.pull_request.head.label}`) 18 | return 19 | } 20 | 21 | if (!context.payload.pull_request.merged) { 22 | context.log.info(`PR was closed but not merged. Keeping ${owner}/${repo}/${ref}`) 23 | return 24 | } 25 | 26 | try { 27 | await context.github.git.deleteRef({ owner, repo, ref }) 28 | context.log.info(`Successfully deleted ${owner}/${repo}/${ref}`) 29 | } catch (e) { 30 | context.log.warn(e, `Failed to delete ${owner}/${repo}/${ref}`) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | const { Probot } = require('probot') 2 | const plugin = require('../index') 3 | const deleteMergedBranch = require('../lib/delete-merged-branch') 4 | const payload = require('./fixtures/pull-request.closed') 5 | 6 | jest.mock('../lib/delete-merged-branch', () => jest.fn()) 7 | 8 | describe('Auto-delete-merged-branch ProBot Application', () => { 9 | let probot 10 | 11 | beforeEach(() => { 12 | probot = new Probot({}) 13 | const app = probot.load(plugin) 14 | app.app = { getSignedJsonWebToken: () => 'test' } 15 | }) 16 | 17 | describe('Delete branch functionality', () => { 18 | describe('It does not receive the `pull_request.closed` event', () => { 19 | beforeEach(async () => { 20 | const name = 'pull_request' 21 | await probot.receive({ 22 | name, 23 | payload: { 24 | ...payload, 25 | action: 'opened', 26 | }, 27 | }) 28 | }) 29 | 30 | it('should NOT call the deleteReference method', () => { 31 | expect(deleteMergedBranch).not.toHaveBeenCalled() 32 | }) 33 | }) 34 | 35 | describe('It receives the `pull_request.closed` event', () => { 36 | beforeEach(async () => { 37 | const name = 'pull_request' 38 | await probot.receive({ name, payload }) 39 | }) 40 | 41 | it('should call the deleteReference method', () => { 42 | expect(deleteMergedBranch).toHaveBeenCalled() 43 | }) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "delete-merged-branch", 3 | "version": "0.0.0-development", 4 | "description": "A GitHub app built that automatically deletes a branch after it's merged. That's it, enjoy!", 5 | "author": "Sebass van Boxel ", 6 | "license": "ISC", 7 | "repository": "https://github.com/SvanBoxel/delete-merged-branch.git", 8 | "homepage": "https://github.com/SvanBoxel/delete-merged-branch", 9 | "bugs": "https://github.com/SvanBoxel/delete-merged-branchissues", 10 | "keywords": [ 11 | "probot", 12 | "github", 13 | "probot-app", 14 | "delete-branch", 15 | "git-tools", 16 | "github-app" 17 | ], 18 | "scripts": { 19 | "now-start": "PRIVATE_KEY=$(echo $PRIVATE_KEY | base64 -d) npm start", 20 | "dev": "nodemon --exec \"npm start\"", 21 | "start": "probot run ./index.js", 22 | "lint": "standard --fix", 23 | "test": "jest && standard", 24 | "test:watch": "jest --watch", 25 | "test:ci": "jest && codecov", 26 | "semantic-release": "semantic-release" 27 | }, 28 | "dependencies": { 29 | "probot": "9.2.15" 30 | }, 31 | "devDependencies": { 32 | "@semantic-release/npm": "5.1.11", 33 | "codecov": "3.4.0", 34 | "jest": "23.4.1", 35 | "nodemon": "1.18.11", 36 | "semantic-release": "15.13.17", 37 | "smee-client": "1.0.3", 38 | "standard": "12.0.0" 39 | }, 40 | "engines": { 41 | "node": ">= 8.3.0" 42 | }, 43 | "jest": { 44 | "coverageDirectory": "./coverage/", 45 | "collectCoverage": true 46 | }, 47 | "release": { 48 | "plugins": [ 49 | "@semantic-release/npm" 50 | ], 51 | "verifyConditions": [ 52 | "@semantic-release/github" 53 | ], 54 | "publish": [ 55 | "@semantic-release/github", 56 | "@semantic-release/npm" 57 | ] 58 | }, 59 | "publishConfig": { 60 | "registry": "https://registry.npmjs.org/", 61 | "tag": "latest" 62 | }, 63 | "now": { 64 | "alias": "delete-merged-branch", 65 | "env": { 66 | "APP_ID": "@app_id", 67 | "NODE_ENV": "production", 68 | "PRIVATE_KEY": "@private_key", 69 | "WEBHOOK_SECRET": "@webhook_secret" 70 | } 71 | }, 72 | "standard": { 73 | "env": [ 74 | "jest" 75 | ] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /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, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | 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 svboxel@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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delete merged branch 2 | [![Build Status](https://travis-ci.com/SvanBoxel/delete-merged-branch.svg?token=BrByTtLgfVKqDJ6GzD2p&branch=master)](https://travis-ci.com/SvanBoxel/delete-merged-branch) 3 | _Want to see more badges? [Click here](#badges)!_ 4 | 5 | 6 | _Want to run this app with [GitHub Actions](https://github.com/features/actions)? [Click here](#running-in-github-actions)_ 7 | 8 | A GitHub app built with [Probot](https://github.com/probot/probot) that automatically deletes a branch after it's merged. That's it, enjoy! 9 | 10 | ## Running it locally 11 | 1. First, follow [these instructions](https://probot.github.io/docs/development/#configure-a-github-app) for making your own GitHub app. 12 | 1. Give your app the following permissions: 13 | - Repository contents: Read & Write. 14 | - Pull requests: Read 15 | 2. And Subscribe to the following events 16 | - Pull Request 17 | 18 | 2. Then, clone the repo: 19 | ```sh 20 | git clone git@github.com:SvanBoxel/delete-merged-branch.git 21 | ``` 22 | 23 | 3. Copy `.env.example` to `.env` and set the right environment variables as [here](https://probot.github.io/docs/configuration/) 24 | 25 | 4. Now, install app dependencies and run it: 26 | 27 | ```sh 28 | # Install dependencies 29 | npm install 30 | 31 | # Run the bot 32 | npm start 33 | ``` 34 | 35 | ## Running with Docker 36 | 37 | 1. Make sure you have docker installed. 38 | 39 | 2. Follow the same steps as running locally to set up the GitHub app and 40 | environment files. 41 | 42 | 3. Build the docker image: 43 | 44 | ```sh 45 | docker build -t delete-merged-branch . 46 | ``` 47 | 48 | 4. Run the docker image: 49 | 50 | ```sh 51 | docker run -i -t --rm \ 52 | -v "$(pwd)/.env:/app/.env" \ 53 | -p 3000:3000 \ 54 | delete-merged-branch 55 | ``` 56 | 57 | Alternate Example: Running test in the docker image 58 | 59 | ```sh 60 | docker run -i -t --rm \ 61 | -v "$(pwd)/.env:/app/.env" \ 62 | -v "$(pwd)/sample-data:/sample-data" \ 63 | delete-merged-branch \ 64 | receive /app/index.js -p /sample-data/event.json 65 | ``` 66 | 67 | ## How it works 68 | This GitHub app listens to the `pull_request.closed` webhook. If a pull request is closed and the connected branch is merged, it will delete the branch. 69 | 70 | ## Configuration 71 | The optional app configuration YAML file should be saved as `.github/delete-merged-branch-config.yml`. At the moment it supports the following options: 72 | 73 | - `exclude` _(array)_ - list of branches that should not be automatically deleted after a merge. Wildcards supported. 74 | 75 | Example `.github/delete-merged-branch-config.yml`: 76 | 77 | ``` 78 | exclude: 79 | - development 80 | - qa 81 | - feature-* 82 | ``` 83 | 84 | ## Release process 85 | CI (Travis) is in charge of releasing new versions of the GitHub Application to [Now](https://zeit.co/now). On every new commit to master we run [semantic-release](https://github.com/semantic-release/semantic-release) to determine whether the major/minor/patch version should be incremented. If so, we update the version running in production. 86 | 87 | ## Running in GitHub actions 88 | This app is compatible with [GitHub Actions](https://github.com/features/actions). You need to create a workflow that is triggered on the `pull_request` event for this. Then, you use this repo for the action. (`SvanBoxel/delete-merged-branch@master`). Don't forget to check the `GITHUB_TOKEN` secret. That's it. 89 | 90 | ![Delete merged branch action](https://user-images.githubusercontent.com/24505883/48064765-14e49180-e1c9-11e8-9fa5-151bf5783b5c.png) 91 | 92 | ## Contributing 93 | 94 | If you have suggestions for how this GitHub app could be improved, or want to report a bug, open an issue! We'd love all and any contributions. 95 | 96 | For more, check out the [Contributing Guide](CONTRIBUTING.md). 97 | 98 | ## License 99 | 100 | [ISC](LICENSE) © 2018 Sebass van Boxel 101 | 102 | ## Badges 103 | [![Build Status](https://travis-ci.com/SvanBoxel/delete-merged-branch.svg?token=BrByTtLgfVKqDJ6GzD2p&branch=master)](https://travis-ci.com/SvanBoxel/delete-merged-branch) 104 | [![codecov](https://codecov.io/gh/SvanBoxel/delete-merged-branch/branch/master/graph/badge.svg)](https://codecov.io/gh/SvanBoxel/delete-merged-branch) 105 | ![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m780713473-6281c6fa7a94950835bfea39.svg) 106 | [![Greenkeeper badge](https://badges.greenkeeper.io/SvanBoxel/delete-merged-branch.svg?token=f5b0c3f23f4ab216a26c3c3559453a514b321c54b14aed881e543a5969eeca62&ts=1531752685299)](https://greenkeeper.io/) 107 | [![Project maintainability](https://sonarcloud.io/api/project_badges/measure?project=SvanBoxel_delete-merged-branch&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=SvanBoxel_delete-merged-branch) 108 | [![npm version](https://badge.fury.io/js/delete-merged-branch.svg)](https://www.npmjs.com/package/delete-merged-branch) 109 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | [fork]: /fork 4 | [pr]: /compare 5 | [style]: https://standardjs.com/ 6 | [code-of-conduct]: CODE_OF_CONDUCT.md 7 | 8 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. 9 | 10 | Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms. 11 | 12 | ## Issues and PRs 13 | 14 | If you have suggestions for how this project could be improved, or want to report a bug, open an issue! We'd love all and any contributions. If you have questions, too, we'd love to hear them. 15 | 16 | We'd also love PRs. If you're thinking of a large PR, we advise opening up an issue first to talk about it, though! Look at the links below if you're not sure how to open a PR. 17 | 18 | ## Submitting a pull request 19 | ### First time contributors 20 | We should encourage first time contributors. A good inspiration on this can be found [here](http://www.firsttimersonly.com/). As pointed out: 21 | 22 | > If you are an OSS project owner, then consider marking a few open issues with the label first-timers-only. The first-timers-only label explicitly announces: 23 | 24 | > "I'm willing to hold your hand so you can make your first PR. This issue is rather a bit easier than normal. And anyone who’s already contributed to open source isn’t allowed to touch this one!" 25 | 26 | By labeling issues with this `first-timers-only` label we help first time contributors step up their game and start contributing. 27 | 28 | ### Pull request step-by-step guide 29 | 30 | Good pull requests - patches, improvements, new features - are a fantastic 31 | help. They should remain focused in scope and avoid containing unrelated 32 | commits. 33 | 34 | **Please ask first** before embarking on any significant pull request (e.g. 35 | implementing features, refactoring code, porting to a different language), 36 | otherwise you risk spending a lot of time working on something that the 37 | project's developers might not want to merge into the project. 38 | 39 | Please adhere to the coding conventions used throughout a project (indentation, 40 | accurate comments, etc.) and any other requirements (such as test coverage). 41 | 42 | Follow this process if you'd like your work considered for inclusion in the 43 | project: 44 | 45 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 46 | and configure the remotes: 47 | 48 | ```bash 49 | # Clone your fork of the repo into the current directory 50 | git clone git@github.com:SvanBoxel/delete-merged-branch.git 51 | # Navigate to the newly cloned directory 52 | cd delete-merged-branch 53 | # Assign the original repo to a remote called "upstream" 54 | git remote add upstream git@github.com:SvanBoxel/delete-merged-branch.git 55 | ``` 56 | 57 | 2. If you cloned a while ago, get the latest changes from upstream: 58 | 59 | ```bash 60 | git checkout 61 | git pull upstream 62 | ``` 63 | 64 | 3. Create a new topic branch (off the master branch) to 65 | contain your feature, change, or fix: 66 | 67 | ```bash 68 | git checkout -b - 69 | ``` 70 | 71 | We recommend having an issue for every improvement you make. Please prepend your branch name with the issue number. eg. issue #381 related to a failing head image: `381-fix-header-image` 72 | 73 | 74 | 4. Commit your changes in logical chunks. Please adhere to these [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y) 75 | or your code is unlikely be merged into the main project. Use Git's 76 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 77 | feature to tidy up your commits before making them public. 78 | 79 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 80 | 81 | ```bash 82 | git pull [--rebase] upstream 83 | ``` 84 | 85 | 6. Push your topic branch up to your fork: 86 | 87 | ```bash 88 | git push origin 89 | ``` 90 | 91 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 92 | with a clear title and description. 93 | 94 | ### Conventions of commit messages 95 | 96 | Adding features on repo 97 | 98 | ```bash 99 | git commit -m "feat: message about this feature" 100 | ``` 101 | 102 | Fixing features on repo 103 | 104 | ```bash 105 | git commit -m "fix: message about this update" 106 | ``` 107 | 108 | Removing features on repo 109 | 110 | ```bash 111 | git commit -m "refactor: message about this" -m "BREAKING CHANGE: message about the breaking change" 112 | ``` 113 | 114 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to license your work under the same license as that used by the project. 115 | 116 | ## Resources 117 | 118 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 119 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) 120 | - [GitHub Help](https://help.github.com) 121 | -------------------------------------------------------------------------------- /test/lib/delete-merged-branch.test.js: -------------------------------------------------------------------------------- 1 | const deleteMergedBranch = require('../../lib/delete-merged-branch') 2 | const payload = require('../fixtures/pull-request.closed') 3 | 4 | describe('deleteMergedBranch function', () => { 5 | let context 6 | let deleteRef 7 | let owner 8 | let ref 9 | let repo 10 | 11 | beforeEach(() => { 12 | deleteRef = jest.fn().mockReturnValue(Promise.resolve()) 13 | context = { 14 | config: jest.fn((_, defaults) => defaults), 15 | log: { 16 | info: jest.fn(), 17 | warn: jest.fn() 18 | }, 19 | event: { 20 | event: 'pull_request.closed' 21 | }, 22 | payload: JSON.parse(JSON.stringify(payload)), // Njeh... 23 | github: { 24 | git: { 25 | deleteRef 26 | } 27 | } 28 | } 29 | owner = payload.repository.owner.login 30 | ref = payload.pull_request.head.ref 31 | repo = payload.repository.name 32 | }) 33 | 34 | describe('branch is merged from fork', () => { 35 | beforeEach(async () => { 36 | context.payload.pull_request.base.repo.id = 200 37 | context.payload.pull_request.head.repo.id = 100 38 | context.payload.pull_request.head.label = 'foo:bar' 39 | await deleteMergedBranch(context) 40 | }) 41 | 42 | it('should log it didn\'t delete the branch', () => { 43 | expect(context.log.info).toBeCalledWith(`Closing PR from fork. Keeping ${context.payload.pull_request.head.label}`) 44 | }) 45 | 46 | it('should NOT call the deleteReference method', () => { 47 | expect(context.github.git.deleteRef).not.toHaveBeenCalled() 48 | }) 49 | }) 50 | 51 | describe('branch is excluded in config', () => { 52 | it('should log it didn\'t delete the branch', async () => { 53 | context.config = jest.fn().mockReturnValue({ exclude: [context.payload.pull_request.head.ref] }) 54 | context.payload.pull_request.head.label = 'foo:bar' 55 | await deleteMergedBranch(context) 56 | expect(context.log.info).toBeCalledWith(`Branch ${context.payload.pull_request.head.ref} excluded. Keeping ${context.payload.pull_request.head.label}`) 57 | }) 58 | 59 | it('should NOT call the deleteReference method', async () => { 60 | context.config = jest.fn().mockReturnValue({ exclude: [context.payload.pull_request.head.ref] }) 61 | context.payload.pull_request.head.label = 'foo:bar' 62 | await deleteMergedBranch(context) 63 | expect(context.github.git.deleteRef).not.toHaveBeenCalled() 64 | }) 65 | 66 | describe('wildcard expression is used', () => { 67 | it('should check for wildcard in end of string', async () => { 68 | const branchWilcard = `${context.payload.pull_request.head.ref.substr(0, 8)}*` 69 | context.config = jest.fn().mockReturnValue({ exclude: ['test', branchWilcard] }) 70 | context.payload.pull_request.head.label = 'bar:foo' 71 | await deleteMergedBranch(context) 72 | expect(context.log.info).toBeCalledWith(`Branch ${context.payload.pull_request.head.ref} excluded. Keeping ${context.payload.pull_request.head.label}`) 73 | }) 74 | 75 | it('should check for wildcard in beginning of string', async () => { 76 | const branchWilcard = `*${context.payload.pull_request.head.ref.substr(1, 20)}` 77 | context.config = jest.fn().mockReturnValue({ exclude: ['test', branchWilcard] }) 78 | context.payload.pull_request.head.label = 'bar:foobar' 79 | await deleteMergedBranch(context) 80 | expect(context.log.info).toBeCalledWith(`Branch ${context.payload.pull_request.head.ref} excluded. Keeping ${context.payload.pull_request.head.label}`) 81 | }) 82 | }) 83 | }) 84 | 85 | describe('branch is merged', async () => { 86 | beforeEach(async () => { 87 | context.payload.pull_request.merged = true 88 | await deleteMergedBranch(context) 89 | }) 90 | 91 | it('should call the deleteReference method', () => { 92 | expect(context.github.git.deleteRef).toHaveBeenCalledWith({ 93 | owner, 94 | ref: `heads/${ref}`, 95 | repo 96 | }) 97 | }) 98 | 99 | it('should log the delete', () => { 100 | expect(context.log.info).toBeCalledWith(`Successfully deleted ${owner}/${repo}/heads/${ref}`) 101 | }) 102 | 103 | describe('deleteReference call fails', () => { 104 | beforeEach(async () => { 105 | context.github.git.deleteRef = jest.fn().mockReturnValue(Promise.reject(new Error())) 106 | await deleteMergedBranch(context) 107 | }) 108 | 109 | it('should log the error', () => { 110 | expect(context.log.warn).toBeCalledWith(expect.any(Error), `Failed to delete ${owner}/${repo}/heads/${ref}`) 111 | }) 112 | }) 113 | }) 114 | 115 | describe('branch is NOT merged', () => { 116 | beforeEach(async () => { 117 | context.payload.pull_request.merged = false 118 | await deleteMergedBranch(context) 119 | }) 120 | 121 | it('should log it didn\'t delete the branch', () => { 122 | expect(context.log.info).toBeCalledWith(`PR was closed but not merged. Keeping ${owner}/${repo}/heads/${ref}`) 123 | }) 124 | 125 | it('should NOT call the deleteReference method', () => { 126 | expect(context.github.git.deleteRef).not.toHaveBeenCalled() 127 | }) 128 | }) 129 | }) 130 | -------------------------------------------------------------------------------- /test/fixtures/pull-request.closed: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "action": "closed", 3 | "number": 7, 4 | "pull_request": { 5 | "url": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/7", 6 | "id": 201586407, 7 | "node_id": "MDExOlB1bGxSZXF1ZXN0MjAxNTg2NDA3", 8 | "html_url": "https://github.com/SvanBoxel/probot-test/pull/7", 9 | "diff_url": "https://github.com/SvanBoxel/probot-test/pull/7.diff", 10 | "patch_url": "https://github.com/SvanBoxel/probot-test/pull/7.patch", 11 | "issue_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/7", 12 | "number": 7, 13 | "state": "closed", 14 | "locked": false, 15 | "title": "Update README.md", 16 | "user": { 17 | "login": "SvanBoxel", 18 | "id": 24505883, 19 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 21 | "gravatar_id": "", 22 | "url": "https://api.github.com/users/SvanBoxel", 23 | "html_url": "https://github.com/SvanBoxel", 24 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 25 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 26 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 27 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 28 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 29 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 30 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 31 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 32 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 33 | "type": "User", 34 | "site_admin": false 35 | }, 36 | "body": "", 37 | "created_at": "2018-07-16T09:48:23Z", 38 | "updated_at": "2018-07-16T09:48:29Z", 39 | "closed_at": "2018-07-16T09:48:29Z", 40 | "merged_at": "2018-07-16T09:48:29Z", 41 | "merge_commit_sha": "0c220266673ca2a921b5c5aef31b66b04706a0fa", 42 | "assignee": null, 43 | "assignees": [ 44 | 45 | ], 46 | "requested_reviewers": [ 47 | 48 | ], 49 | "requested_teams": [ 50 | 51 | ], 52 | "labels": [ 53 | 54 | ], 55 | "milestone": null, 56 | "commits_url": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/7/commits", 57 | "review_comments_url": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/7/comments", 58 | "review_comment_url": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/comments{/number}", 59 | "comments_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/7/comments", 60 | "statuses_url": "https://api.github.com/repos/SvanBoxel/probot-test/statuses/cde3420f207f96f5125776895a45d6fff3316af1", 61 | "head": { 62 | "label": "SvanBoxel:SvanBoxel-patch-5", 63 | "ref": "SvanBoxel-patch-5", 64 | "sha": "cde3420f207f96f5125776895a45d6fff3316af1", 65 | "user": { 66 | "login": "SvanBoxel", 67 | "id": 24505883, 68 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 69 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 70 | "gravatar_id": "", 71 | "url": "https://api.github.com/users/SvanBoxel", 72 | "html_url": "https://github.com/SvanBoxel", 73 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 74 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 75 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 76 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 77 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 78 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 79 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 80 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 81 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 82 | "type": "User", 83 | "site_admin": false 84 | }, 85 | "repo": { 86 | "id": 141112458, 87 | "node_id": "MDEwOlJlcG9zaXRvcnkxNDExMTI0NTg=", 88 | "name": "probot-test", 89 | "full_name": "SvanBoxel/probot-test", 90 | "owner": { 91 | "login": "SvanBoxel", 92 | "id": 24505883, 93 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 94 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 95 | "gravatar_id": "", 96 | "url": "https://api.github.com/users/SvanBoxel", 97 | "html_url": "https://github.com/SvanBoxel", 98 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 99 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 100 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 101 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 102 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 103 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 104 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 105 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 106 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 107 | "type": "User", 108 | "site_admin": false 109 | }, 110 | "private": true, 111 | "html_url": "https://github.com/SvanBoxel/probot-test", 112 | "description": null, 113 | "fork": false, 114 | "url": "https://api.github.com/repos/SvanBoxel/probot-test", 115 | "forks_url": "https://api.github.com/repos/SvanBoxel/probot-test/forks", 116 | "keys_url": "https://api.github.com/repos/SvanBoxel/probot-test/keys{/key_id}", 117 | "collaborators_url": "https://api.github.com/repos/SvanBoxel/probot-test/collaborators{/collaborator}", 118 | "teams_url": "https://api.github.com/repos/SvanBoxel/probot-test/teams", 119 | "hooks_url": "https://api.github.com/repos/SvanBoxel/probot-test/hooks", 120 | "issue_events_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/events{/number}", 121 | "events_url": "https://api.github.com/repos/SvanBoxel/probot-test/events", 122 | "assignees_url": "https://api.github.com/repos/SvanBoxel/probot-test/assignees{/user}", 123 | "branches_url": "https://api.github.com/repos/SvanBoxel/probot-test/branches{/branch}", 124 | "tags_url": "https://api.github.com/repos/SvanBoxel/probot-test/tags", 125 | "blobs_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/blobs{/sha}", 126 | "git_tags_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/tags{/sha}", 127 | "git_refs_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/refs{/sha}", 128 | "trees_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/trees{/sha}", 129 | "statuses_url": "https://api.github.com/repos/SvanBoxel/probot-test/statuses/{sha}", 130 | "languages_url": "https://api.github.com/repos/SvanBoxel/probot-test/languages", 131 | "stargazers_url": "https://api.github.com/repos/SvanBoxel/probot-test/stargazers", 132 | "contributors_url": "https://api.github.com/repos/SvanBoxel/probot-test/contributors", 133 | "subscribers_url": "https://api.github.com/repos/SvanBoxel/probot-test/subscribers", 134 | "subscription_url": "https://api.github.com/repos/SvanBoxel/probot-test/subscription", 135 | "commits_url": "https://api.github.com/repos/SvanBoxel/probot-test/commits{/sha}", 136 | "git_commits_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/commits{/sha}", 137 | "comments_url": "https://api.github.com/repos/SvanBoxel/probot-test/comments{/number}", 138 | "issue_comment_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/comments{/number}", 139 | "contents_url": "https://api.github.com/repos/SvanBoxel/probot-test/contents/{+path}", 140 | "compare_url": "https://api.github.com/repos/SvanBoxel/probot-test/compare/{base}...{head}", 141 | "merges_url": "https://api.github.com/repos/SvanBoxel/probot-test/merges", 142 | "archive_url": "https://api.github.com/repos/SvanBoxel/probot-test/{archive_format}{/ref}", 143 | "downloads_url": "https://api.github.com/repos/SvanBoxel/probot-test/downloads", 144 | "issues_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues{/number}", 145 | "pulls_url": "https://api.github.com/repos/SvanBoxel/probot-test/pulls{/number}", 146 | "milestones_url": "https://api.github.com/repos/SvanBoxel/probot-test/milestones{/number}", 147 | "notifications_url": "https://api.github.com/repos/SvanBoxel/probot-test/notifications{?since,all,participating}", 148 | "labels_url": "https://api.github.com/repos/SvanBoxel/probot-test/labels{/name}", 149 | "releases_url": "https://api.github.com/repos/SvanBoxel/probot-test/releases{/id}", 150 | "deployments_url": "https://api.github.com/repos/SvanBoxel/probot-test/deployments", 151 | "created_at": "2018-07-16T08:51:54Z", 152 | "updated_at": "2018-07-16T09:48:13Z", 153 | "pushed_at": "2018-07-16T09:48:29Z", 154 | "git_url": "git://github.com/SvanBoxel/probot-test.git", 155 | "ssh_url": "git@github.com:SvanBoxel/probot-test.git", 156 | "clone_url": "https://github.com/SvanBoxel/probot-test.git", 157 | "svn_url": "https://github.com/SvanBoxel/probot-test", 158 | "homepage": null, 159 | "size": 0, 160 | "stargazers_count": 0, 161 | "watchers_count": 0, 162 | "language": null, 163 | "has_issues": true, 164 | "has_projects": true, 165 | "has_downloads": true, 166 | "has_wiki": true, 167 | "has_pages": false, 168 | "forks_count": 0, 169 | "mirror_url": null, 170 | "archived": false, 171 | "open_issues_count": 0, 172 | "license": null, 173 | "forks": 0, 174 | "open_issues": 0, 175 | "watchers": 0, 176 | "default_branch": "master" 177 | } 178 | }, 179 | "base": { 180 | "label": "SvanBoxel:master", 181 | "ref": "master", 182 | "sha": "46834b97485795c97a2f076f19c2eb9730f64e27", 183 | "user": { 184 | "login": "SvanBoxel", 185 | "id": 24505883, 186 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 187 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 188 | "gravatar_id": "", 189 | "url": "https://api.github.com/users/SvanBoxel", 190 | "html_url": "https://github.com/SvanBoxel", 191 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 192 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 193 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 194 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 195 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 196 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 197 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 198 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 199 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 200 | "type": "User", 201 | "site_admin": false 202 | }, 203 | "repo": { 204 | "id": 141112458, 205 | "node_id": "MDEwOlJlcG9zaXRvcnkxNDExMTI0NTg=", 206 | "name": "probot-test", 207 | "full_name": "SvanBoxel/probot-test", 208 | "owner": { 209 | "login": "SvanBoxel", 210 | "id": 24505883, 211 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 212 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 213 | "gravatar_id": "", 214 | "url": "https://api.github.com/users/SvanBoxel", 215 | "html_url": "https://github.com/SvanBoxel", 216 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 217 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 218 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 219 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 220 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 221 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 222 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 223 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 224 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 225 | "type": "User", 226 | "site_admin": false 227 | }, 228 | "private": true, 229 | "html_url": "https://github.com/SvanBoxel/probot-test", 230 | "description": null, 231 | "fork": false, 232 | "url": "https://api.github.com/repos/SvanBoxel/probot-test", 233 | "forks_url": "https://api.github.com/repos/SvanBoxel/probot-test/forks", 234 | "keys_url": "https://api.github.com/repos/SvanBoxel/probot-test/keys{/key_id}", 235 | "collaborators_url": "https://api.github.com/repos/SvanBoxel/probot-test/collaborators{/collaborator}", 236 | "teams_url": "https://api.github.com/repos/SvanBoxel/probot-test/teams", 237 | "hooks_url": "https://api.github.com/repos/SvanBoxel/probot-test/hooks", 238 | "issue_events_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/events{/number}", 239 | "events_url": "https://api.github.com/repos/SvanBoxel/probot-test/events", 240 | "assignees_url": "https://api.github.com/repos/SvanBoxel/probot-test/assignees{/user}", 241 | "branches_url": "https://api.github.com/repos/SvanBoxel/probot-test/branches{/branch}", 242 | "tags_url": "https://api.github.com/repos/SvanBoxel/probot-test/tags", 243 | "blobs_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/blobs{/sha}", 244 | "git_tags_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/tags{/sha}", 245 | "git_refs_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/refs{/sha}", 246 | "trees_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/trees{/sha}", 247 | "statuses_url": "https://api.github.com/repos/SvanBoxel/probot-test/statuses/{sha}", 248 | "languages_url": "https://api.github.com/repos/SvanBoxel/probot-test/languages", 249 | "stargazers_url": "https://api.github.com/repos/SvanBoxel/probot-test/stargazers", 250 | "contributors_url": "https://api.github.com/repos/SvanBoxel/probot-test/contributors", 251 | "subscribers_url": "https://api.github.com/repos/SvanBoxel/probot-test/subscribers", 252 | "subscription_url": "https://api.github.com/repos/SvanBoxel/probot-test/subscription", 253 | "commits_url": "https://api.github.com/repos/SvanBoxel/probot-test/commits{/sha}", 254 | "git_commits_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/commits{/sha}", 255 | "comments_url": "https://api.github.com/repos/SvanBoxel/probot-test/comments{/number}", 256 | "issue_comment_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/comments{/number}", 257 | "contents_url": "https://api.github.com/repos/SvanBoxel/probot-test/contents/{+path}", 258 | "compare_url": "https://api.github.com/repos/SvanBoxel/probot-test/compare/{base}...{head}", 259 | "merges_url": "https://api.github.com/repos/SvanBoxel/probot-test/merges", 260 | "archive_url": "https://api.github.com/repos/SvanBoxel/probot-test/{archive_format}{/ref}", 261 | "downloads_url": "https://api.github.com/repos/SvanBoxel/probot-test/downloads", 262 | "issues_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues{/number}", 263 | "pulls_url": "https://api.github.com/repos/SvanBoxel/probot-test/pulls{/number}", 264 | "milestones_url": "https://api.github.com/repos/SvanBoxel/probot-test/milestones{/number}", 265 | "notifications_url": "https://api.github.com/repos/SvanBoxel/probot-test/notifications{?since,all,participating}", 266 | "labels_url": "https://api.github.com/repos/SvanBoxel/probot-test/labels{/name}", 267 | "releases_url": "https://api.github.com/repos/SvanBoxel/probot-test/releases{/id}", 268 | "deployments_url": "https://api.github.com/repos/SvanBoxel/probot-test/deployments", 269 | "created_at": "2018-07-16T08:51:54Z", 270 | "updated_at": "2018-07-16T09:48:13Z", 271 | "pushed_at": "2018-07-16T09:48:29Z", 272 | "git_url": "git://github.com/SvanBoxel/probot-test.git", 273 | "ssh_url": "git@github.com:SvanBoxel/probot-test.git", 274 | "clone_url": "https://github.com/SvanBoxel/probot-test.git", 275 | "svn_url": "https://github.com/SvanBoxel/probot-test", 276 | "homepage": null, 277 | "size": 0, 278 | "stargazers_count": 0, 279 | "watchers_count": 0, 280 | "language": null, 281 | "has_issues": true, 282 | "has_projects": true, 283 | "has_downloads": true, 284 | "has_wiki": true, 285 | "has_pages": false, 286 | "forks_count": 0, 287 | "mirror_url": null, 288 | "archived": false, 289 | "open_issues_count": 0, 290 | "license": null, 291 | "forks": 0, 292 | "open_issues": 0, 293 | "watchers": 0, 294 | "default_branch": "master" 295 | } 296 | }, 297 | "_links": { 298 | "self": { 299 | "href": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/7" 300 | }, 301 | "html": { 302 | "href": "https://github.com/SvanBoxel/probot-test/pull/7" 303 | }, 304 | "issue": { 305 | "href": "https://api.github.com/repos/SvanBoxel/probot-test/issues/7" 306 | }, 307 | "comments": { 308 | "href": "https://api.github.com/repos/SvanBoxel/probot-test/issues/7/comments" 309 | }, 310 | "review_comments": { 311 | "href": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/7/comments" 312 | }, 313 | "review_comment": { 314 | "href": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/comments{/number}" 315 | }, 316 | "commits": { 317 | "href": "https://api.github.com/repos/SvanBoxel/probot-test/pulls/7/commits" 318 | }, 319 | "statuses": { 320 | "href": "https://api.github.com/repos/SvanBoxel/probot-test/statuses/cde3420f207f96f5125776895a45d6fff3316af1" 321 | } 322 | }, 323 | "author_association": "OWNER", 324 | "merged": true, 325 | "mergeable": null, 326 | "rebaseable": null, 327 | "mergeable_state": "unknown", 328 | "merged_by": { 329 | "login": "SvanBoxel", 330 | "id": 24505883, 331 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 332 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 333 | "gravatar_id": "", 334 | "url": "https://api.github.com/users/SvanBoxel", 335 | "html_url": "https://github.com/SvanBoxel", 336 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 337 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 338 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 339 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 340 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 341 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 342 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 343 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 344 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 345 | "type": "User", 346 | "site_admin": false 347 | }, 348 | "comments": 0, 349 | "review_comments": 0, 350 | "maintainer_can_modify": false, 351 | "commits": 1, 352 | "additions": 1, 353 | "deletions": 0, 354 | "changed_files": 1 355 | }, 356 | "repository": { 357 | "id": 141112458, 358 | "node_id": "MDEwOlJlcG9zaXRvcnkxNDExMTI0NTg=", 359 | "name": "probot-test", 360 | "full_name": "SvanBoxel/probot-test", 361 | "owner": { 362 | "login": "SvanBoxel", 363 | "id": 24505883, 364 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 365 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 366 | "gravatar_id": "", 367 | "url": "https://api.github.com/users/SvanBoxel", 368 | "html_url": "https://github.com/SvanBoxel", 369 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 370 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 371 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 372 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 373 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 374 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 375 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 376 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 377 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 378 | "type": "User", 379 | "site_admin": false 380 | }, 381 | "private": true, 382 | "html_url": "https://github.com/SvanBoxel/probot-test", 383 | "description": null, 384 | "fork": false, 385 | "url": "https://api.github.com/repos/SvanBoxel/probot-test", 386 | "forks_url": "https://api.github.com/repos/SvanBoxel/probot-test/forks", 387 | "keys_url": "https://api.github.com/repos/SvanBoxel/probot-test/keys{/key_id}", 388 | "collaborators_url": "https://api.github.com/repos/SvanBoxel/probot-test/collaborators{/collaborator}", 389 | "teams_url": "https://api.github.com/repos/SvanBoxel/probot-test/teams", 390 | "hooks_url": "https://api.github.com/repos/SvanBoxel/probot-test/hooks", 391 | "issue_events_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/events{/number}", 392 | "events_url": "https://api.github.com/repos/SvanBoxel/probot-test/events", 393 | "assignees_url": "https://api.github.com/repos/SvanBoxel/probot-test/assignees{/user}", 394 | "branches_url": "https://api.github.com/repos/SvanBoxel/probot-test/branches{/branch}", 395 | "tags_url": "https://api.github.com/repos/SvanBoxel/probot-test/tags", 396 | "blobs_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/blobs{/sha}", 397 | "git_tags_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/tags{/sha}", 398 | "git_refs_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/refs{/sha}", 399 | "trees_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/trees{/sha}", 400 | "statuses_url": "https://api.github.com/repos/SvanBoxel/probot-test/statuses/{sha}", 401 | "languages_url": "https://api.github.com/repos/SvanBoxel/probot-test/languages", 402 | "stargazers_url": "https://api.github.com/repos/SvanBoxel/probot-test/stargazers", 403 | "contributors_url": "https://api.github.com/repos/SvanBoxel/probot-test/contributors", 404 | "subscribers_url": "https://api.github.com/repos/SvanBoxel/probot-test/subscribers", 405 | "subscription_url": "https://api.github.com/repos/SvanBoxel/probot-test/subscription", 406 | "commits_url": "https://api.github.com/repos/SvanBoxel/probot-test/commits{/sha}", 407 | "git_commits_url": "https://api.github.com/repos/SvanBoxel/probot-test/git/commits{/sha}", 408 | "comments_url": "https://api.github.com/repos/SvanBoxel/probot-test/comments{/number}", 409 | "issue_comment_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues/comments{/number}", 410 | "contents_url": "https://api.github.com/repos/SvanBoxel/probot-test/contents/{+path}", 411 | "compare_url": "https://api.github.com/repos/SvanBoxel/probot-test/compare/{base}...{head}", 412 | "merges_url": "https://api.github.com/repos/SvanBoxel/probot-test/merges", 413 | "archive_url": "https://api.github.com/repos/SvanBoxel/probot-test/{archive_format}{/ref}", 414 | "downloads_url": "https://api.github.com/repos/SvanBoxel/probot-test/downloads", 415 | "issues_url": "https://api.github.com/repos/SvanBoxel/probot-test/issues{/number}", 416 | "pulls_url": "https://api.github.com/repos/SvanBoxel/probot-test/pulls{/number}", 417 | "milestones_url": "https://api.github.com/repos/SvanBoxel/probot-test/milestones{/number}", 418 | "notifications_url": "https://api.github.com/repos/SvanBoxel/probot-test/notifications{?since,all,participating}", 419 | "labels_url": "https://api.github.com/repos/SvanBoxel/probot-test/labels{/name}", 420 | "releases_url": "https://api.github.com/repos/SvanBoxel/probot-test/releases{/id}", 421 | "deployments_url": "https://api.github.com/repos/SvanBoxel/probot-test/deployments", 422 | "created_at": "2018-07-16T08:51:54Z", 423 | "updated_at": "2018-07-16T09:48:13Z", 424 | "pushed_at": "2018-07-16T09:48:29Z", 425 | "git_url": "git://github.com/SvanBoxel/probot-test.git", 426 | "ssh_url": "git@github.com:SvanBoxel/probot-test.git", 427 | "clone_url": "https://github.com/SvanBoxel/probot-test.git", 428 | "svn_url": "https://github.com/SvanBoxel/probot-test", 429 | "homepage": null, 430 | "size": 0, 431 | "stargazers_count": 0, 432 | "watchers_count": 0, 433 | "language": null, 434 | "has_issues": true, 435 | "has_projects": true, 436 | "has_downloads": true, 437 | "has_wiki": true, 438 | "has_pages": false, 439 | "forks_count": 0, 440 | "mirror_url": null, 441 | "archived": false, 442 | "open_issues_count": 0, 443 | "license": null, 444 | "forks": 0, 445 | "open_issues": 0, 446 | "watchers": 0, 447 | "default_branch": "master" 448 | }, 449 | "sender": { 450 | "login": "SvanBoxel", 451 | "id": 24505883, 452 | "node_id": "MDQ6VXNlcjI0NTA1ODgz", 453 | "avatar_url": "https://avatars3.githubusercontent.com/u/24505883?v=4", 454 | "gravatar_id": "", 455 | "url": "https://api.github.com/users/SvanBoxel", 456 | "html_url": "https://github.com/SvanBoxel", 457 | "followers_url": "https://api.github.com/users/SvanBoxel/followers", 458 | "following_url": "https://api.github.com/users/SvanBoxel/following{/other_user}", 459 | "gists_url": "https://api.github.com/users/SvanBoxel/gists{/gist_id}", 460 | "starred_url": "https://api.github.com/users/SvanBoxel/starred{/owner}{/repo}", 461 | "subscriptions_url": "https://api.github.com/users/SvanBoxel/subscriptions", 462 | "organizations_url": "https://api.github.com/users/SvanBoxel/orgs", 463 | "repos_url": "https://api.github.com/users/SvanBoxel/repos", 464 | "events_url": "https://api.github.com/users/SvanBoxel/events{/privacy}", 465 | "received_events_url": "https://api.github.com/users/SvanBoxel/received_events", 466 | "type": "User", 467 | "site_admin": false 468 | }, 469 | "installation": { 470 | "id": 244386 471 | } 472 | } 473 | --------------------------------------------------------------------------------