Skip to content
This repository was archived by the owner on Aug 2, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
uses: repo-sync/pull-request@v2
with:
destination_branch: "main"
github_token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
```

This will automatically create a pull request from `feature-1` to `main`.
Expand All @@ -57,16 +57,23 @@ jobs:
with:
source_branch: "" # If blank, default: triggered branch
destination_branch: "master" # If blank, default: master
repository: ${{ github.repository }} # repository with owner, can be another repository than currently checked out when using a PAT during checkout that has access to the other repo.
pr_title: "Pulling ${{ github.ref }} into master" # Title of pull request
pr_body: ":crown: *An automated PR*" # Full markdown support, requires pr_title to be set
pr_body: | # Full markdown support, requires pr_title to be set
:crown: *An automated PR*
:arrow_heading_up: Closes: #issueid <!-- your issue -->

**Describe the Change** <!-- A longer description -->
"Put a description here"
pr_template: ".github/PULL_REQUEST_TEMPLATE.md" # Path to pull request template, requires pr_title to be set, excludes pr_body
pr_reviewer: "wei,worker" # Comma-separated list (no spaces)
pr_assignee: "wei,worker" # Comma-separated list (no spaces)
pr_label: "auto-pr" # Comma-separated list (no spaces)
pr_milestone: "Milestone 1" # Milestone name
pr_draft: true # Creates pull request as draft
pr_allow_empty: true # Creates pull request even if there are no changes
github_token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN }}
debug: false # bash set -x verbose debugging output
```

### Outputs
Expand All @@ -89,7 +96,7 @@ jobs:
uses: repo-sync/pull-request@v2
with:
destination_branch: "main"
github_token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: output-url
run: echo ${{steps.open-pr.outputs.pr_url}}
- name: output-number
Expand All @@ -99,6 +106,43 @@ jobs:

```

### Example: Pull-Request on another repo
This example demonstrates how to create a pull-request in another repo. There are a few caveats such as the requirement of checking out the code with a [Github Personal Access Token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). There are some pretty advanced use cases for this such as building an app on every push to develop, updating the docker image tag and repo url in the config repo, and creating a pull-request to the config repo. After the pr in the config repo is merged a deployment is kicked off.
```yaml
on:
push:
branches:
- 'develop'
jobs:
draft-new-pr:
name: "Create PR in my-apps-config-repo"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
token: ${{ secrets.ACTIONS_WORKFLOW_PAT }}
- name: Create Alt Repo Pull-Request
uses: ./.github/actions/actions-create-pull-request
with:
source_branch: 'develop'
destination_branch: 'master'
repository: '${{ github.repository_owner }}/my-apps-config-repo'
pr_title: "Pulling 'release/${{ steps.ver.outputs.version-after }}' into master"
pr_body: |
:crown: *An automated PR*

This PR was created in response to a manual trigger of the release workflow here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}.
..And creates a pr in this other repo here: https://github.com/${{ github.repository_owner }}/my-apps-config-repo.

"Put a description here"
'Quotes are being handled'
pr_label: "some-label,another-label"
pr_allow_empty: false
token: ${{ secrets.ACTIONS_WORKFLOW_PAT }}
debug: false
```

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down
11 changes: 7 additions & 4 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ inputs:
description: Branch name to sync to in this repo, default is master
required: false
default: master
repository:
required: false
description: Repository on which to perform the pull-request. Defaults to current repo. Can different from the current one.
pr_title:
description: Pull request title
required: false
Expand Down Expand Up @@ -39,9 +42,11 @@ inputs:
pr_allow_empty:
description: Create PR even if no changes
required: false
github_token:
description: GitHub token secret
token:
description: GitHub token secret or Personal Access Token
required: true
debug:
description: Bash set -x debugging mode
outputs:
pr_url:
description: 'Pull request URL'
Expand All @@ -52,5 +57,3 @@ outputs:
runs:
using: 'docker'
image: 'Dockerfile'
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
195 changes: 168 additions & 27 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,98 +3,239 @@
set -e
set -o pipefail

if [[ -z "$GITHUB_TOKEN" ]]; then
echo "Set the GITHUB_TOKEN environment variable."
#####################################
# establish a few helper functions
reset_color="\\e[0m"
color_red="\\e[31m"
color_green="\\e[32m"
color_yellow="\\e[33m"
color_blue="\\e[36m"
color_gray="\\e[37m"
function echo_blue { echo -e "${color_blue}$*${reset_color}"; }
function echo_green { echo -e "${color_green}$*${reset_color}"; }
function echo_red { echo -e "${color_red}$*${reset_color}"; }
function echo_yellow { echo -e "${color_yellow}$*${reset_color}"; }
function echo_gray { echo -e "${color_gray}$*${reset_color}"; }
function echo_grey { echo -e "${color_gray}$*${reset_color}"; }
function echo_info { echo -e "${color_blue}info: $*${reset_color}"; }
function echo_error { echo -e "${color_red}error: $*${reset_color}"; }
function echo_warning { echo -e "${color_yellow}✔ $*${reset_color}"; }
function echo_success { echo -e "${color_green}✔ $*${reset_color}"; }
function echo_fail { echo -e "${color_red}✖ $*${reset_color}"; }
function enable_debug {
if [[ "${INPUT_DEBUG}" == "true" ]]; then
echo_info "Enabling debug mode."
set -x
fi
}
function disable_debug {
if [[ "${INPUT_DEBUG}" == "true" ]]; then
set +x
fi
}
# no more helper functions.
###################################


echo "::group::Check inputs"
if [[ -z "$INPUT_TOKEN" ]]; then
echo_yellow "INPUT_TOKEN unsupplied. Defaulting to GITHUB_TOKEN."
INPUT_TOKEN="$GITHUB_TOKEN"
fi

if [[ -z "$INPUT_TOKEN" ]]; then
echo_fail "GITHUB_TOKEN is missing. This is usually provided as an environment variable by Github Actions. If INPUT_TOKEN or GITHUB_TOKEN are unsupplied, unable to continue."
exit 1
fi

if [[ ! -z "$INPUT_TOKEN" ]]; then
echo "::add-mask::$INPUT_TOKEN"
fi

# enable debug after token handling
enable_debug

if [[ -z "$GITHUB_ACTOR" ]]; then
echo_yellow "GITHUB_ACTOR is missing. This is usually provided as an environment variable by Github Actions."
echo_yellow "Setting GITHUB_ACTOR to 'github-actions'"
GITHUB_ACTOR='github-actions'
fi

if [[ -z "$INPUT_REPOSITORY" ]]; then
echo_yellow "INPUT_REPOSITORY is empty to not set. Defaulting INPUT_REPOSITORY to GITHUB_REPOSITORY"
if [[ -z "$GITHUB_REPOSITORY" ]]; then
echo_fail "GITHUB_REPOSITORY is missing. This is usually provided as an environment variable by Github Actions."
echo_fail "Expected Format is generally <github_organization||github_user>/<repo_name>. i.e. aws-official/s3-sync"
exit 1
fi
INPUT_REPOSITORY="$GITHUB_REPOSITORY"
fi

if [[ ! -z "$INPUT_SOURCE_BRANCH" ]]; then
SOURCE_BRANCH="$INPUT_SOURCE_BRANCH"
elif [[ ! -z "$GITHUB_REF" ]]; then
SOURCE_BRANCH=${GITHUB_REF/refs\/heads\//} # Remove branch prefix
else
echo "Set the INPUT_SOURCE_BRANCH environment variable or trigger from a branch."
echo_fail "Set the INPUT_SOURCE_BRANCH environment variable or trigger from a branch."
exit 1
fi

DESTINATION_BRANCH="${INPUT_DESTINATION_BRANCH:-"master"}"
echo "::endgroup::"


echo "::group::Configure git"
# Github actions no longer auto set the username and GITHUB_TOKEN
git remote set-url origin "https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY"
GIT_ORIGIN_URL="$(git remote get-url origin)"
git remote set-url origin "https://$GITHUB_ACTOR:$INPUT_TOKEN@github.com/$INPUT_REPOSITORY"

# Pull all branches references down locally so subsequent commands can see them
git fetch origin '+refs/heads/*:refs/heads/*' --update-head-ok
git fetch origin '+refs/heads/*:refs/heads/*' --update-head-ok --prune
git fetch --prune

# Print out all branches
git --no-pager branch -a -vv
echo "::endgroup::"


echo "::group::Ensure pull-request contains differences"
if [ "$(git rev-parse --revs-only "$SOURCE_BRANCH")" = "$(git rev-parse --revs-only "$DESTINATION_BRANCH")" ]; then
echo "Source and destination branches are the same."
echo_blue "Source and destination branches are the same."
exit 0
fi

# Do not proceed if there are no file differences, this avoids PRs with just a merge commit and no content
LINES_CHANGED=$(git diff --name-only "$DESTINATION_BRANCH" "$SOURCE_BRANCH" -- | wc -l | awk '{print $1}')
if [[ "$LINES_CHANGED" = "0" ]] && [[ ! "$INPUT_PR_ALLOW_EMPTY" == "true" ]]; then
echo "No file changes detected between source and destination branches."
echo_blue "No file changes detected between source and destination branches."
exit 0
fi
echo_yellow "Source and Destination Branches Contain Differences."
echo_yellow "Number of lines changed: $LINES_CHANGED"
echo "::endgroup::"


echo "::group::Establish hub cli parameters"
# Workaround for `hub` auth error https://github.com/github/hub/issues/2149#issuecomment-513214342
export GITHUB_USER="$GITHUB_ACTOR"
# set GITHUB_TOKEN envar so hub cli commands can authenticate.
export GITHUB_TOKEN="$INPUT_TOKEN"

PR_ARG="$INPUT_PR_TITLE"
if [[ ! -z "$PR_ARG" ]]; then
PR_ARG="-m \"$PR_ARG\""
if [[ ! -z "$DESTINATION_BRANCH" ]]; then
FLAGS=(-b "$DESTINATION_BRANCH")
fi

if [[ ! -z "$SOURCE_BRANCH" ]]; then
FLAGS+=(-h "$SOURCE_BRANCH")
fi

FLAGS+=(--no-edit)

if [[ ! -z "$INPUT_PR_TITLE" ]]; then
FLAGS+=(-m "$INPUT_PR_TITLE")
if [[ ! -z "$INPUT_PR_TEMPLATE" ]]; then
sed -i 's/`/\\`/g; s/\$/\\\$/g' "$INPUT_PR_TEMPLATE"
PR_ARG="$PR_ARG -m \"$(echo -e "$(cat "$INPUT_PR_TEMPLATE")")\""
FLAGS+=(-m "$(echo -e "$(cat "$INPUT_PR_TEMPLATE")")")
elif [[ ! -z "$INPUT_PR_BODY" ]]; then
PR_ARG="$PR_ARG -m \"$INPUT_PR_BODY\""
FLAGS+=(-m "$INPUT_PR_BODY")
fi
fi

if [[ ! -z "$INPUT_PR_REVIEWER" ]]; then
PR_ARG="$PR_ARG -r \"$INPUT_PR_REVIEWER\""
FLAGS+=(-r "$INPUT_PR_REVIEWER")
fi

if [[ ! -z "$INPUT_PR_ASSIGNEE" ]]; then
PR_ARG="$PR_ARG -a \"$INPUT_PR_ASSIGNEE\""
FLAGS+=(-a "$INPUT_PR_ASSIGNEE")
fi

if [[ ! -z "$INPUT_PR_LABEL" ]]; then
PR_ARG="$PR_ARG -l \"$INPUT_PR_LABEL\""
FLAGS+=(-l "$INPUT_PR_LABEL")
fi

if [[ ! -z "$INPUT_PR_MILESTONE" ]]; then
PR_ARG="$PR_ARG -M \"$INPUT_PR_MILESTONE\""
FLAGS+=(-M "$INPUT_PR_MILESTONE")
fi

if [[ "$INPUT_PR_DRAFT" == "true" ]]; then
PR_ARG="$PR_ARG -d"
FLAGS+=(-d)
fi

COMMAND="hub pull-request \
-b $DESTINATION_BRANCH \
-h $SOURCE_BRANCH \
--no-edit \
$PR_ARG \
|| true"
echo "${FLAGS[@]}"
echo "::endgroup::"


echo "::group::Create Pull-Request $SOURCE_BRANCH -> $DESTINATION_BRANCH"
RAND_UUID=$(cat /proc/sys/kernel/random/uuid)
COMMAND="hub pull-request "${FLAGS[@]}" 2>\"./create-pull-request.$RAND_UUID.stderr\" || true"
echo "$COMMAND"

PR_URL=$(sh -c "$COMMAND")
if [[ "$?" != "0" ]]; then
exit 1
PR_URL=$( \
hub pull-request "${FLAGS[@]}" \
2>"./create-pull-request.$RAND_UUID.stderr" || true \
)
STD_ERROR="$( cat "./create-pull-request.$RAND_UUID.stderr" || true )"
rm -rf "./create-pull-request.$RAND_UUID.stderr"
echo "::endgroup::"


echo "::group::Revert Git Config Changes"
# set origin back as was previously configured.
git remote set-url origin "$GIT_ORIGIN_URL"
git fetch origin '+refs/heads/*:refs/heads/*' --update-head-ok
git fetch --prune
echo "::endgroup::"


# determine success / failure
# since various things can go wrong such as bad user input or non-existant branches, there is a need to handle outputs to determine if the pr was successfully created or not.
if [[ -z "$PR_URL" ]]; then
if [[ ! -z "$(echo "$STD_ERROR" | grep -oie "A pull request already exists for")" ]]; then
echo_yellow "Pull-Request Already Exists. This is the stderr output:"
echo_yellow "$STD_ERROR"
else
echo_fail "Pull-Request Command Failed. This is the stderr output:"
echo_red "$STD_ERROR"
exit 1
fi
else
echo_success "Pull-Request was successfully created."
echo_success "pr_url: ${PR_URL}"
fi

# attempt to obtain the pull-request details - pr already exists.
if [[ -z "$PR_URL" ]]; then
echo "::group::Retrieving Pull-Request Details"
RAND_UUID=$(cat /proc/sys/kernel/random/uuid)
PR_URL=$( \
hub pr list -h $SOURCE_BRANCH -b $DESTINATION_BRANCH -f %U \
2>"./get-pull-request.$RAND_UUID.stderr" || true \
)
STD_ERROR="$( cat "./get-pull-request.$RAND_UUID.stderr" || true )"
rm -rf "./get-pull-request.$RAND_UUID.stderr"

if [[ -z "$PR_URL" ]]; then
echo_fail "Pull-Request Already Exists, but was unable to retrieve url. This is the stderr output:"
echo_red "$STD_ERROR"
else
echo_success "Pull-Request details successfully obtained."
echo_success "pr_url: ${PR_URL}"
fi
echo "::endgroup::"
fi

echo ${PR_URL}

echo "::group::Set Outputs"
echo "::set-output name=pr_url::${PR_URL}"
echo "::set-output name=pr_number::${PR_URL##*/}"
if [[ "$LINES_CHANGED" = "0" ]]; then
echo "::set-output name=has_changed_files::false"
else
echo "::set-output name=has_changed_files::true"
fi
echo_yellow "Outputs Set."
echo "::endgroup::"


# disable debug mode
disable_debug