From 06a212202ff54fab89c7fdc1c291f9ee6933063c Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Tue, 16 Jul 2019 19:58:27 +0900 Subject: [PATCH] Add action --- Dockerfile | 18 ++++++ README.md | 68 +++++++++++++++++++++- create-pull-request.py | 126 +++++++++++++++++++++++++++++++++++++++++ requirements.txt | 2 + 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 Dockerfile create mode 100644 create-pull-request.py create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..698e424 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.7.3 + +LABEL maintainer="Peter Evans " +LABEL repository="https://github.com/peter-evans/create-pull-request" +LABEL homepage="https://github.com/peter-evans/create-pull-request" + +LABEL com.github.actions.name="Create Pull Request" +LABEL com.github.actions.description="Creates a pull request for changes to your repository in the actions workspace" +LABEL com.github.actions.icon="git-pull-request" +LABEL com.github.actions.color="gray-dark" + +COPY LICENSE README.md / + +COPY requirements.txt /tmp/ +RUN pip install --requirement /tmp/requirements.txt + +COPY create-pull-request.py /create-pull-request.py +ENTRYPOINT [ "python", "/create-pull-request.py" ] \ No newline at end of file diff --git a/README.md b/README.md index 4da0715..512ae52 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,68 @@ -# create-pull-request +# Create Pull Request +[![GitHub Marketplace](https://img.shields.io/badge/Marketplace-Create%20Pull%20Request-blue.svg?colorA=24292e&colorB=0366d6&style=flat&longCache=true&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6wAADOsB5dZE0gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAERSURBVCiRhZG/SsMxFEZPfsVJ61jbxaF0cRQRcRJ9hlYn30IHN/+9iquDCOIsblIrOjqKgy5aKoJQj4O3EEtbPwhJbr6Te28CmdSKeqzeqr0YbfVIrTBKakvtOl5dtTkK+v4HfA9PEyBFCY9AGVgCBLaBp1jPAyfAJ/AAdIEG0dNAiyP7+K1qIfMdonZic6+WJoBJvQlvuwDqcXadUuqPA1NKAlexbRTAIMvMOCjTbMwl1LtI/6KWJ5Q6rT6Ht1MA58AX8Apcqqt5r2qhrgAXQC3CZ6i1+KMd9TRu3MvA3aH/fFPnBodb6oe6HM8+lYHrGdRXW8M9bMZtPXUji69lmf5Cmamq7quNLFZXD9Rq7v0Bpc1o/tp0fisAAAAASUVORK5CYII=)](https://github.com/marketplace/actions/create-pull-request) + A GitHub action to create a pull request for changes to your repository in the actions workspace. + +Changes to a repository in the actions workspace persist between actions in a workflow. +This action is useful to pair with other actions that modify or add files to your repository. +The changes will be automatically committed to a new branch and a pull request created. + +Create Pull Request action will: + +1. Check for repository changes in the actions workspace. This includes untracked (new) files as well as modified files. +2. Commit all changes to a new branch. The commit will be made using the name and email of the `HEAD` commit author. +3. Create a pull request to merge the new branch into the currently active branch executing the workflow. + +## Usage + +```hcl +action "Create Pull Request" { + uses = "peter-evans/create-pull-request@v1.0.0" + secrets = ["GITHUB_TOKEN"] +} +``` + +#### Environment variables + +These variables are all optional. If not set, a default value will be used. + +- `PULL_REQUEST_BRANCH` - The branch name. See **Branch naming** below for details. +- `COMMIT_MESSAGE` - The message to use when committing changes. +- `PULL_REQUEST_TITLE` - The title of the pull request. +- `PULL_REQUEST_BODY` - The body of the pull request. + +#### Branch naming + +The variable `PULL_REQUEST_BRANCH` defaults to `create-pull-request/patch`. +Commits will be made to a branch with this name and suffixed with the short SHA1 commit hash. + +e.g. +``` +create-pull-request/patch-fcdfb59 +create-pull-request/patch-394710b +``` + +#### Ignoring files + +If there are files or directories you want to ignore you can simply add them to a `.gitignore` file at the root of your repository. The action will respect this file. + +## Example + +Here is an example that sets all the environment variables. + +```hcl +action "Create Pull Request" { + uses = "peter-evans/create-pull-request@v1.0.0" + secrets = ["GITHUB_TOKEN"] + env = { + PULL_REQUEST_BRANCH = "auto-branch" + COMMIT_MESSAGE = "Auto-modify files by my-file-modifier-action" + PULL_REQUEST_TITLE = "Changes from my-file-modifier-action" + PULL_REQUEST_BODY = "This is an auto-generated PR with changes from my-file-modifier-action" + } +} +``` + +## License + +MIT License - see the [LICENSE](LICENSE) file for details diff --git a/create-pull-request.py b/create-pull-request.py new file mode 100644 index 0000000..74ac5c9 --- /dev/null +++ b/create-pull-request.py @@ -0,0 +1,126 @@ +''' create-pull-request.py ''' +import json +import os +from git import Repo +from github import Github + + +def get_github_event(github_event_path): + with open(github_event_path) as f: + github_event = json.load(f) + if os.environ.get('DEBUG_EVENT') is not None: + print(json.dumps(github_event, sort_keys=True, indent=2)) + return github_event + + +def ignore_event(github_event): + # Ignore push events on deleted branches + deleted = "{deleted}".format(**github_event) + if deleted == "True": + print("Ignoring delete branch event.") + return True + return False + + +def pr_branch_exists(repo, branch): + for ref in repo.remotes.origin.refs: + if ref.name == ("origin/%s" % branch): + return True + return False + + +def get_head_author(github_event): + email = "{head_commit[author][email]}".format(**github_event) + name = "{head_commit[author][name]}".format(**github_event) + return email, name + + +def get_head_short_sha1(repo): + return repo.git.rev_parse('--short', 'HEAD') + + +def set_git_config(git, email, name): + git.config('--global', 'user.email', '"%s"' % email) + git.config('--global', 'user.name', '"%s"' % name) + + +def commit_changes(git, branch, commit_message): + git.checkout('HEAD', b=branch) + git.add('-A') + git.commit(m=commit_message) + return git.push('--set-upstream', 'origin', branch) + + +def create_pull_request(token, repo, head, base, title, body): + return Github(token).get_repo(repo).create_pull( + title=title, + body=body, + base=base, + head=head) + + +def process_event(github_event, repo, branch): + # Fetch required environment variables + github_token = os.environ['GITHUB_TOKEN'] + github_repository = os.environ['GITHUB_REPOSITORY'] + # Fetch remaining optional environment variables + commit_message = os.getenv( + 'COMMIT_MESSAGE', + "Auto-committed changes by create-pull-request action") + title = os.getenv( + 'PULL_REQUEST_TITLE', + "Auto-generated by create-pull-request action") + body = os.getenv( + 'PULL_REQUEST_BODY', "Auto-generated pull request by " + "[create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub Action") + + # Get the HEAD committer's email and name + author_email, author_name = get_head_author(github_event) + # Set git configuration + set_git_config(repo.git, author_email, author_name) + + # Set the target base branch of the pull request + base = repo.active_branch.name + + # Commit the repository changes + print("Committing changes.") + commit_result = commit_changes(repo.git, branch, commit_message) + print(commit_result) + + # Create the pull request + print("Creating a request to pull %s into %s." % (branch, base)) + pull_request = create_pull_request( + github_token, + github_repository, + branch, + base, + title, + body + ) + print("Created pull request %d." % pull_request.number) + + +# Get the JSON event data +github_event = get_github_event(os.environ['GITHUB_EVENT_PATH']) +# Check if this event should be ignored +if not ignore_event(github_event): + # Set the repo to the working directory + repo = Repo(os.getcwd()) + + # Fetch/Set the branch name + branch = os.getenv('PULL_REQUEST_BRANCH', 'create-pull-request/patch') + # Suffix with the short SHA1 hash + branch = "%s-%s" % (branch, get_head_short_sha1(repo)) + + # Check if a PR branch already exists for this HEAD commit + if not pr_branch_exists(repo, branch): + # Check if there are changes to pull request + if repo.is_dirty() or len(repo.untracked_files) > 0: + print("Repository has modified or untracked files.") + process_event(github_event, repo, branch) + else: + print("Repository has no modified or untracked files. Skipping.") + else: + print( + "Pull request branch '%s' already exists for this commit. Skipping." % + branch) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d22536f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +GitPython==2.1.11 +PyGithub==1.43.7 \ No newline at end of file