├── .gitignore ├── Dockerfile ├── lib ├── git.bash ├── metadata.bash ├── config.bash └── github.bash ├── docker-compose.yml ├── plugin.yml ├── .buildkite └── pipeline.yml ├── .github └── workflows │ └── tests.yml ├── LICENSE ├── hooks └── command ├── README.md └── tests └── command.bats /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM buildkite/plugin-tester 2 | 3 | RUN apk add --no-cache \ 4 | jq 5 | -------------------------------------------------------------------------------- /lib/git.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | function repo_from_origin() { 6 | local origin 7 | local without_prefix 8 | 9 | origin="$(git remote get-url origin)" 10 | without_prefix="${origin#*:}" 11 | echo "${without_prefix%.git}" 12 | } 13 | -------------------------------------------------------------------------------- /lib/metadata.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | # Write agent metadata for the plugin 6 | function plugin_set_metadata() { 7 | local key="github-pull-request-plugin-$1" 8 | local value="$2" 9 | buildkite-agent meta-data set "$key" "$value" 10 | } 11 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | tests: 4 | build: 5 | context: . 6 | volumes: 7 | - ".:/plugin" 8 | lint: 9 | image: "buildkite/plugin-linter" 10 | command: ["--id", "envato/github-pull-request"] 11 | volumes: 12 | - ".:/plugin:ro" 13 | -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | name: Github Pull Request 2 | description: A Buildkite plugin that opens Github pull requests 3 | author: https://github.com/envato 4 | requirements: 5 | - curl 6 | - jq 7 | configuration: 8 | properties: 9 | base: 10 | type: string 11 | body: 12 | type: string 13 | head: 14 | type: string 15 | labels: 16 | type: [ string, array ] 17 | repo: 18 | type: string 19 | reviewers: 20 | type: [ string, array ] 21 | team-reviewers: 22 | type: [ string, array ] 23 | title: 24 | type: string 25 | required: 26 | - title 27 | 28 | -------------------------------------------------------------------------------- /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | steps: 4 | 5 | - label: ":hammer: Test" 6 | plugins: 7 | - docker-compose#v2.5.1: 8 | run: "tests" 9 | agents: 10 | queue: "${AGENT_QUEUE}" 11 | 12 | - label: ":sparkles: Lint" 13 | plugins: 14 | - plugin-linter#v2.0.0: 15 | id: "envato/github-pull-request" 16 | agents: 17 | queue: "${AGENT_QUEUE}" 18 | 19 | - label: ":shell: Shellcheck" 20 | plugins: 21 | - shellcheck#v1.1.1: 22 | files: 23 | - "hooks/**" 24 | - "lib/**" 25 | agents: 26 | queue: "${AGENT_QUEUE}" 27 | -------------------------------------------------------------------------------- /lib/config.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | function plugin_read_config() { 6 | local var="BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_$1" 7 | local default="${2:-}" 8 | echo "${!var:-$default}" 9 | } 10 | 11 | function plugin_read_list() { 12 | local prefix="BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_$1" 13 | local parameter="${prefix}_0" 14 | 15 | if [[ -n "${!parameter:-}" ]]; then 16 | local i=0 17 | local parameter="${prefix}_${i}" 18 | while [[ -n "${!parameter:-}" ]]; do 19 | echo "${!parameter}" 20 | i=$((i+1)) 21 | parameter="${prefix}_${i}" 22 | done 23 | elif [[ -n "${!prefix:-}" ]]; then 24 | echo "${!prefix}" 25 | fi 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: tests 3 | on: [push] 4 | jobs: 5 | plugin-tests: 6 | name: Tests 7 | runs-on: ubuntu-latest 8 | container: 9 | image: buildkite/plugin-tester:latest 10 | volumes: 11 | - "${{github.workspace}}:/plugin" 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: tests 15 | run: bats tests/ 16 | plugin-lint: 17 | name: Lint 18 | runs-on: ubuntu-latest 19 | container: 20 | image: buildkite/plugin-linter:latest 21 | volumes: 22 | - "${{github.workspace}}:/plugin" 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: lint 26 | run: lint --id envato/github-pull-request 27 | plugin-shellcheck: 28 | name: Shellcheck 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v2 32 | - name: shellcheck 33 | uses: ludeeus/action-shellcheck@1.1.0 34 | with: 35 | check_together: 'yes' 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Envato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /hooks/command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" 6 | 7 | # shellcheck source=lib/config.bash 8 | . "$DIR/../lib/config.bash" 9 | # shellcheck source=lib/git.bash 10 | . "$DIR/../lib/git.bash" 11 | # shellcheck source=lib/github.bash 12 | . "$DIR/../lib/github.bash" 13 | # shellcheck source=lib/metadata.bash 14 | . "$DIR/../lib/metadata.bash" 15 | 16 | echo "+++ :github: Opening Pull Request" 17 | 18 | if [[ -z "${GITHUB_TOKEN:-}" ]]; then 19 | echo 'Error: GITHUB_TOKEN environment variable not set' 20 | echo 'A Github personal access token with repo permissions is needed to open pull requests' 21 | exit 1 22 | fi 23 | 24 | base=$(plugin_read_config BASE master) 25 | body=$(plugin_read_config BODY '') 26 | head=$(plugin_read_config HEAD "${BUILDKITE_BRANCH}") 27 | title=$(plugin_read_config TITLE '') 28 | repo=$(plugin_read_config REPO "$(repo_from_origin)") 29 | 30 | open_pull_request "$title" "$body" "$head" "$base" "$repo" 31 | response=$(cat "tmp/github_api_calls/open_pull_request_response.json") 32 | pr_number=$(echo "$response" | jq '.number') 33 | html_url=$(echo "$response" | jq --raw-output '.html_url') 34 | echo "Github pull request opened: ${html_url}" 35 | plugin_set_metadata number "$pr_number" 36 | 37 | reviewers=$(plugin_read_list REVIEWERS) 38 | team_reviewers=$(plugin_read_list TEAM_REVIEWERS) 39 | if [[ -n "${reviewers:-}" || -n "${team_reviewers:-}" ]]; then 40 | request_reviews "$reviewers" "$team_reviewers" "$pr_number" "$repo" 41 | echo 'Reviews requested' 42 | fi 43 | 44 | labels=$(plugin_read_list LABELS) 45 | if [[ -n "${labels:-}" ]]; then 46 | add_labels "$labels" "$pr_number" "$repo" 47 | echo 'Labels added' 48 | fi 49 | 50 | buildkite-agent annotate ":github: Github pull request [#$pr_number]($html_url) opened." --style info 51 | -------------------------------------------------------------------------------- /lib/github.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | function open_pull_request() { 6 | local title=$1 7 | local body=$2 8 | local head=$3 9 | local base=$4 10 | local repo=$5 11 | local payload 12 | local url 13 | 14 | payload=$(jq -n \ 15 | --compact-output \ 16 | --arg TITLE "${title}" \ 17 | --arg BODY "${body}" \ 18 | --arg HEAD "${head}" \ 19 | --arg BASE "${base}" \ 20 | '{ title: $TITLE, body: $BODY, head: $HEAD, base: $BASE }') 21 | url="$(base_url "${repo}")/pulls" 22 | github_post open_pull_request "${url}" "${payload}" 23 | } 24 | 25 | function request_reviews() { 26 | local reviewers=$1 27 | local team_reviewers=$2 28 | local pr_number=$3 29 | local repo=$4 30 | local payload 31 | local url 32 | 33 | payload=$(jq -n \ 34 | --compact-output \ 35 | --arg REVIEWERS "${reviewers}" \ 36 | --arg TEAM_REVIEWERS "${team_reviewers}" \ 37 | '{ reviewers: $REVIEWERS | split("\n"), team_reviewers: $TEAM_REVIEWERS | split("\n") }') 38 | url="$(base_url "${repo}")/pulls/${pr_number}/requested_reviewers" 39 | github_post request_reviews "${url}" "${payload}" 40 | } 41 | 42 | function add_labels() { 43 | local labels=$1 44 | local pr_number=$2 45 | local repo=$3 46 | local payload 47 | local url 48 | 49 | payload=$(jq -n --compact-output --arg LABELS "${labels}" '$LABELS | split("\n")') 50 | url="$(base_url "${repo}")/issues/${pr_number}/labels" 51 | github_post add_labels "${url}" "${payload}" 52 | } 53 | 54 | function base_url() { 55 | local repo=$1 56 | 57 | echo "https://api.github.com/repos/${repo}" 58 | } 59 | 60 | function github_post() { 61 | local name=$1 62 | local url=$2 63 | local payload=$3 64 | local temp_dir='tmp/github_api_calls' 65 | local request_file="${temp_dir}/${name}_request.json" 66 | local response_file="${temp_dir}/${name}_response.json" 67 | local http_code 68 | 69 | mkdir -p "${temp_dir}" 70 | echo "${payload}" > "${request_file}" 71 | 72 | http_code="$(curl --silent \ 73 | --write-out '%{http_code}'\ 74 | --data "${payload}" \ 75 | --header "Authorization: Bearer ${GITHUB_TOKEN}" \ 76 | --output "${response_file}" \ 77 | --request POST \ 78 | "${url}")" 79 | if [[ ! "${http_code}" =~ ^2[[:digit:]]{2}$ ]]; then 80 | echo "Github responded with ${http_code}:" 81 | cat "${response_file}" 82 | exit 1 83 | fi 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Github Pull Request Buildkite Plugin 2 | 3 | [![tests](https://github.com/envato/github-pull-request-buildkite-plugin/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/envato/github-pull-request-buildkite-plugin/actions/workflows/tests.yml) 4 | 5 | A [Buildkite plugin](https://buildkite.com/docs/agent/v3/plugins) that lets you 6 | open Github pull requests. 7 | 8 | Once opened the number identifying the new Github pull request will be stored 9 | in the build meta-data with key: `github-pull-request-plugin-number`. 10 | 11 | ## Example 12 | 13 | The only required configuration is the pull request title. In this case the 14 | pull request body will be empty, and the new pull request will propose merging 15 | the current branch into `master`. 16 | 17 | ```yml 18 | steps: 19 | - label: ":github: Open Pull Request" 20 | plugins: 21 | - envato/github-pull-request#v0.4.0: 22 | title: "Example pull request title" 23 | ``` 24 | 25 | One can specify the branches to use. Here we open a pull request to merge the 26 | `feature-1` branch into `staging`. 27 | 28 | ```yml 29 | steps: 30 | - label: ":github: Open Pull Request" 31 | plugins: 32 | - envato/github-pull-request#v0.4.0: 33 | title: "Deploy feature-1 to staging" 34 | head: "feature-1" 35 | base: "staging" 36 | ``` 37 | 38 | One can specify a cross account pull request also: 39 | 40 | ```yml 41 | steps: 42 | - label: ":github: Open Pull Request" 43 | plugins: 44 | - envato/github-pull-request#v0.4.0: 45 | title: "Please accept my cool feature" 46 | head: "my-account:my-branch" 47 | repo: "someone-elses-account/project" 48 | ``` 49 | 50 | To request reviews: 51 | 52 | ```yml 53 | steps: 54 | - label: ":github: Open Pull Request" 55 | plugins: 56 | - envato/github-pull-request#v0.4.0: 57 | title: "Example pull request title" 58 | reviewers: 59 | - toolmantim 60 | - keithpitt 61 | team-reviewers: 62 | - a_team 63 | - b_team 64 | ``` 65 | 66 | To add labels: 67 | 68 | ```yml 69 | steps: 70 | - label: ":github: Open Pull Request" 71 | plugins: 72 | - envato/github-pull-request#v0.4.0: 73 | title: "Example pull request title" 74 | labels: 75 | - wip 76 | - security 77 | ``` 78 | 79 | ## Authentication 80 | 81 | This plugin needs to authenticate with Github to open pull requests. To do so 82 | it needs an API token. To provide this please store the token in the 83 | `GITHUB_TOKEN` environment variable. 84 | 85 | While this works, it's not recommended to commit unencrypted private tokens to 86 | SCM. 87 | 88 | ```yml 89 | steps: 90 | - label: ":github: Open Pull Request (not recommended)" 91 | plugins: 92 | - envato/github-pull-request#v0.4.0: 93 | title: "Example pull request title" 94 | env: 95 | - GITHUB_TOKEN= 96 | ``` 97 | 98 | Instead, provide your secrets in via a secure mechanisim. Perhaps using the 99 | [AWS S3 Secrets Buildkite Plugin](https://github.com/buildkite/elastic-ci-stack-s3-secrets-hooks#environment-variables). 100 | 101 | ## Configuration 102 | 103 | ### `title` 104 | 105 | The title of the pull request. 106 | 107 | ### `body` (optional) 108 | 109 | The contents of the pull request. Add some context and description of the 110 | changes being proposed. 111 | 112 | ### `head` (optional) 113 | 114 | The name of the branch where your changes are implemented. For cross-repository 115 | pull requests in the same network, namespace head with a user like this: 116 | `username:branch`. 117 | 118 | Default: `BUILDKITE_BRANCH` (The current branch being built) 119 | 120 | ### `base` (optional) 121 | 122 | The name of the branch you want the changes pulled into. This should be an 123 | existing branch on the repository (see below). 124 | 125 | Default: `master` 126 | 127 | ### `repo` (optional) 128 | 129 | The repository on which the proposed changes will be pulled into. In the form 130 | `owner/project`. 131 | 132 | Default: The repository of the pipeline currently being built. 133 | 134 | ### `reviewers` (optional) 135 | 136 | A list of users who will be requested to review the pull request. The reviewers 137 | must be collaborators on the project. 138 | 139 | ### `team-reviewers` (optional) 140 | 141 | A list of teams who will be requested to review the pull request. The teams 142 | must be collaborators on the project. 143 | 144 | ### `labels` (optional) 145 | 146 | A list of labels that will be added to the pull request. 147 | 148 | ## Development 149 | 150 | To run the tests: 151 | 152 | ```sh 153 | docker-compose run --rm tests 154 | ``` 155 | 156 | To run the [Buildkite Plugin 157 | Linter](https://github.com/buildkite-plugins/buildkite-plugin-linter): 158 | 159 | ```sh 160 | docker-compose run --rm lint 161 | ``` 162 | -------------------------------------------------------------------------------- /tests/command.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load "$BATS_PATH/load.bash" 4 | 5 | # Uncomment the following to get more detail on failures of stubs 6 | # export CURL_STUB_DEBUG=/dev/tty 7 | # export GIT_STUB_DEBUG=/dev/tty 8 | # export BUILDKITE_AGENT_STUB_DEBUG=/dev/tty 9 | 10 | @test 'Opens the Github pull request' { 11 | export BUILDKITE_BRANCH=feature-branch 12 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 13 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 14 | export GITHUB_TOKEN=secret-github-token 15 | 16 | stub curl "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" 17 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 18 | stub buildkite-agent 19 | 20 | run $PWD/hooks/command 21 | 22 | assert_success 23 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 24 | unstub curl 25 | unstub git 26 | } 27 | 28 | @test 'Records the opened Github pull request number in build metadata and adds an annotation' { 29 | export BUILDKITE_BRANCH=feature-branch 30 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 31 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 32 | export GITHUB_TOKEN=secret-github-token 33 | 34 | stub curl "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" 35 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 36 | stub buildkite-agent \ 37 | 'meta-data set github-pull-request-plugin-number 42 : echo metadata set' \ 38 | 'annotate ":github: Github pull request [#42](https://github.com/owner/project/pull/42) opened." --style info : echo annotated' 39 | 40 | run $PWD/hooks/command 41 | 42 | assert_success 43 | unstub buildkite-agent 44 | unstub git 45 | } 46 | 47 | @test 'Opens the Github pull request on specified repository' { 48 | export BUILDKITE_BRANCH=feature-branch 49 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 50 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 51 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_REPO=another-owner/another-project 52 | export GITHUB_TOKEN=secret-github-token 53 | 54 | stub curl "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/another-owner/another-project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/another-owner/another-project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" 55 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 56 | stub buildkite-agent 57 | 58 | run $PWD/hooks/command 59 | 60 | assert_success 61 | assert_output --partial 'Github pull request opened: https://github.com/another-owner/another-project/pull/42' 62 | unstub curl 63 | unstub git 64 | } 65 | 66 | @test 'Opens the Github pull request using specified head and base' { 67 | export BUILDKITE_BRANCH=feature-branch 68 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 69 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 70 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_HEAD=pr-head 71 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BASE=pr-base 72 | export GITHUB_TOKEN=secret-github-token 73 | 74 | stub curl "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"pr-head\",\"base\":\"pr-base\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" 75 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 76 | stub buildkite-agent 77 | 78 | run $PWD/hooks/command 79 | 80 | assert_success 81 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 82 | unstub curl 83 | unstub git 84 | } 85 | 86 | @test 'Opens the Github pull request and requests reviews from one user' { 87 | export BUILDKITE_BRANCH=feature-branch 88 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 89 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 90 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_REVIEWERS=pr-reviewer 91 | export GITHUB_TOKEN=secret-github-token 92 | 93 | stub curl \ 94 | "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" \ 95 | "--silent --write-out '%{http_code}' --data '{\"reviewers\":[\"pr-reviewer\"],\"team_reviewers\":[]}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/request_reviews_response.json --request POST https://api.github.com/repos/owner/project/pulls/42/requested_reviewers : echo '{}' > tmp/github_api_calls/request_reviews_response.json && echo 200" 96 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 97 | stub buildkite-agent 98 | 99 | run $PWD/hooks/command 100 | 101 | assert_success 102 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 103 | assert_output --partial 'Reviews requested' 104 | unstub curl 105 | unstub git 106 | } 107 | 108 | @test 'Opens the Github pull request and requests reviews from two users' { 109 | export BUILDKITE_BRANCH=feature-branch 110 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 111 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 112 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_REVIEWERS_0=pr-reviewer1 113 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_REVIEWERS_1=pr-reviewer2 114 | export GITHUB_TOKEN=secret-github-token 115 | 116 | stub curl \ 117 | "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" \ 118 | "--silent --write-out '%{http_code}' --data '{\"reviewers\":[\"pr-reviewer1\",\"pr-reviewer2\"],\"team_reviewers\":[]}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/request_reviews_response.json --request POST https://api.github.com/repos/owner/project/pulls/42/requested_reviewers : echo '{}' > tmp/github_api_calls/request_reviews_response.json && echo 200" 119 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 120 | stub buildkite-agent 121 | 122 | run $PWD/hooks/command 123 | 124 | assert_success 125 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 126 | assert_output --partial 'Reviews requested' 127 | unstub curl 128 | unstub git 129 | } 130 | 131 | @test 'Opens the Github pull request and requests reviews from one team' { 132 | export BUILDKITE_BRANCH=feature-branch 133 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 134 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 135 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TEAM_REVIEWERS=pr-reviewer-team 136 | export GITHUB_TOKEN=secret-github-token 137 | 138 | stub curl \ 139 | "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" \ 140 | "--silent --write-out '%{http_code}' --data '{\"reviewers\":[],\"team_reviewers\":[\"pr-reviewer-team\"]}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/request_reviews_response.json --request POST https://api.github.com/repos/owner/project/pulls/42/requested_reviewers : echo '{}' > tmp/github_api_calls/request_reviews_response.json && echo 200" 141 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 142 | stub buildkite-agent 143 | 144 | run $PWD/hooks/command 145 | 146 | assert_success 147 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 148 | assert_output --partial 'Reviews requested' 149 | unstub curl 150 | unstub git 151 | } 152 | 153 | @test 'Opens the Github pull request and requests reviews from two teams' { 154 | export BUILDKITE_BRANCH=feature-branch 155 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 156 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 157 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TEAM_REVIEWERS_0=pr-reviewer-team1 158 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TEAM_REVIEWERS_1=pr-reviewer-team2 159 | export GITHUB_TOKEN=secret-github-token 160 | 161 | stub curl \ 162 | "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" \ 163 | "--silent --write-out '%{http_code}' --data '{\"reviewers\":[],\"team_reviewers\":[\"pr-reviewer-team1\",\"pr-reviewer-team2\"]}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/request_reviews_response.json --request POST https://api.github.com/repos/owner/project/pulls/42/requested_reviewers : echo '{}' > tmp/github_api_calls/request_reviews_response.json && echo 200" 164 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 165 | stub buildkite-agent 166 | 167 | run $PWD/hooks/command 168 | 169 | assert_success 170 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 171 | assert_output --partial 'Reviews requested' 172 | unstub curl 173 | unstub git 174 | } 175 | 176 | @test 'Opens the Github pull request and adds a label' { 177 | export BUILDKITE_BRANCH=feature-branch 178 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 179 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 180 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_LABELS=pr-label 181 | export GITHUB_TOKEN=secret-github-token 182 | 183 | stub curl \ 184 | "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" \ 185 | "--silent --write-out '%{http_code}' --data '[\"pr-label\"]' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/add_labels_response.json --request POST https://api.github.com/repos/owner/project/issues/42/labels : echo '{}' > tmp/github_api_calls/add_labels_response.json && echo 200" 186 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 187 | stub buildkite-agent 188 | 189 | run $PWD/hooks/command 190 | 191 | assert_success 192 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 193 | assert_output --partial 'Labels added' 194 | unstub curl 195 | unstub git 196 | } 197 | 198 | @test 'Opens the Github pull request and adds two labels' { 199 | export BUILDKITE_BRANCH=feature-branch 200 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 201 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 202 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_LABELS_0=pr-label1 203 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_LABELS_1=pr-label2 204 | export GITHUB_TOKEN=secret-github-token 205 | 206 | stub curl \ 207 | "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo '{\"number\":42,\"html_url\":\"https://github.com/owner/project/pull/42\"}' > tmp/github_api_calls/open_pull_request_response.json && echo 200" \ 208 | "--silent --write-out '%{http_code}' --data '[\"pr-label1\",\"pr-label2\"]' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/add_labels_response.json --request POST https://api.github.com/repos/owner/project/issues/42/labels : echo '{}' > tmp/github_api_calls/add_labels_response.json && echo 200" 209 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 210 | stub buildkite-agent 211 | 212 | run $PWD/hooks/command 213 | 214 | assert_success 215 | assert_output --partial 'Github pull request opened: https://github.com/owner/project/pull/42' 216 | assert_output --partial 'Labels added' 217 | unstub curl 218 | unstub git 219 | } 220 | 221 | @test 'Errors out if Github return non-200 HTTP status' { 222 | export BUILDKITE_BRANCH=feature-branch 223 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_TITLE=pr-title 224 | export BUILDKITE_PLUGIN_GITHUB_PULL_REQUEST_BODY=pr-body 225 | export GITHUB_TOKEN=secret-github-token 226 | 227 | stub curl "--silent --write-out '%{http_code}' --data '{\"title\":\"pr-title\",\"body\":\"pr-body\",\"head\":\"feature-branch\",\"base\":\"master\"}' --header 'Authorization: Bearer secret-github-token' --output tmp/github_api_calls/open_pull_request_response.json --request POST https://api.github.com/repos/owner/project/pulls : echo 'The error message from Github' > tmp/github_api_calls/open_pull_request_response.json && echo 512" 228 | stub git 'remote get-url origin : echo "git@github.com:owner/project"' 229 | stub buildkite-agent 230 | 231 | run $PWD/hooks/command 232 | 233 | assert_failure 234 | assert_output --partial 'Github responded with 512:' 235 | assert_output --partial 'error message from Github' 236 | unstub curl 237 | unstub git 238 | } 239 | 240 | @test 'Errors out if GITHUB_TOKEN is not provided' { 241 | run $PWD/hooks/command 242 | 243 | assert_failure 244 | assert_output --partial 'Error: GITHUB_TOKEN environment variable not set' 245 | } 246 | --------------------------------------------------------------------------------