Compare commits

...

36 Commits

Author SHA1 Message Date
Peter Evans
8c603dbb04 Merge pull request #653 from peter-evans/fix-error-handling
fix: change or->and to catch all errors
2020-12-09 09:56:23 +09:00
Peter Evans
d01e0807ef fix: change or->and to catch all errors 2020-12-09 09:38:10 +09:00
Peter Evans
ce699aa2d1 Merge pull request #637 from peter-evans/prune-remote
fix: prune stale remote ref for self-hosted runners
2020-11-25 12:40:23 +09:00
Peter Evans
9984f611a7 fix: prune stale remote ref for self-hosted runners 2020-11-24 12:34:57 +09:00
Peter Evans
ff0beed1b2 Merge pull request #627 from peter-evans/git-diff-perf
perf: set diff quiet and switch isdirty command order
2020-11-17 13:22:44 +09:00
Peter Evans
ddeca94037 perf: set diff quiet and switch isdirty command order 2020-11-17 11:42:31 +09:00
Peter Evans
0fd77ba8cc docs: add missing contents link 2020-10-26 09:23:01 +09:00
Peter Evans
c7f493a800 docs: remove deprecated set-env example 2020-10-02 15:53:49 +09:00
Peter Evans
91664dfb28 Merge pull request #604 from peter-evans/update-distribution
Update distribution
2020-10-02 15:18:04 +09:00
peter-evans
13ec5274b1 build: update distribution 2020-10-02 06:17:23 +00:00
Peter Evans
bcf9790963 Merge pull request #599 from peter-evans/dependabot/npm_and_yarn/actions/core-1.2.6
Bump @actions/core from 1.2.5 to 1.2.6
2020-10-02 15:14:43 +09:00
dependabot[bot]
88ea447de7 Bump @actions/core from 1.2.5 to 1.2.6
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-01 17:24:16 +00:00
Peter Evans
da928d5fcc Merge pull request #587 from peter-evans/url-output
feat: output the pull request url
2020-09-17 11:18:34 +09:00
Peter Evans
2465e435b9 feat: output the pull request url 2020-09-17 10:41:26 +09:00
Peter Evans
37b2bd1eca Merge pull request #584 from peter-evans/update-distribution
Update distribution
2020-09-17 10:39:30 +09:00
peter-evans
eb13e17e17 build: update distribution 2020-09-17 01:38:44 +00:00
Peter Evans
a1ecc20658 Merge pull request #565 from peter-evans/update-dependencies
Update dependencies
2020-09-17 10:36:25 +09:00
actions-bot
ffcad23634 chore: update dependencies 2020-09-17 01:15:53 +00:00
Peter Evans
f4b52b768a Merge pull request #574 from peter-evans/update-distribution
Update distribution
2020-09-13 15:13:25 +09:00
peter-evans
af682c8fcb build: update distribution 2020-09-13 06:11:21 +00:00
Peter Evans
7378b23cb0 Merge pull request #569 from peter-evans/dependabot/npm_and_yarn/node-fetch-2.6.1
Bump node-fetch from 2.6.0 to 2.6.1
2020-09-13 15:08:27 +09:00
dependabot[bot]
370ae6d537 Bump node-fetch from 2.6.0 to 2.6.1
Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-12 21:49:56 +00:00
Peter Evans
ae0797ee12 ci: update commit message type 2020-09-07 17:25:24 +09:00
Peter Evans
e05457394a ci: update commit messages 2020-09-07 15:26:59 +09:00
Peter Evans
44f76dd5b3 Merge pull request #558 from peter-evans/update-distribution
Update distribution
2020-09-07 09:30:37 +09:00
peter-evans
279e66ed27 Update distribution 2020-09-07 00:29:59 +00:00
Peter Evans
ce9dd3641e Merge pull request #511 from peter-evans/update-dependencies
Update dependencies
2020-09-07 09:27:33 +09:00
actions-bot
1a00b34382 Update dependencies 2020-09-07 09:18:17 +09:00
Peter Evans
e17bb55cb7 Merge pull request #547 from peter-evans/delete-branch
feat: add input for branch delete
2020-09-07 09:14:08 +09:00
Peter Evans
1890e1ec35 Merge pull request #537 from peter-evans/squash-merge-fix
fix: reset branches to handle squash merge
2020-09-07 09:10:33 +09:00
Peter Evans
a49ee3308e feat: add input for branch delete 2020-09-06 10:21:35 +09:00
Peter Evans
16fa12ee5f fix: reset branches to handle squash merge 2020-09-06 08:55:33 +09:00
Peter Evans
5ea31358e9 docs: updates related to checkout 2020-09-01 09:15:45 +09:00
Peter Evans
105f0d3816 docs: update examples 2020-08-30 15:17:23 +09:00
Peter Evans
8fb2374109 docs: update readme 2020-08-30 11:20:42 +09:00
Peter Evans
a68328a1ee docs: update concepts-guidelines 2020-08-30 11:06:03 +09:00
16 changed files with 5110 additions and 4869 deletions

View File

@@ -120,7 +120,7 @@ jobs:
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v3 uses: peter-evans/create-pull-request@v3
with: with:
commit-message: Update distribution commit-message: 'build: update distribution'
title: Update distribution title: Update distribution
body: | body: |
- Updates the distribution for changes on `master` - Updates the distribution for changes on `master`

View File

@@ -34,10 +34,12 @@ jobs:
milestone: 1 milestone: 1
draft: false draft: false
branch: example-patches branch: example-patches
delete-branch: true
- name: Check output - name: Check output
run: | run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
- name: Add reaction - name: Add reaction
uses: peter-evans/create-or-update-comment@v1 uses: peter-evans/create-or-update-comment@v1

View File

@@ -18,7 +18,7 @@ jobs:
uses: peter-evans/create-pull-request@v3 uses: peter-evans/create-pull-request@v3
with: with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }} token: ${{ secrets.ACTIONS_BOT_TOKEN }}
commit-message: Update dependencies commit-message: 'chore: update dependencies'
committer: GitHub <noreply@github.com> committer: GitHub <noreply@github.com>
author: actions-bot <actions-bot@users.noreply.github.com> author: actions-bot <actions-bot@users.noreply.github.com>
title: Update dependencies title: Update dependencies

View File

@@ -26,6 +26,10 @@ Create Pull Request action will:
## Usage ## Usage
```yml ```yml
- uses: actions/checkout@v2
# Make changes to pull request here
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v3 uses: peter-evans/create-pull-request@v3
``` ```
@@ -47,6 +51,7 @@ All inputs are **optional**. If not set, sensible defaults will be used.
| `author` | The author name and email address in the format `Display Name <email@address.com>`. Defaults to the user who triggered the workflow run. | `${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>` | | `author` | The author name and email address in the format `Display Name <email@address.com>`. Defaults to the user who triggered the workflow run. | `${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>` |
| `signoff` | Add [`Signed-off-by`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---signoff) line by the committer at the end of the commit log message. | `false` | | `signoff` | Add [`Signed-off-by`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---signoff) line by the committer at the end of the commit log message. | `false` |
| `branch` | The pull request branch name. | `create-pull-request/patch` | | `branch` | The pull request branch name. | `create-pull-request/patch` |
| `delete-branch` | Delete the `branch` when closing pull requests, and when undeleted after merging. Recommend `true`. | `false` |
| `branch-suffix` | The branch suffix type when using the alternative branching strategy. Valid values are `random`, `timestamp` and `short-commit-hash`. See [Alternative strategy](#alternative-strategy---always-create-a-new-pull-request-branch) for details. | | | `branch-suffix` | The branch suffix type when using the alternative branching strategy. Valid values are `random`, `timestamp` and `short-commit-hash`. See [Alternative strategy](#alternative-strategy---always-create-a-new-pull-request-branch) for details. | |
| `base` | Sets the pull request base branch. | Defaults to the branch checked out in the workflow. | | `base` | Sets the pull request base branch. | Defaults to the branch checked out in the workflow. |
| `push-to-fork` | A fork of the checked-out parent repository to which the pull request branch will be pushed. e.g. `owner/repo-fork`. The pull request will be created to merge the fork's branch into the parent's base. See [push pull request branches to a fork](docs/concepts-guidelines.md#push-pull-request-branches-to-a-fork) for details. | | | `push-to-fork` | A fork of the checked-out parent repository to which the pull request branch will be pushed. e.g. `owner/repo-fork`. The pull request will be created to merge the fork's branch into the parent's base. See [push pull request branches to a fork](docs/concepts-guidelines.md#push-pull-request-branches-to-a-fork) for details. | |
@@ -61,8 +66,8 @@ All inputs are **optional**. If not set, sensible defaults will be used.
### Action outputs ### Action outputs
The pull request number is output as a step output. The pull request number and URL are available as step outputs.
Note that in order to read the step output the action step must have an id. Note that in order to read the step outputs the action step must have an id.
```yml ```yml
- name: Create Pull Request - name: Create Pull Request
@@ -71,6 +76,7 @@ Note that in order to read the step output the action step must have an id.
- name: Check outputs - name: Check outputs
run: | run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
``` ```
### Action behaviour ### Action behaviour
@@ -84,7 +90,7 @@ How the action behaves:
- If there are changes (i.e. a diff exists with the checked-out base branch), the changes will be pushed to a new `branch` and a pull request created. - If there are changes (i.e. a diff exists with the checked-out base branch), the changes will be pushed to a new `branch` and a pull request created.
- If there are no changes (i.e. no diff exists with the checked-out base branch), no pull request will be created and the action exits silently. - If there are no changes (i.e. no diff exists with the checked-out base branch), no pull request will be created and the action exits silently.
- If a pull request already exists and there are no further changes (i.e. no diff with the current pull request branch) then the action exits silently. - If a pull request already exists and there are no further changes (i.e. no diff with the current pull request branch) then the action exits silently.
- If a pull request exists and new changes on the base branch make the pull request unnecessary (i.e. there is no longer a diff between the base and pull request branch), the pull request is automatically closed and the branch deleted. - If a pull request exists and new changes on the base branch make the pull request unnecessary (i.e. there is no longer a diff between the pull request branch and the base), the pull request is automatically closed. Additionally, if `delete-branch` is set to `true` the `branch` will be deleted.
For further details about how the action works and usage guidelines, see [Concepts, guidelines and advanced usage](docs/concepts-guidelines.md). For further details about how the action works and usage guidelines, see [Concepts, guidelines and advanced usage](docs/concepts-guidelines.md).
@@ -147,13 +153,12 @@ To create a project card for the pull request, pass the `pull-request-number` st
## Reference Example ## Reference Example
The following workflow is a reference example that sets many of the main inputs. The following workflow sets many of the action's inputs for reference purposes.
Check the [defaults](#action-inputs) to avoid setting inputs unnecessarily.
See [examples](docs/examples.md) for more realistic use cases. See [examples](docs/examples.md) for more realistic use cases.
```yml ```yml
name: Create Pull Request
on: push
jobs: jobs:
createPullRequest: createPullRequest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -173,6 +178,7 @@ jobs:
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
signoff: false signoff: false
branch: example-patches branch: example-patches
delete-branch: true
title: '[Example] Update report' title: '[Example] Update report'
body: | body: |
Update report Update report
@@ -191,9 +197,10 @@ jobs:
milestone: 1 milestone: 1
draft: false draft: false
- name: Check output - name: Check outputs
run: | run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}" echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
``` ```
An example based on the above reference configuration creates pull requests that look like this: An example based on the above reference configuration creates pull requests that look like this:

View File

@@ -543,6 +543,74 @@ describe('create-or-update-branch tests', () => {
).toBeTruthy() ).toBeTruthy()
}) })
it('tests create, squash merge, and update with identical changes', async () => {
// Branches that have been squash merged appear to have a diff with the base due to
// different commits for the same changes. To prevent creating pull requests
// unnecessarily we reset (rebase) the pull request branch when a reset would result
// in no diff with the base. This will reset any undeleted branches after merging.
// Create tracked and untracked file changes
const changes = await createChanges()
const commitMessage = uuidv4()
const result = await createOrUpdateBranch(
git,
commitMessage,
'',
BRANCH,
REMOTE_NAME,
false
)
expect(result.action).toEqual('created')
expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked)
expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked)
expect(
await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE])
).toBeTruthy()
// Push pull request branch to remote
await git.push([
'--force-with-lease',
REMOTE_NAME,
`HEAD:refs/heads/${BRANCH}`
])
await afterTest(false)
await beforeTest()
// Create a commit on the base with the same changes as the branch
// This simulates squash merge of the pull request
const commits = await createCommits(
git,
1,
changes.tracked,
changes.untracked
)
await git.push([
'--force',
REMOTE_NAME,
`HEAD:refs/heads/${DEFAULT_BRANCH}`
])
// Create the same tracked and untracked file changes (no change on update)
const _changes = await createChanges(changes.tracked, changes.untracked)
const _commitMessage = uuidv4()
const _result = await createOrUpdateBranch(
git,
_commitMessage,
'',
BRANCH,
REMOTE_NAME,
false
)
expect(_result.action).toEqual('updated')
expect(_result.hasDiffWithBase).toBeFalsy()
expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked)
expect(await getFileContent(UNTRACKED_FILE)).toEqual(_changes.untracked)
expect(
await gitLogMatches([...commits.commitMsgs, INIT_COMMIT_MESSAGE])
).toBeTruthy()
})
it('tests create and update with commits on the working base (during the workflow)', async () => { it('tests create and update with commits on the working base (during the workflow)', async () => {
// Create commits on the working base // Create commits on the working base
const commits = await createCommits(git) const commits = await createCommits(git)
@@ -1213,6 +1281,80 @@ describe('create-or-update-branch tests', () => {
).toBeTruthy() ).toBeTruthy()
}) })
it('tests create, squash merge, and update with identical changes (WBNB)', async () => {
// Branches that have been squash merged appear to have a diff with the base due to
// different commits for the same changes. To prevent creating pull requests
// unnecessarily we reset (rebase) the pull request branch when a reset would result
// in no diff with the base. This will reset any undeleted branches after merging.
// Set the working base to a branch that is not the pull request base
await git.checkout(NOT_BASE_BRANCH)
// Create tracked and untracked file changes
const changes = await createChanges()
const commitMessage = uuidv4()
const result = await createOrUpdateBranch(
git,
commitMessage,
BASE,
BRANCH,
REMOTE_NAME,
false
)
expect(result.action).toEqual('created')
expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked)
expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked)
expect(
await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE])
).toBeTruthy()
// Push pull request branch to remote
await git.push([
'--force-with-lease',
REMOTE_NAME,
`HEAD:refs/heads/${BRANCH}`
])
await afterTest(false)
await beforeTest()
// Create a commit on the base with the same changes as the branch
// This simulates squash merge of the pull request
const commits = await createCommits(
git,
1,
changes.tracked,
changes.untracked
)
await git.push([
'--force',
REMOTE_NAME,
`HEAD:refs/heads/${DEFAULT_BRANCH}`
])
// Set the working base to a branch that is not the pull request base
await git.checkout(NOT_BASE_BRANCH)
// Create the same tracked and untracked file changes (no change on update)
const _changes = await createChanges(changes.tracked, changes.untracked)
const _commitMessage = uuidv4()
const _result = await createOrUpdateBranch(
git,
_commitMessage,
BASE,
BRANCH,
REMOTE_NAME,
false
)
expect(_result.action).toEqual('updated')
expect(_result.hasDiffWithBase).toBeFalsy()
expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked)
expect(await getFileContent(UNTRACKED_FILE)).toEqual(_changes.untracked)
expect(
await gitLogMatches([...commits.commitMsgs, INIT_COMMIT_MESSAGE])
).toBeTruthy()
})
it('tests create and update with commits on the working base (during the workflow) (WBNB)', async () => { it('tests create and update with commits on the working base (during the workflow) (WBNB)', async () => {
// Set the working base to a branch that is not the pull request base // Set the working base to a branch that is not the pull request base
await git.checkout(NOT_BASE_BRANCH) await git.checkout(NOT_BASE_BRANCH)

View File

@@ -27,6 +27,11 @@ inputs:
branch: branch:
description: 'The pull request branch name.' description: 'The pull request branch name.'
default: 'create-pull-request/patch' default: 'create-pull-request/patch'
delete-branch:
description: >
Delete the `branch` when closing pull requests, and when undeleted after merging.
Recommend `true`.
default: false
branch-suffix: branch-suffix:
description: 'The branch suffix type when using the alternative branching strategy.' description: 'The branch suffix type when using the alternative branching strategy.'
base: base:

8797
dist/index.js vendored

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,8 @@ A pull request references two branches:
## Events and checkout ## Events and checkout
For each [event type](https://docs.github.com/en/actions/reference/events-that-trigger-workflows) there is a default `GITHUB_SHA` that will be checked out by the GitHub Actions [checkout](https://github.com/actions/checkout) action. This action expects repositories to be checked out with the official GitHub Actions [checkout](https://github.com/actions/checkout) action.
For each [event type](https://docs.github.com/en/actions/reference/events-that-trigger-workflows) there is a default `GITHUB_SHA` that will be checked out.
The default can be overridden by specifying a `ref` on checkout. The default can be overridden by specifying a `ref` on checkout.
@@ -141,9 +142,13 @@ Pull requests created by the action using the default `GITHUB_TOKEN` cannot trig
There are a number of workarounds with different pros and cons. There are a number of workarounds with different pros and cons.
- Use the default `GITHUB_TOKEN` and allow the action to create pull requests that have no checks enabled. Manually close pull requests and immediately reopen them. This will enable `on: pull_request` workflows to run and be added as checks. - Use the default `GITHUB_TOKEN` and allow the action to create pull requests that have no checks enabled. Manually close pull requests and immediately reopen them. This will enable `on: pull_request` workflows to run and be added as checks.
- Use a `repo` scoped [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) created on an account that has write access to the repository that pull requests are being created in. This is the standard workaround and [recommended by GitHub](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token). However, the PAT cannot be scoped to a specific repository so the token becomes a very sensitive secret. If this is a concern, the PAT can instead be created for a dedicated [machine account](https://docs.github.com/en/github/site-policy/github-terms-of-service#3-account-requirements) that has collaborator access to the repository. Also note that because the account that owns the PAT will be the creator of pull requests, that user account will be unable to perform actions such as request changes or approve the pull request. - Use a `repo` scoped [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) created on an account that has write access to the repository that pull requests are being created in. This is the standard workaround and [recommended by GitHub](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token). However, the PAT cannot be scoped to a specific repository so the token becomes a very sensitive secret. If this is a concern, the PAT can instead be created for a dedicated [machine account](https://docs.github.com/en/github/site-policy/github-terms-of-service#3-account-requirements) that has collaborator access to the repository. Also note that because the account that owns the PAT will be the creator of pull requests, that user account will be unable to perform actions such as request changes or approve the pull request.
- Use [SSH (deploy keys)](#push-using-ssh-deploy-keys) to push the pull request branch. This is arguably more secure than using a PAT because deploy keys can be set per repository. However, this method will only trigger `on: push` workflows. - Use [SSH (deploy keys)](#push-using-ssh-deploy-keys) to push the pull request branch. This is arguably more secure than using a PAT because deploy keys can be set per repository. However, this method will only trigger `on: push` workflows.
- Use a [machine account that creates pull requests from its own fork](#push-pull-request-branches-to-a-fork). This is the most secure because the PAT created only grants access to the machine account's fork, not the main repository. This method will trigger `on: pull_request` workflows to run. Workflows triggered `on: push` will not run because the push event is in the fork. - Use a [machine account that creates pull requests from its own fork](#push-pull-request-branches-to-a-fork). This is the most secure because the PAT created only grants access to the machine account's fork, not the main repository. This method will trigger `on: pull_request` workflows to run. Workflows triggered `on: push` will not run because the push event is in the fork.
- Use a [GitHub App to generate a token](#authenticating-with-github-app-generated-tokens) that can be used with this action. GitHub App generated tokens are more secure than using a PAT because GitHub App access permissions can be set with finer granularity and are scoped to only repositories where the App is installed. This method will trigger both `on: push` and `on: pull_request` workflows. - Use a [GitHub App to generate a token](#authenticating-with-github-app-generated-tokens) that can be used with this action. GitHub App generated tokens are more secure than using a PAT because GitHub App access permissions can be set with finer granularity and are scoped to only repositories where the App is installed. This method will trigger both `on: push` and `on: pull_request` workflows.
### Security ### Security

View File

@@ -3,6 +3,8 @@
- [Use case: Create a pull request to update X on push](#use-case-create-a-pull-request-to-update-x-on-push) - [Use case: Create a pull request to update X on push](#use-case-create-a-pull-request-to-update-x-on-push)
- [Update project authors](#update-project-authors) - [Update project authors](#update-project-authors)
- [Keep a branch up-to-date with another](#keep-a-branch-up-to-date-with-another) - [Keep a branch up-to-date with another](#keep-a-branch-up-to-date-with-another)
- [Use case: Create a pull request to update X on release](#use-case-create-a-pull-request-to-update-x-on-release)
- [Update changelog](#update-changelog)
- [Use case: Create a pull request to update X periodically](#use-case-create-a-pull-request-to-update-x-periodically) - [Use case: Create a pull request to update X periodically](#use-case-create-a-pull-request-to-update-x-periodically)
- [Update NPM dependencies](#update-npm-dependencies) - [Update NPM dependencies](#update-npm-dependencies)
- [Update Gradle dependencies](#update-gradle-dependencies) - [Update Gradle dependencies](#update-gradle-dependencies)
@@ -18,6 +20,7 @@
- [Misc workflow tips](#misc-workflow-tips) - [Misc workflow tips](#misc-workflow-tips)
- [Filtering push events](#filtering-push-events) - [Filtering push events](#filtering-push-events)
- [Dynamic configuration using variables](#dynamic-configuration-using-variables) - [Dynamic configuration using variables](#dynamic-configuration-using-variables)
- [Setting the pull request body from a file](#setting-the-pull-request-body-from-a-file)
- [Debugging GitHub Actions](#debugging-github-actions) - [Debugging GitHub Actions](#debugging-github-actions)
@@ -83,6 +86,45 @@ jobs:
branch: production-promotion branch: production-promotion
``` ```
## Use case: Create a pull request to update X on release
This pattern will work well for updating any kind of static content based on the tagged commit of a release. Note that because `release` is one of the [events which checkout a commit](concepts-guidelines.md#events-which-checkout-a-commit) it is necessary to supply the `base` input to the action.
### Update changelog
Raises a pull request to update the `CHANGELOG.md` file based on the tagged commit of the release.
Note that [git-chglog](https://github.com/git-chglog/git-chglog/) requires some configuration files to exist in the repository before this workflow will work.
This workflow assumes the tagged release was made on a default branch called `master`.
```yml
name: Update Changelog
on:
release:
types: [published]
jobs:
updateChangelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Update Changelog
run: |
curl -o git-chglog -L https://github.com/git-chglog/git-chglog/releases/download/0.9.1/git-chglog_linux_amd64
chmod u+x git-chglog
./git-chglog -o CHANGELOG.md
rm git-chglog
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
commit-message: update changelog
title: Update Changelog
body: Update changelog to reflect release changes
branch: update-changelog
base: master
```
## Use case: Create a pull request to update X periodically ## Use case: Create a pull request to update X periodically
This pattern will work well for updating any kind of static content from an external source. The workflow executes on a schedule and raises a pull request when there are changes. This pattern will work well for updating any kind of static content from an external source. The workflow executes on a schedule and raises a pull request when there are changes.
@@ -498,21 +540,6 @@ The recommended method is to use [`set-output`](https://docs.github.com/en/actio
body: ${{ steps.vars.outputs.pr_body }} body: ${{ steps.vars.outputs.pr_body }}
``` ```
Alternatively, [`set-env`](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable) can be used to create environment variables.
```yml
- name: Set environment variables
run: |
echo ::set-env name=PULL_REQUEST_TITLE::"[Test] Add report file $(date +%d-%m-%Y)"
echo ::set-env name=PULL_REQUEST_BODY::"This PR was auto-generated on $(date +%d-%m-%Y) \
by [create-pull-request](https://github.com/peter-evans/create-pull-request)."
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
title: ${{ env.PULL_REQUEST_TITLE }}
body: ${{ env.PULL_REQUEST_BODY }}
```
### Setting the pull request body from a file ### Setting the pull request body from a file
This example shows how file content can be read into a variable and passed to the action. This example shows how file content can be read into a variable and passed to the action.

797
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -29,26 +29,26 @@
}, },
"homepage": "https://github.com/peter-evans/create-pull-request", "homepage": "https://github.com/peter-evans/create-pull-request",
"dependencies": { "dependencies": {
"@actions/core": "1.2.4", "@actions/core": "1.2.6",
"@actions/exec": "1.0.4", "@actions/exec": "1.0.4",
"@octokit/core": "3.1.2", "@octokit/core": "3.1.2",
"@octokit/plugin-paginate-rest": "2.3.0", "@octokit/plugin-paginate-rest": "2.4.0",
"@octokit/plugin-rest-endpoint-methods": "4.1.2", "@octokit/plugin-rest-endpoint-methods": "4.2.0",
"uuid": "8.3.0" "uuid": "8.3.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "26.0.9", "@types/jest": "26.0.14",
"@types/node": "14.0.27", "@types/node": "14.10.3",
"@typescript-eslint/parser": "3.9.0", "@typescript-eslint/parser": "4.1.1",
"@vercel/ncc": "0.23.0", "@vercel/ncc": "0.24.1",
"eslint": "7.6.0", "eslint": "7.9.0",
"eslint-plugin-github": "4.1.1", "eslint-plugin-github": "4.1.1",
"eslint-plugin-jest": "23.20.0", "eslint-plugin-jest": "24.0.1",
"jest": "26.4.0", "jest": "26.4.2",
"jest-circus": "26.4.0", "jest-circus": "26.4.2",
"js-yaml": "3.14.0", "js-yaml": "3.14.0",
"prettier": "2.0.5", "prettier": "2.1.2",
"ts-jest": "26.2.0", "ts-jest": "26.3.0",
"typescript": "3.9.7" "typescript": "4.0.2"
} }
} }

View File

@@ -78,15 +78,6 @@ async function isEven(
) )
} }
async function hasDiff(
git: GitCommandManager,
branch1: string,
branch2: string
): Promise<boolean> {
const result = await git.diff([`${branch1}..${branch2}`])
return result.length > 0
}
function splitLines(multilineString: string): string[] { function splitLines(multilineString: string): string[] {
return multilineString return multilineString
.split('\n') .split('\n')
@@ -196,9 +187,18 @@ export async function createOrUpdateBranch(
// Checkout the pull request branch // Checkout the pull request branch
await git.checkout(branch) await git.checkout(branch)
if (await hasDiff(git, branch, tempBranch)) { // Reset the branch if one of the following conditions is true.
// If the branch differs from the recreated temp version then the branch is reset // - If the branch differs from the recreated temp branch.
// For changes on base this action is similar to a rebase of the pull request branch // - If the recreated temp branch is not ahead of the base. This means there will be
// no pull request diff after the branch is reset. This will reset any undeleted
// branches after merging. In particular, it catches a case where the branch was
// squash merged but not deleted. We need to reset to make sure it doesn't appear
// to have a diff with the base due to different commits for the same changes.
// For changes on base this reset is equivalent to a rebase of the pull request branch.
if (
(await git.hasDiff([`${branch}..${tempBranch}`])) ||
!(await isAhead(git, base, tempBranch))
) {
core.info(`Resetting '${branch}'`) core.info(`Resetting '${branch}'`)
// Alternatively, git switch -C branch tempBranch // Alternatively, git switch -C branch tempBranch
await git.checkout(branch, tempBranch) await git.checkout(branch, tempBranch)

View File

@@ -17,6 +17,7 @@ export interface Inputs {
author: string author: string
signoff: boolean signoff: boolean
branch: string branch: string
deleteBranch: boolean
branchSuffix: string branchSuffix: string
base: string base: string
pushToFork: string pushToFork: string
@@ -105,6 +106,12 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
`The 'base' and 'branch' for a pull request must be different branches. Unable to continue.` `The 'base' and 'branch' for a pull request must be different branches. Unable to continue.`
) )
} }
// For self-hosted runners the repository state persists between runs.
// This command prunes the stale remote ref when the pull request branch was
// deleted after being merged or closed. Without this the push using
// '--force-with-lease' fails due to "stale info."
// https://github.com/peter-evans/create-pull-request/issues/633
await git.exec(['remote', 'prune', branchRemoteName])
core.endGroup() core.endGroup()
// Apply the branch suffix if set // Apply the branch suffix if set
@@ -194,18 +201,21 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
branchRepository branchRepository
) )
} else { } else {
// If there is no longer a diff with the base delete the branch // There is no longer a diff with the base
// Check we are in a state where a branch exists
if (['updated', 'not-updated'].includes(result.action)) { if (['updated', 'not-updated'].includes(result.action)) {
core.info( core.info(
`Branch '${inputs.branch}' no longer differs from base branch '${inputs.base}'` `Branch '${inputs.branch}' no longer differs from base branch '${inputs.base}'`
) )
core.info(`Closing pull request and deleting branch '${inputs.branch}'`) if (inputs.deleteBranch) {
await git.push([ core.info(`Deleting branch '${inputs.branch}'`)
'--delete', await git.push([
'--force', '--delete',
branchRemoteName, '--force',
`refs/heads/${inputs.branch}` branchRemoteName,
]) `refs/heads/${inputs.branch}`
])
}
} }
} }
} catch (error) { } catch (error) {

View File

@@ -96,15 +96,6 @@ export class GitCommandManager {
return output.exitCode === 0 return output.exitCode === 0
} }
async diff(options?: string[]): Promise<string> {
const args = ['-c', 'core.pager=cat', 'diff']
if (options) {
args.push(...options)
}
const output = await this.exec(args)
return output.stdout.trim()
}
async fetch( async fetch(
refSpec: string[], refSpec: string[],
remoteName?: string, remoteName?: string,
@@ -153,18 +144,26 @@ export class GitCommandManager {
return this.workingDirectory return this.workingDirectory
} }
async hasDiff(options?: string[]): Promise<boolean> {
const args = ['diff', '--quiet']
if (options) {
args.push(...options)
}
const output = await this.exec(args, true)
return output.exitCode === 1
}
async isDirty(untracked: boolean): Promise<boolean> { async isDirty(untracked: boolean): Promise<boolean> {
const diffArgs = ['--abbrev=40', '--full-index', '--raw'] // Check untracked changes
// Check staged changes if (untracked && (await this.status(['--porcelain', '-unormal']))) {
if (await this.diff([...diffArgs, '--staged'])) {
return true return true
} }
// Check working index changes // Check working index changes
if (await this.diff(diffArgs)) { if (await this.hasDiff()) {
return true return true
} }
// Check untracked changes // Check staged changes
if (untracked && (await this.status(['--porcelain', '-unormal']))) { if (await this.hasDiff(['--staged'])) {
return true return true
} }
return false return false

View File

@@ -10,6 +10,11 @@ interface Repository {
repo: string repo: string
} }
interface Pull {
number: number
html_url: string
}
export class GitHubHelper { export class GitHubHelper {
private octokit: InstanceType<typeof Octokit> private octokit: InstanceType<typeof Octokit>
@@ -33,7 +38,7 @@ export class GitHubHelper {
inputs: Inputs, inputs: Inputs,
baseRepository: string, baseRepository: string,
headBranch: string headBranch: string
): Promise<number> { ): Promise<Pull> {
// Try to create the pull request // Try to create the pull request
try { try {
const {data: pull} = await this.octokit.pulls.create({ const {data: pull} = await this.octokit.pulls.create({
@@ -47,12 +52,17 @@ export class GitHubHelper {
core.info( core.info(
`Created pull request #${pull.number} (${headBranch} => ${inputs.base})` `Created pull request #${pull.number} (${headBranch} => ${inputs.base})`
) )
return pull.number return {
number: pull.number,
html_url: pull.html_url
}
} catch (e) { } catch (e) {
if ( if (
!e.message || e.message &&
!e.message.includes(`A pull request already exists for ${headBranch}`) e.message.includes(`A pull request already exists for ${headBranch}`)
) { ) {
core.info(`A pull request already exists for ${headBranch}`)
} else {
throw e throw e
} }
} }
@@ -74,7 +84,10 @@ export class GitHubHelper {
core.info( core.info(
`Updated pull request #${pull.number} (${headBranch} => ${inputs.base})` `Updated pull request #${pull.number} (${headBranch} => ${inputs.base})`
) )
return pull.number return {
number: pull.number,
html_url: pull.html_url
}
} }
async getRepositoryParent(headRepository: string): Promise<string> { async getRepositoryParent(headRepository: string): Promise<string> {
@@ -98,16 +111,14 @@ export class GitHubHelper {
const headBranch = `${headOwner}:${inputs.branch}` const headBranch = `${headOwner}:${inputs.branch}`
// Create or update the pull request // Create or update the pull request
const pullNumber = await this.createOrUpdate( const pull = await this.createOrUpdate(inputs, baseRepository, headBranch)
inputs,
baseRepository,
headBranch
)
// Set outputs // Set outputs
core.startGroup('Setting outputs') core.startGroup('Setting outputs')
core.setOutput('pull-request-number', pullNumber) core.setOutput('pull-request-number', pull.number)
core.exportVariable('PULL_REQUEST_NUMBER', pullNumber) core.setOutput('pull-request-url', pull.html_url)
// Deprecated
core.exportVariable('PULL_REQUEST_NUMBER', pull.number)
core.endGroup() core.endGroup()
// Set milestone, labels and assignees // Set milestone, labels and assignees
@@ -127,7 +138,7 @@ export class GitHubHelper {
if (Object.keys(updateIssueParams).length > 0) { if (Object.keys(updateIssueParams).length > 0) {
await this.octokit.issues.update({ await this.octokit.issues.update({
...this.parseRepository(baseRepository), ...this.parseRepository(baseRepository),
issue_number: pullNumber, issue_number: pull.number,
...updateIssueParams ...updateIssueParams
}) })
} }
@@ -146,7 +157,7 @@ export class GitHubHelper {
try { try {
await this.octokit.pulls.requestReviewers({ await this.octokit.pulls.requestReviewers({
...this.parseRepository(baseRepository), ...this.parseRepository(baseRepository),
pull_number: pullNumber, pull_number: pull.number,
...requestReviewersParams ...requestReviewersParams
}) })
} catch (e) { } catch (e) {

View File

@@ -13,6 +13,7 @@ async function run(): Promise<void> {
author: core.getInput('author'), author: core.getInput('author'),
signoff: core.getInput('signoff') === 'true', signoff: core.getInput('signoff') === 'true',
branch: core.getInput('branch'), branch: core.getInput('branch'),
deleteBranch: core.getInput('delete-branch') === 'true',
branchSuffix: core.getInput('branch-suffix'), branchSuffix: core.getInput('branch-suffix'),
base: core.getInput('base'), base: core.getInput('base'),
pushToFork: core.getInput('push-to-fork'), pushToFork: core.getInput('push-to-fork'),