├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── action.yml └── entrypoint.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN apk --no-cache add jq bash curl git 4 | 5 | ADD entrypoint.sh /entrypoint.sh 6 | ENTRYPOINT ["/entrypoint.sh"] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 cirrus-actions 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 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub action to automatically rebase PRs 2 | 3 | [![Build Status](https://api.cirrus-ci.com/github/cirrus-actions/rebase.svg)](https://cirrus-ci.com/github/cirrus-actions/rebase) [![](https://images.microbadger.com/badges/version/cirrusactions/rebase.svg)](https://microbadger.com/images/cirrusactions/rebase) [![](https://images.microbadger.com/badges/image/cirrusactions/rebase.svg)](https://microbadger.com/images/cirrusactions/rebase) 4 | 5 | After installation simply comment `/rebase` to trigger the action: 6 | 7 | ![rebase-action](https://user-images.githubusercontent.com/989066/51547853-14a57b00-1e35-11e9-841d-33114f0f0bd5.gif) 8 | 9 | ## Installation 10 | 11 | To configure the action simply add the following lines to your `.github/main.workflow` workflow file: 12 | 13 | ``` 14 | workflow "Automatic Rebase" { 15 | on = "issue_comment" 16 | resolves = "Rebase" 17 | } 18 | 19 | action "Rebase" { 20 | uses = "docker://cirrusactions/rebase:latest" 21 | secrets = ["GITHUB_TOKEN"] 22 | } 23 | ``` 24 | 25 | ## Restricting who can call the action 26 | 27 | It's possible to use `author_association` field of a comment to restrict who can call the action and skip the rebase for others. Simply add the following expression to the `if` statement in your workflow file: `github.event.comment.author_association == 'MEMBER'`. See [documentation](https://developer.github.com/v4/enum/commentauthorassociation/) for a list of all available values of `author_association`. 28 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Automatic Rebase' 2 | description: 'Automatically rebase a PR when a merge commit appears' 3 | author: 'Jordan Harband' 4 | runs: 5 | using: 'docker' 6 | image: 'Dockerfile' 7 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # skip if not a PR 6 | echo "Checking if a PR..." 7 | (jq -r ".pull_request._links.html.href" "$GITHUB_EVENT_PATH") || exit 78 8 | 9 | # jq . $GITHUB_EVENT_PATH 10 | 11 | PR_NUMBER=$(jq -r ".number" "$GITHUB_EVENT_PATH") 12 | REPO_FULLNAME=$(jq -r ".repository.full_name" "$GITHUB_EVENT_PATH") 13 | echo "Collecting information about PR #$PR_NUMBER of $REPO_FULLNAME..." 14 | 15 | if [ -z "$GITHUB_TOKEN" ]; then 16 | echo "Set the GITHUB_TOKEN env variable." 17 | exit 1 18 | fi 19 | 20 | URI=https://api.github.com 21 | API_HEADER="Accept: application/vnd.github.v3+json" 22 | AUTH_HEADER="Authorization: token $GITHUB_TOKEN" 23 | 24 | pr_resp=$(curl -X GET -s -H "${AUTH_HEADER}" -H "${API_HEADER}" \ 25 | "${URI}/repos/$REPO_FULLNAME/pulls/$PR_NUMBER") 26 | 27 | BASE_REPO=$(echo "$pr_resp" | jq -r .base.repo.full_name) 28 | BASE_BRANCH=$(echo "$pr_resp" | jq -r .base.ref) 29 | 30 | # echo "API response: $pr_resp" 31 | 32 | if [ "$(echo "$pr_resp" | jq -r .rebaseable)" != "true" ]; then 33 | >&2 echo 'GitHub doesn‘t think that the PR is rebaseable! it probably has merge conflicts' 34 | exit 1 35 | fi 36 | 37 | if [[ -z "$BASE_BRANCH" ]]; then 38 | >&2 echo "Cannot get base branch information for PR #${PR_NUMBER}!" 39 | exit 1 40 | fi 41 | 42 | HEAD_REPO=$(echo "$pr_resp" | jq -r .head.repo.full_name) 43 | HEAD_BRANCH=$(echo "$pr_resp" | jq -r .head.ref) 44 | REBASEABLE=$(echo "$pr_resp" | jq -r .rebaseable) 45 | CAN_MODIFY=$(echo "$pr_resp" | jq -r .maintainer_can_modify) 46 | IS_FORK=$(echo "$pr_resp" | jq -r .base.repo.fork) 47 | 48 | if [ $REBASEABLE != true ]; then 49 | >&2 echo 'Branch is not rebaseable; has merge conflicts' 50 | exit 1 51 | fi 52 | if [ $CAN_MODIFY != true ] && [ $IS_FORK = true ]; then 53 | >&2 echo 'PR is a fork, and does not allow edits' 54 | exit 1 55 | fi 56 | 57 | echo "Base branch for PR #${PR_NUMBER} is ${BASE_BRANCH}" 58 | 59 | # avoids weird `fatal: unsafe repository ('/github/workspace' is owned by someone else)` error 60 | git config --global --add safe.directory /github/workspace 61 | 62 | git remote add --no-tags pr_source https://x-access-token:$GITHUB_TOKEN@github.com/${HEAD_REPO}.git 63 | 64 | git remote set-url origin https://x-access-token:$GITHUB_TOKEN@github.com/$REPO_FULLNAME.git 65 | git config --global user.email 'action@github.com' 66 | git config --global user.name 'GitHub Action' 67 | 68 | set -o xtrace 69 | 70 | # make sure branches are up-to-date 71 | git fetch origin $BASE_BRANCH 72 | git fetch pr_source $HEAD_BRANCH 73 | 74 | # ensure that if the head branch and base branch have the same name, nothing breaks 75 | git checkout origin/$BASE_BRANCH 76 | git branch -D $HEAD_BRANCH || echo "$HEAD_BRANCH did not exist already" 77 | 78 | # do the rebase 79 | git checkout -b $HEAD_BRANCH pr_source/$HEAD_BRANCH 80 | MERGE_COUNT=$(git log --oneline origin/$BASE_BRANCH..pr_source/$HEAD_BRANCH --merges | wc -l | tr -d '[:space:]') 81 | if [ $? -ne 0 ]; then 82 | >&2 echo 'Unable to count merge commits' 83 | exit 1; 84 | fi 85 | if [ $MERGE_COUNT -eq 0 ]; then 86 | echo 'No merge commits found, yay!' 87 | exit 0 88 | fi 89 | if [ "$REPO_FULLNAME" = 'tc39/ecma262' ]; then 90 | git rebase -f origin/$BASE_BRANCH 91 | else 92 | git rebase origin/$BASE_BRANCH --committer-date-is-author-date 93 | fi 94 | 95 | # push back 96 | git push pr_source HEAD:$HEAD_BRANCH --force 97 | 98 | >&2 echo "${MERGE_COUNT} merge commit(s) found; PR branch rebased." 99 | exit 1 100 | --------------------------------------------------------------------------------