diff --git a/.github/workflows/create-pull-request-multi.yml b/.github/workflows/create-pull-request-multi.yml index e382fdd..a2788ec 100644 --- a/.github/workflows/create-pull-request-multi.yml +++ b/.github/workflows/create-pull-request-multi.yml @@ -10,23 +10,31 @@ jobs: platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v1 - - name: Create report file - if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'macos-latest' - run: touch report.txt - - name: Create report file (windows) - if: matrix.platform == 'windows-latest' - run: type NUL > report.txt - - name: Create Pull Request - uses: peter-evans/create-pull-request@multi-platform-release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMMIT_MESSAGE: Add report file - PULL_REQUEST_BODY: This PR is auto-generated by [create-pull-request](https://github.com/peter-evans/create-pull-request). - PULL_REQUEST_TITLE: '[Example] Add report file' - PULL_REQUEST_LABELS: report, automated pr - PULL_REQUEST_ASSIGNEES: peter-evans - PULL_REQUEST_REVIEWERS: peter-evans - PULL_REQUEST_MILESTONE: 1 - PULL_REQUEST_BRANCH: example-patches - BRANCH_SUFFIX: 'timestamp' + - uses: actions/checkout@v1 + - name: Create report file + if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'macos-latest' + run: date +%s > report.txt + - name: Create report file (windows) + if: matrix.platform == 'windows-latest' + run: echo %DATE% %TIME% > report.txt + - name: Create Pull Request + uses: peter-evans/create-pull-request@multi-platform-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMIT_MESSAGE: Add report file + PULL_REQUEST_BODY: > + This PR is auto-generated by + [create-pull-request](https://github.com/peter-evans/create-pull-request). + PULL_REQUEST_TITLE: '[Example] Add report file' + PULL_REQUEST_LABELS: report, automated pr + PULL_REQUEST_ASSIGNEES: peter-evans + PULL_REQUEST_REVIEWERS: peter-evans + PULL_REQUEST_MILESTONE: 1 + PULL_REQUEST_BRANCH: example-patches + BRANCH_SUFFIX: 'random' + - name: Check output environment variable + if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'macos-latest' + run: echo "Pull Request Number - $PULL_REQUEST_NUMBER" + - name: Check output environment variable (windows) + if: matrix.platform == 'windows-latest' + run: echo Pull Request Number - %PULL_REQUEST_NUMBER% diff --git a/.github/workflows/create-pull-request.yml b/.github/workflows/create-pull-request.yml index c990434..aade0c7 100644 --- a/.github/workflows/create-pull-request.yml +++ b/.github/workflows/create-pull-request.yml @@ -6,19 +6,23 @@ jobs: createPullRequest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Create report file - run: touch report.txt - - name: Create Pull Request - uses: ./ - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMMIT_MESSAGE: Add report file - PULL_REQUEST_BODY: This PR is auto-generated by [create-pull-request](https://github.com/peter-evans/create-pull-request). - PULL_REQUEST_TITLE: '[Example] Add report file' - PULL_REQUEST_LABELS: report, automated pr - PULL_REQUEST_ASSIGNEES: peter-evans - PULL_REQUEST_REVIEWERS: peter-evans - PULL_REQUEST_MILESTONE: 1 - PULL_REQUEST_BRANCH: example-patches - BRANCH_SUFFIX: short-commit-hash + - uses: actions/checkout@v1 + - name: Create report file + run: date +%s > report.txt + - name: Create Pull Request + uses: ./ + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMIT_MESSAGE: Add report file + PULL_REQUEST_BODY: > + This PR is auto-generated by + [create-pull-request](https://github.com/peter-evans/create-pull-request). + PULL_REQUEST_TITLE: '[Example] Add report file' + PULL_REQUEST_LABELS: report, automated pr + PULL_REQUEST_ASSIGNEES: peter-evans + PULL_REQUEST_REVIEWERS: peter-evans + PULL_REQUEST_MILESTONE: 1 + PULL_REQUEST_BRANCH: example-patches + BRANCH_SUFFIX: short-commit-hash + - name: Check output environment variable + run: echo "Pull Request Number - $PULL_REQUEST_NUMBER" diff --git a/README.md b/README.md index db49bb7..46f771c 100644 --- a/README.md +++ b/README.md @@ -10,29 +10,25 @@ The changes will be automatically committed to a new branch and a pull request c 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. +2. Commit all changes to a new branch, or update an existing pull request 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. -Note: Modifying a repository during workflows is not good practice in general. -However, this action opens up some interesting possibilities when used carefully. -This action is experimental and may not work well for some use cases. - ## Usage Linux ```yml - - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.3.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Pull Request + uses: peter-evans/create-pull-request@v1.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` Multi platform - Linux, MacOS, Windows (beta) ```yml - - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.2.1-multi - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Pull Request + uses: peter-evans/create-pull-request@v1.3.1-multi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` #### Environment variables @@ -48,7 +44,11 @@ These variables are all optional. If not set, a default value will be used. - `PULL_REQUEST_TEAM_REVIEWERS` - A comma separated list of GitHub teams to request a review from. - `PULL_REQUEST_MILESTONE` - The number of the milestone to associate this pull request with. - `PULL_REQUEST_BRANCH` - The branch name. See **Branch naming** below for details. -- `BRANCH_SUFFIX` - The branch suffix type. Valid values are `short-commit-hash` (default) and `timestamp`. See **Branch naming** below for details. +- `BRANCH_SUFFIX` - The branch suffix type. Valid values are `short-commit-hash` (default), `timestamp`, `random` and `none`. See **Branch naming** below for details. + +Output environment variables + +- `PULL_REQUEST_NUMBER` - The number of the pull request created. The following parameters are available for debugging and troubleshooting. @@ -57,22 +57,31 @@ The following parameters are available for debugging and troubleshooting. #### 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. +For branch naming there are two strategies. Always create a new branch each time there are changes to be committed, OR, create a pull request branch that will be updated with any new commits until it is merged or closed. -e.g. -``` -create-pull-request/patch-fcdfb59 -create-pull-request/patch-394710b -``` +**Strategy A - Always create a new pull request branch (default)** -Alternatively, branches can be suffixed with a timestamp by setting the environment variable `BRANCH_SUFFIX` to the value `timestamp`. This option must be used if multiple pull requests will be created during the execution of a workflow. +For this strategy there are three options to suffix the branch name. +The branch name is defined by the variable `PULL_REQUEST_BRANCH` and defaults to `create-pull-request/patch`. -e.g. -``` -create-pull-request/patch-1569322532 -create-pull-request/patch-1569322552 -``` +1. `short-commit-hash` (default) + + Commits will be made to a branch suffixed with the short SHA1 commit hash. + eg. `create-pull-request/patch-fcdfb59`, `create-pull-request/patch-394710b` + +2. `timestamp` + + Commits will be made to a branch suffixed by a timestamp. + eg. `create-pull-request/patch-1569322532`, `create-pull-request/patch-1569322552` + +3. `random` + + Commits will be made to a branch suffixed with a random alpha-numeric string. This option must be used if multiple pull requests will be created during the execution of a workflow. + eg. `create-pull-request/patch-6qj97jr`, `create-pull-request/patch-5jrjhvd` + +**Strategy B - Create and update a pull request branch** + +To use this strategy, set `BRANCH_SUFFIX` to the value `none`. The variable `PULL_REQUEST_BRANCH` defaults to `create-pull-request/patch`. Commits will be made to this branch and a pull request created. Any subsequent changes will be committed to the same branch and reflected in the existing pull request. #### Ignoring files @@ -91,22 +100,26 @@ jobs: createPullRequest: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Create report file - run: touch report.txt - - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.3.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMMIT_MESSAGE: Add report file - PULL_REQUEST_BODY: This PR is auto-generated by [create-pull-request](https://github.com/peter-evans/create-pull-request). - PULL_REQUEST_TITLE: '[Example] New report' - PULL_REQUEST_LABELS: report, automated pr - PULL_REQUEST_ASSIGNEES: peter-evans - PULL_REQUEST_REVIEWERS: peter-evans - PULL_REQUEST_MILESTONE: 1 - PULL_REQUEST_BRANCH: example-patches - BRANCH_SUFFIX: short-commit-hash + - uses: actions/checkout@v1 + - name: Create report file + run: date +%s > report.txt + - name: Create Pull Request + uses: peter-evans/create-pull-request@v1.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMMIT_MESSAGE: Add report file + PULL_REQUEST_BODY: > + This PR is auto-generated by + [create-pull-request](https://github.com/peter-evans/create-pull-request). + PULL_REQUEST_TITLE: '[Example] Add report file' + PULL_REQUEST_LABELS: report, automated pr + PULL_REQUEST_ASSIGNEES: peter-evans + PULL_REQUEST_REVIEWERS: peter-evans + PULL_REQUEST_MILESTONE: 1 + PULL_REQUEST_BRANCH: example-patches + BRANCH_SUFFIX: short-commit-hash + - name: Check output environment variable + run: echo "Pull Request Number - $PULL_REQUEST_NUMBER" ``` This configuration will create pull requests that look like this: @@ -115,4 +128,4 @@ This configuration will create pull requests that look like this: ## License -MIT License - see the [LICENSE](LICENSE) file for details +[MIT](LICENSE) diff --git a/create-pull-request.py b/create-pull-request.py index 548e667..9475c9a 100755 --- a/create-pull-request.py +++ b/create-pull-request.py @@ -2,6 +2,9 @@ ''' Create Pull Request ''' import json import os +import random +import string +import sys import time from git import Repo from github import Github @@ -34,7 +37,15 @@ def ignore_event(event_name, event_data): return False -def pr_branch_exists(repo, branch): +def get_head_short_sha1(repo): + return repo.git.rev_parse('--short', 'HEAD') + + +def get_random_suffix(size=7, chars=string.ascii_lowercase + string.digits): + return ''.join(random.choice(chars) for _ in range(size)) + + +def remote_branch_exists(repo, branch): for ref in repo.remotes.origin.refs: if ref.name == ("origin/%s" % branch): return True @@ -51,10 +62,6 @@ def get_head_author(event_name, event_data): 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) @@ -64,11 +71,15 @@ def set_git_remote_url(git, token, github_repository): git.remote('set-url', 'origin', "https://x-access-token:%s@github.com/%s" % (token, github_repository)) -def commit_changes(git, branch, commit_message): - git.checkout('HEAD', b=branch) +def push_changes(git, remote_exists, branch, commit_message): git.add('-A') git.commit(m=commit_message) - return git.push('--set-upstream', 'origin', branch) + if remote_exists: + git.checkout(branch) + git.rebase('-Xtheirs', '-') + else: + git.checkout('HEAD', b=branch) + return git.push('-f', '--set-upstream', 'origin', branch) def cs_string_to_list(str): @@ -78,7 +89,7 @@ def cs_string_to_list(str): return list(filter(None, l)) -def process_event(event_name, event_data, repo, branch, base): +def process_event(event_name, event_data, repo, branch, base, remote_exists): # Fetch required environment variables github_token = os.environ['GITHUB_TOKEN'] github_repository = os.environ['GITHUB_REPOSITORY'] @@ -106,10 +117,15 @@ def process_event(event_name, event_data, repo, branch, base): # Update URL for the 'origin' remote set_git_remote_url(repo.git, github_token, github_repository) - # Commit the repository changes - print("Committing changes.") - commit_result = commit_changes(repo.git, branch, commit_message) - print(commit_result) + # Push the local changes to the remote branch + print("Pushing changes.") + push_result = push_changes(repo.git, remote_exists, branch, commit_message) + print(push_result) + + # If the remote existed then a PR likely exists and we can finish here + if remote_exists: + print("Updated pull request branch %s." % branch) + sys.exit() # Create the pull request print("Creating a request to pull %s into %s." % (branch, base)) @@ -120,6 +136,7 @@ def process_event(event_name, event_data, repo, branch, base): base=base, head=branch) print("Created pull request %d." % pull_request.number) + os.system('echo ::set-env name=PULL_REQUEST_NUMBER::%d' % pull_request.number) # Set labels, assignees and milestone if pull_request_labels is not None: @@ -157,28 +174,34 @@ if skip_ignore_event or not ignore_event(event_name, event_data): base = os.environ['GITHUB_REF'][11:] # Skip if the current branch is a PR branch created by this action - if not base.startswith(branch): - # Fetch an optional environment variable to determine the branch suffix - branch_suffix = os.getenv('BRANCH_SUFFIX', 'short-commit-hash') - if branch_suffix == "timestamp": - # Suffix with the current timestamp - branch = "%s-%s" % (branch, int(time.time())) - else: - # Suffix with the short SHA1 hash - branch = "%s-%s" % (branch, get_head_short_sha1(repo)) + if base.startswith(branch): + print("Branch '%s' was created by this action. Skipping." % base) + sys.exit() - # 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(event_name, event_data, repo, branch, base) - else: - print("Repository has no modified or untracked files. Skipping.") - else: - print( - "Pull request branch '%s' already exists for this commit. Skipping." % - branch) + # Fetch an optional environment variable to determine the branch suffix + branch_suffix = os.getenv('BRANCH_SUFFIX', 'short-commit-hash') + if branch_suffix == "short-commit-hash": + # Suffix with the short SHA1 hash + branch = "%s-%s" % (branch, get_head_short_sha1(repo)) + elif branch_suffix == "timestamp": + # Suffix with the current timestamp + branch = "%s-%s" % (branch, int(time.time())) + elif branch_suffix == "random": + # Suffix with the current timestamp + branch = "%s-%s" % (branch, get_random_suffix()) + + # Check if the remote branch exists + remote_exists = remote_branch_exists(repo, branch) + + # If using short commit hash prefixes, check if a remote + # branch already exists for this HEAD commit + if branch_suffix == 'short-commit-hash' and remote_exists: + print("Pull request branch '%s' already exists for this commit. Skipping." % branch) + sys.exit() + + # 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(event_name, event_data, repo, branch, base, remote_exists) else: - print( - "Branch '%s' was created by this action. Skipping." % base) + print("Repository has no modified or untracked files. Skipping.") diff --git a/pull-request-example.png b/pull-request-example.png index 5f1ead0..6ae9bc0 100644 Binary files a/pull-request-example.png and b/pull-request-example.png differ