Compare commits
254 Commits
Author | SHA1 | Date | |
---|---|---|---|
284f54f989 | |||
9e5b234402 | |||
2d8e7db84c | |||
041b6ab163 | |||
31de0fdf3f | |||
28295f6636 | |||
8dcaf3883b | |||
2827897dcc | |||
c4f19d3a23 | |||
46035868a3 | |||
c09e1094b6 | |||
eab3ea092e | |||
e179caf91b | |||
f3a21bf340 | |||
5b4a9f6a9e | |||
1847e5d1d6 | |||
c246f7e912 | |||
2dd2b11b09 | |||
05d5a3c3f9 | |||
21479f22fc | |||
36a56dac07 | |||
b7f0c9773b | |||
6a62596740 | |||
d1ed29fe1e | |||
e34fedf492 | |||
495ffbb489 | |||
073c8e6ece | |||
375254568c | |||
09af4e30b5 | |||
38e0b6e68b | |||
a95ef54b72 | |||
021e16bf4a | |||
5141da944e | |||
6217f0d61d | |||
e5cb5210cd | |||
7414bc0848 | |||
f8fe2469e5 | |||
b8f683bda6 | |||
d5f5f4bf24 | |||
6c2fad6b3d | |||
a716f73d72 | |||
24b0f8edad | |||
0e6a687637 | |||
e913bb956f | |||
82604c59cc | |||
951d1b87f0 | |||
df0f02f84a | |||
274dd7d2dc | |||
a38ad37eff | |||
450c1f7d69 | |||
a057a6160f | |||
391a18894a | |||
f5be40c5f3 | |||
4eca541d3c | |||
5a4c812497 | |||
37c8100cff | |||
ec919b7792 | |||
000e3c6002 | |||
85176410d0 | |||
ac971fe9b8 | |||
3019dd596d | |||
e73c69172d | |||
d4a593d321 | |||
58815ba2b6 | |||
29267e1085 | |||
7f1f68b1c1 | |||
38e03b3c64 | |||
3861a52f7a | |||
f14600d64a | |||
9895f5748a | |||
84c5454c6e | |||
f63262067e | |||
aa2f52f5a1 | |||
bb5781b719 | |||
e398a27af3 | |||
d015db1f4a | |||
f962808be0 | |||
2dd85e3f19 | |||
2f0833515b | |||
ca4fd2f0d1 | |||
ea54357f43 | |||
df09107abb | |||
16ae6c427b | |||
852fead4ce | |||
9b996f5c6a | |||
e8211ce8f4 | |||
10f43fe334 | |||
1313abbfb6 | |||
73ff87b6a4 | |||
d4d765620e | |||
646f785c1f | |||
f73101ae67 | |||
78389986b6 | |||
c5bc9fd446 | |||
17815d689c | |||
a8d53a0985 | |||
daf3998db5 | |||
8861bdedc8 | |||
75a5de63f0 | |||
d36c8e0863 | |||
2b011faafd | |||
331d02c7e2 | |||
d7db273d6c | |||
ee93d78b55 | |||
6c704eb7a8 | |||
88bf0de51c | |||
b38e8b0abe | |||
b4d51739f9 | |||
ad43dccb4d | |||
c2f9cef04d | |||
b67934cbcf | |||
ef83023339 | |||
671dc9c9e0 | |||
ddab646771 | |||
3f9dbd5a76 | |||
171dd555b9 | |||
6e59b075e0 | |||
9c5916f06d | |||
33434f1c62 | |||
9ca978d38e | |||
8e154b2a92 | |||
18f90432be | |||
2721abb4d0 | |||
20dac2ed48 | |||
8557470a68 | |||
10db75894f | |||
5a6b15373e | |||
923ad837f1 | |||
f094b77505 | |||
af7c021bb9 | |||
97872c4843 | |||
bd72e1b792 | |||
f1a7646cea | |||
15b68d176d | |||
0dfc93c104 | |||
252fb19db2 | |||
4b867c4939 | |||
4fb3835236 | |||
19ace475b4 | |||
84d431ad62 | |||
d6d5519d05 | |||
0e8dfbd57d | |||
ffa8cc2261 | |||
00fb6900cb | |||
2fe7e77753 | |||
18f7dc018c | |||
89265e8d24 | |||
a7bb76508d | |||
357cebe268 | |||
f22a7da129 | |||
3f60247108 | |||
dcd5fd746d | |||
4b53b6fd1a | |||
10a1849302 | |||
c209d72428 | |||
ef9e028216 | |||
f8f85df783 | |||
b9117f2e0c | |||
598ffcd35c | |||
d8e8e547cc | |||
507420e035 | |||
67df31e08a | |||
f530141cd3 | |||
a5a72ba246 | |||
a6c8b3814a | |||
c0a9598b0e | |||
3c3d696d5b | |||
bd0f84d69c | |||
ad71e1f128 | |||
368c9da6f6 | |||
d8a389d1fa | |||
225cf628aa | |||
d7a8d0affc | |||
2dc79e58de | |||
95767e7d51 | |||
3263596ac4 | |||
3e21ec0c82 | |||
5050a372c9 | |||
7380612b49 | |||
771ad1b5f4 | |||
093c191148 | |||
00cb0abb4d | |||
fa0950476f | |||
b90b9c1e20 | |||
028a63020c | |||
9d59234a82 | |||
210f7aab2c | |||
6bb7394339 | |||
a518698c07 | |||
8be395fdd3 | |||
36d063872e | |||
9825ae65b1 | |||
243251cf92 | |||
28beef91aa | |||
01f7dd1d28 | |||
32c71c837c | |||
a6d621d73e | |||
09f51e6391 | |||
ed04db61de | |||
ea6b55fc9d | |||
88896f707d | |||
423630f7c0 | |||
66cc0cc1e2 | |||
2a63057d1e | |||
f83efdc34a | |||
fff683e9ca | |||
9f83247b25 | |||
670e508eb6 | |||
d9d6fd980e | |||
8bb8511e4d | |||
c1d92ef456 | |||
1ff93da091 | |||
0524c01297 | |||
548adff9dc | |||
28674474a4 | |||
4fb90330a4 | |||
e4c811acf5 | |||
99ccb3479b | |||
13616a4432 | |||
b5b91bc2b0 | |||
5666cd8fe9 | |||
ad897490d5 | |||
0735106af9 | |||
9aeedaa8c2 | |||
52d31873b6 | |||
09b9ac155b | |||
6ec5e3e26b | |||
8b46437b6d | |||
e361fd1788 | |||
052fc72b41 | |||
ed00d4629c | |||
34371f09e5 | |||
c27ea51ae0 | |||
5e9d0ee9ea | |||
b5f41d9b08 | |||
2455e15969 | |||
05bc46786e | |||
adc6552966 | |||
171fc6cce4 | |||
3fb765f674 | |||
d95c81ee98 | |||
8d5ed6557f | |||
7b1819c092 | |||
be0a8c9666 | |||
a0a6157bf1 | |||
9c5ec2e07d | |||
45c510e1f6 | |||
249b80db6b | |||
6c2b44c6ac | |||
76c58cf6a9 | |||
8c603dbb04 | |||
d01e0807ef | |||
ce699aa2d1 | |||
9984f611a7 |
@ -9,11 +9,15 @@
|
|||||||
"plugin:import/errors",
|
"plugin:import/errors",
|
||||||
"plugin:import/warnings",
|
"plugin:import/warnings",
|
||||||
"plugin:import/typescript",
|
"plugin:import/typescript",
|
||||||
"plugin:prettier/recommended",
|
"plugin:prettier/recommended"
|
||||||
"prettier/@typescript-eslint"
|
|
||||||
],
|
],
|
||||||
"plugins": ["@typescript-eslint"],
|
"plugins": ["@typescript-eslint"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/camelcase": "off"
|
"@typescript-eslint/camelcase": "off"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"typescript": {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
github: peter-evans
|
20
.github/dependabot.yml
vendored
Normal file
20
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
day: "tuesday"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
day: "tuesday"
|
||||||
|
ignore:
|
||||||
|
- dependency-name: "*"
|
||||||
|
update-types: ["version-update:semver-major"]
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
13
.github/workflows/automerge-dependabot.yml
vendored
Normal file
13
.github/workflows/automerge-dependabot.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
name: Auto-merge Dependabot
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
automerge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.actor == 'dependabot[bot]'
|
||||||
|
steps:
|
||||||
|
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
|
||||||
|
pull-request-number: ${{ github.event.pull_request.number }}
|
||||||
|
merge-method: squash
|
50
.github/workflows/ci.yml
vendored
50
.github/workflows/ci.yml
vendored
@ -1,36 +1,39 @@
|
|||||||
name: CI
|
name: CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches: [main]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [main]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
- uses: actions/setup-python@v2
|
cache: npm
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run format-check
|
- run: npm run format-check
|
||||||
- run: npm run lint
|
- run: npm run lint
|
||||||
- run: npm run test
|
- run: npm run test
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: dist
|
path: dist
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: action.yml
|
name: action.yml
|
||||||
path: action.yml
|
path: action.yml
|
||||||
@ -43,16 +46,16 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
target: [built, committed]
|
target: [built, committed]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: main
|
||||||
- if: matrix.target == 'built' || github.event_name == 'pull_request'
|
- if: matrix.target == 'built' || github.event_name == 'pull_request'
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: dist
|
path: dist
|
||||||
- if: matrix.target == 'built' || github.event_name == 'pull_request'
|
- if: matrix.target == 'built' || github.event_name == 'pull_request'
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: action.yml
|
name: action.yml
|
||||||
path: .
|
path: .
|
||||||
@ -74,10 +77,10 @@ jobs:
|
|||||||
Auto-generated by [create-pull-request][1]
|
Auto-generated by [create-pull-request][1]
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
[1]: https://github.com/peter-evans/create-pull-request
|
||||||
branch: ci-test-${{ matrix.target }}
|
branch: ci-test-${{ matrix.target }}-${{ github.sha }}
|
||||||
|
|
||||||
- name: Close Pull
|
- name: Close Pull
|
||||||
uses: peter-evans/close-pull@v1
|
uses: peter-evans/close-pull@v3
|
||||||
with:
|
with:
|
||||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||||
comment: '[CI] test ${{ matrix.target }}'
|
comment: '[CI] test ${{ matrix.target }}'
|
||||||
@ -89,7 +92,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Find Comment
|
- name: Find Comment
|
||||||
uses: peter-evans/find-comment@v1
|
uses: peter-evans/find-comment@v2
|
||||||
id: fc
|
id: fc
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.number }}
|
issue-number: ${{ github.event.number }}
|
||||||
@ -98,7 +101,7 @@ jobs:
|
|||||||
|
|
||||||
- if: steps.fc.outputs.comment-id == ''
|
- if: steps.fc.outputs.comment-id == ''
|
||||||
name: Create comment
|
name: Create comment
|
||||||
uses: peter-evans/create-or-update-comment@v1
|
uses: peter-evans/create-or-update-comment@v3
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.number }}
|
issue-number: ${{ github.event.number }}
|
||||||
body: |
|
body: |
|
||||||
@ -108,22 +111,23 @@ jobs:
|
|||||||
```
|
```
|
||||||
|
|
||||||
package:
|
package:
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
needs: [test]
|
needs: [test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/download-artifact@v2
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: dist
|
path: dist
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
|
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
|
||||||
commit-message: 'build: 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 `main`
|
||||||
|
|
||||||
Auto-generated by [create-pull-request][1]
|
Auto-generated by [create-pull-request][1]
|
||||||
|
|
||||||
|
4
.github/workflows/cpr-example-command.yml
vendored
4
.github/workflows/cpr-example-command.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
|||||||
createPullRequest:
|
createPullRequest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Make changes to pull request
|
- name: Make changes to pull request
|
||||||
run: date +%s > report.txt
|
run: date +%s > report.txt
|
||||||
@ -42,7 +42,7 @@ jobs:
|
|||||||
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
|
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@v3
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
|
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
|
||||||
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
|
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
|
||||||
|
8
.github/workflows/slash-command-dispatch.yml
vendored
8
.github/workflows/slash-command-dispatch.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Slash Command Dispatch
|
- name: Slash Command Dispatch
|
||||||
uses: peter-evans/slash-command-dispatch@v2
|
uses: peter-evans/slash-command-dispatch@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
|
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
|
||||||
config: >
|
config: >
|
||||||
@ -18,6 +18,12 @@ jobs:
|
|||||||
"repository": "peter-evans/create-pull-request-tests",
|
"repository": "peter-evans/create-pull-request-tests",
|
||||||
"named_args": true
|
"named_args": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "testv4",
|
||||||
|
"permission": "admin",
|
||||||
|
"repository": "peter-evans/create-pull-request-tests",
|
||||||
|
"named_args": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "clean",
|
"command": "clean",
|
||||||
"permission": "admin",
|
"permission": "admin",
|
||||||
|
31
.github/workflows/update-dep.yml
vendored
31
.github/workflows/update-dep.yml
vendored
@ -1,31 +0,0 @@
|
|||||||
name: Update Dependencies
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 1 * * 4'
|
|
||||||
jobs:
|
|
||||||
update-dep:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: '12.x'
|
|
||||||
- name: Update dependencies
|
|
||||||
run: |
|
|
||||||
npx -p npm-check-updates ncu -u
|
|
||||||
npm install
|
|
||||||
- name: Create Pull Request
|
|
||||||
uses: peter-evans/create-pull-request@v3
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
|
|
||||||
commit-message: 'chore: update dependencies'
|
|
||||||
committer: GitHub <noreply@github.com>
|
|
||||||
author: actions-bot <actions-bot@users.noreply.github.com>
|
|
||||||
title: Update dependencies
|
|
||||||
body: |
|
|
||||||
- Dependency updates
|
|
||||||
|
|
||||||
Auto-generated by [create-pull-request][1]
|
|
||||||
|
|
||||||
[1]: https://github.com/peter-evans/create-pull-request
|
|
||||||
branch: update-dependencies
|
|
32
.github/workflows/update-major-version.yml
vendored
Normal file
32
.github/workflows/update-major-version.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: Update Major Version
|
||||||
|
run-name: Update ${{ github.event.inputs.main_version }} to ${{ github.event.inputs.target }}
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
target:
|
||||||
|
description: The target tag or reference
|
||||||
|
required: true
|
||||||
|
main_version:
|
||||||
|
type: choice
|
||||||
|
description: The major version tag to update
|
||||||
|
options:
|
||||||
|
- v4
|
||||||
|
- v5
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Git config
|
||||||
|
run: |
|
||||||
|
git config user.name actions-bot
|
||||||
|
git config user.email actions-bot@users.noreply.github.com
|
||||||
|
- name: Tag new target
|
||||||
|
run: git tag -f ${{ github.event.inputs.main_version }} ${{ github.event.inputs.target }}
|
||||||
|
- name: Push new tag
|
||||||
|
run: git push origin ${{ github.event.inputs.main_version }} --force
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ lib/
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
116
README.md
116
README.md
@ -1,6 +1,6 @@
|
|||||||
# <img width="24" height="24" src="docs/assets/logo.svg"> Create Pull Request
|
# <img width="24" height="24" src="docs/assets/logo.svg"> Create Pull Request
|
||||||
[](https://github.com/peter-evans/create-pull-request/actions?query=workflow%3ACI)
|
[](https://github.com/peter-evans/create-pull-request/actions?query=workflow%3ACI)
|
||||||
[](https://github.com/marketplace/actions/create-pull-request)
|
[](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.
|
A GitHub action to create a pull request for changes to your repository in the actions workspace.
|
||||||
|
|
||||||
@ -21,20 +21,27 @@ Create Pull Request action will:
|
|||||||
|
|
||||||
- [Concepts, guidelines and advanced usage](docs/concepts-guidelines.md)
|
- [Concepts, guidelines and advanced usage](docs/concepts-guidelines.md)
|
||||||
- [Examples](docs/examples.md)
|
- [Examples](docs/examples.md)
|
||||||
- [Updating to v3](docs/updating.md)
|
- [Updating to v5](docs/updating.md)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
# Make changes to pull request here
|
# 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@v5
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v3.x.x`
|
You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v5.x.x`
|
||||||
|
|
||||||
|
### Workflow permissions
|
||||||
|
|
||||||
|
For this action to work you must explicitly allow GitHub Actions to create pull requests.
|
||||||
|
This setting can be found in a repository's settings under Actions > General > Workflow permissions.
|
||||||
|
|
||||||
|
For repositories belonging to an organization, this setting can be managed by admins in organization settings under Actions > General > Workflow permissions.
|
||||||
|
|
||||||
### Action inputs
|
### Action inputs
|
||||||
|
|
||||||
@ -44,36 +51,54 @@ All inputs are **optional**. If not set, sensible defaults will be used.
|
|||||||
|
|
||||||
| Name | Description | Default |
|
| Name | Description | Default |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `token` | `GITHUB_TOKEN` or a `repo` scoped [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). | `GITHUB_TOKEN` |
|
| `token` | `GITHUB_TOKEN` (permissions `contents: write` and `pull-requests: write`) or a `repo` scoped [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). | `GITHUB_TOKEN` |
|
||||||
| `path` | Relative path under `GITHUB_WORKSPACE` to the repository. | `GITHUB_WORKSPACE` |
|
| `path` | Relative path under `GITHUB_WORKSPACE` to the repository. | `GITHUB_WORKSPACE` |
|
||||||
|
| `add-paths` | A comma or newline-separated list of file paths to commit. Paths should follow git's [pathspec](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec) syntax. If no paths are specified, all new and modified files are added. See [Add specific paths](#add-specific-paths). | |
|
||||||
| `commit-message` | The message to use when committing changes. | `[create-pull-request] automated change` |
|
| `commit-message` | The message to use when committing changes. | `[create-pull-request] automated change` |
|
||||||
| `committer` | The committer name and email address in the format `Display Name <email@address.com>`. Defaults to the GitHub Actions bot user. | `GitHub <noreply@github.com>` |
|
| `committer` | The committer name and email address in the format `Display Name <email@address.com>`. Defaults to the GitHub Actions bot user. | `GitHub <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>` |
|
| `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` |
|
| `delete-branch` | Delete the `branch` when closing pull requests, and when undeleted after merging. | `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. | |
|
||||||
| `title` | The title of the pull request. | `Changes by create-pull-request action` |
|
| `title` | The title of the pull request. | `Changes by create-pull-request action` |
|
||||||
| `body` | The body of the pull request. | `Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action` |
|
| `body` | The body of the pull request. | `Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action` |
|
||||||
|
| `body-path` | The path to a file containing the pull request body. Takes precedence over `body`. | |
|
||||||
| `labels` | A comma or newline-separated list of labels. | |
|
| `labels` | A comma or newline-separated list of labels. | |
|
||||||
| `assignees` | A comma or newline-separated list of assignees (GitHub usernames). | |
|
| `assignees` | A comma or newline-separated list of assignees (GitHub usernames). | |
|
||||||
| `reviewers` | A comma or newline-separated list of reviewers (GitHub usernames) to request a review from. | |
|
| `reviewers` | A comma or newline-separated list of reviewers (GitHub usernames) to request a review from. | |
|
||||||
| `team-reviewers` | A comma or newline-separated list of GitHub teams to request a review from. Note that a `repo` scoped [PAT](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) may be required. See [this issue](https://github.com/peter-evans/create-pull-request/issues/155). | |
|
| `team-reviewers` | A comma or newline-separated list of GitHub teams to request a review from. Note that a `repo` scoped [PAT](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token), or equivalent [GitHub App permissions](docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens), are required. | |
|
||||||
| `milestone` | The number of the milestone to associate this pull request with. | |
|
| `milestone` | The number of the milestone to associate this pull request with. | |
|
||||||
| `draft` | Create a [draft pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests). | `false` |
|
| `draft` | Create a [draft pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests). It is not possible to change draft status after creation except through the web interface. | `false` |
|
||||||
|
|
||||||
|
For self-hosted runners behind a corporate proxy set the `https_proxy` environment variable.
|
||||||
|
```yml
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
env:
|
||||||
|
https_proxy: http://<proxy_address>:<port>
|
||||||
|
```
|
||||||
|
|
||||||
### Action outputs
|
### Action outputs
|
||||||
|
|
||||||
The pull request number and URL are available as step outputs.
|
The following outputs can be used by subsequent workflow steps.
|
||||||
|
|
||||||
|
- `pull-request-number` - The pull request number.
|
||||||
|
- `pull-request-url` - The URL of the pull request.
|
||||||
|
- `pull-request-operation` - The pull request operation performed by the action, `created`, `updated` or `closed`.
|
||||||
|
- `pull-request-head-sha` - The commit SHA of the pull request branch.
|
||||||
|
|
||||||
|
Step outputs can be accessed as in the following example.
|
||||||
Note that in order to read the step outputs 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
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
- name: Check outputs
|
- name: Check outputs
|
||||||
|
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||||
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 }}"
|
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
|
||||||
@ -89,7 +114,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 it will be updated if necessary. Local changes in the Actions workspace, or changes on the base branch, can cause an update. If no update is required 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 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.
|
- 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).
|
||||||
@ -107,14 +132,48 @@ To use this strategy, set input `branch-suffix` with one of the following option
|
|||||||
|
|
||||||
- `short-commit-hash` - Commits will be made to a branch suffixed with the short SHA1 commit hash. e.g. `create-pull-request/patch-fcdfb59`, `create-pull-request/patch-394710b`
|
- `short-commit-hash` - Commits will be made to a branch suffixed with the short SHA1 commit hash. e.g. `create-pull-request/patch-fcdfb59`, `create-pull-request/patch-394710b`
|
||||||
|
|
||||||
### Controlling commits
|
### Controlling committed files
|
||||||
|
|
||||||
|
The action defaults to adding all new and modified files.
|
||||||
|
If there are files that should not be included in the pull request, you can use the following methods to control the committed content.
|
||||||
|
|
||||||
|
#### Remove files
|
||||||
|
|
||||||
|
The most straightforward way to handle unwanted files is simply to remove them in a step before the action runs.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- run: |
|
||||||
|
rm -rf temp-dir
|
||||||
|
rm temp-file.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Ignore 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.
|
||||||
|
|
||||||
|
#### Add specific paths
|
||||||
|
|
||||||
|
You can control which files are committed with the `add-paths` input.
|
||||||
|
Paths should follow git's [pathspec](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec) syntax.
|
||||||
|
File changes that do not match one of the paths will be stashed and restored after the action has completed.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
with:
|
||||||
|
add-paths: |
|
||||||
|
*.java
|
||||||
|
docs/*.md
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create your own commits
|
||||||
|
|
||||||
As well as relying on the action to handle uncommitted changes, you can additionally make your own commits before the action runs.
|
As well as relying on the action to handle uncommitted changes, you can additionally make your own commits before the action runs.
|
||||||
Note that the repository must be checked out on a branch with a remote, it won't work for [events which checkout a commit](docs/concepts-guidelines.md#events-which-checkout-a-commit).
|
Note that the repository must be checked out on a branch with a remote, it won't work for [events which checkout a commit](docs/concepts-guidelines.md#events-which-checkout-a-commit).
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Create commits
|
- name: Create commits
|
||||||
run: |
|
run: |
|
||||||
git config user.name 'Peter Evans'
|
git config user.name 'Peter Evans'
|
||||||
@ -127,13 +186,9 @@ Note that the repository must be checked out on a branch with a remote, it won't
|
|||||||
- name: Uncommitted change
|
- name: Uncommitted change
|
||||||
run: date +%s > report.txt
|
run: date +%s > report.txt
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
```
|
```
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### Create a project card
|
### Create a project card
|
||||||
|
|
||||||
To create a project card for the pull request, pass the `pull-request-number` step output to [create-or-update-project-card](https://github.com/peter-evans/create-or-update-project-card) action.
|
To create a project card for the pull request, pass the `pull-request-number` step output to [create-or-update-project-card](https://github.com/peter-evans/create-or-update-project-card) action.
|
||||||
@ -141,16 +196,22 @@ To create a project card for the pull request, pass the `pull-request-number` st
|
|||||||
```yml
|
```yml
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
|
||||||
- name: Create or Update Project Card
|
- name: Create or Update Project Card
|
||||||
uses: peter-evans/create-or-update-project-card@v1
|
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||||
|
uses: peter-evans/create-or-update-project-card@v2
|
||||||
with:
|
with:
|
||||||
project-name: My project
|
project-name: My project
|
||||||
column-name: My column
|
column-name: My column
|
||||||
issue-number: ${{ steps.cpr.outputs.pull-request-number }}
|
issue-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Auto-merge
|
||||||
|
|
||||||
|
Auto-merge can be enabled on a pull request allowing it to be automatically merged once requirements have been satisfied.
|
||||||
|
See [enable-pull-request-automerge](https://github.com/peter-evans/enable-pull-request-automerge) action for usage details.
|
||||||
|
|
||||||
## Reference Example
|
## Reference Example
|
||||||
|
|
||||||
The following workflow sets many of the action's inputs for reference purposes.
|
The following workflow sets many of the action's inputs for reference purposes.
|
||||||
@ -163,14 +224,14 @@ jobs:
|
|||||||
createPullRequest:
|
createPullRequest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Make changes to pull request
|
- name: Make changes to pull request
|
||||||
run: date +%s > report.txt
|
run: date +%s > report.txt
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
commit-message: Update report
|
commit-message: Update report
|
||||||
@ -192,15 +253,10 @@ jobs:
|
|||||||
assignees: peter-evans
|
assignees: peter-evans
|
||||||
reviewers: peter-evans
|
reviewers: peter-evans
|
||||||
team-reviewers: |
|
team-reviewers: |
|
||||||
owners
|
developers
|
||||||
maintainers
|
qa-team
|
||||||
milestone: 1
|
milestone: 1
|
||||||
draft: false
|
draft: false
|
||||||
|
|
||||||
- name: Check outputs
|
|
||||||
run: |
|
|
||||||
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:
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@ WORKINGDIR=$PWD
|
|||||||
|
|
||||||
# Create and serve a remote repo
|
# Create and serve a remote repo
|
||||||
mkdir -p /git/remote
|
mkdir -p /git/remote
|
||||||
|
git config --global init.defaultBranch main
|
||||||
git init --bare /git/remote/test-base.git
|
git init --bare /git/remote/test-base.git
|
||||||
git daemon --verbose --enable=receive-pack --base-path=/git/remote --export-all /git/remote &>/dev/null &
|
git daemon --verbose --enable=receive-pack --base-path=/git/remote --export-all /git/remote &>/dev/null &
|
||||||
|
|
||||||
|
@ -46,4 +46,25 @@ describe('git-auth-helper tests', () => {
|
|||||||
|
|
||||||
await gitAuthHelper.removeAuth()
|
await gitAuthHelper.removeAuth()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('tests adding and removing the safe.directory config', async () => {
|
||||||
|
await git.config('safe.directory', '/another-value', true, true)
|
||||||
|
|
||||||
|
await gitAuthHelper.removeSafeDirectory()
|
||||||
|
await gitAuthHelper.addSafeDirectory()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await git.configExists('safe.directory', REPO_PATH, true)
|
||||||
|
).toBeTruthy()
|
||||||
|
|
||||||
|
await gitAuthHelper.addSafeDirectory()
|
||||||
|
await gitAuthHelper.removeSafeDirectory()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await git.configExists('safe.directory', REPO_PATH, true)
|
||||||
|
).toBeFalsy()
|
||||||
|
expect(
|
||||||
|
await git.configExists('safe.directory', '/another-value', true)
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -8,15 +8,15 @@ if [[ "$(docker images -q $IMAGE 2> /dev/null)" == "" || $ARG1 == "build" ]]; th
|
|||||||
echo "Building Docker image $IMAGE ..."
|
echo "Building Docker image $IMAGE ..."
|
||||||
|
|
||||||
cat > Dockerfile << EOF
|
cat > Dockerfile << EOF
|
||||||
FROM node:12-alpine
|
FROM node:16-alpine
|
||||||
RUN apk --no-cache add git git-daemon
|
RUN apk --no-cache add git git-daemon
|
||||||
RUN npm install jest --global
|
RUN npm install jest jest-environment-jsdom --global
|
||||||
WORKDIR /cpr
|
WORKDIR /cpr
|
||||||
COPY __test__/entrypoint.sh /entrypoint.sh
|
COPY __test__/entrypoint.sh /entrypoint.sh
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
docker build -t $IMAGE .
|
docker build --no-cache -t $IMAGE .
|
||||||
rm Dockerfile
|
rm Dockerfile
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -25,6 +25,18 @@ describe('utils tests', () => {
|
|||||||
expect(array2.length).toEqual(0)
|
expect(array2.length).toEqual(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('stripOrgPrefixFromTeams strips org prefixes correctly', async () => {
|
||||||
|
const array = utils.stripOrgPrefixFromTeams([
|
||||||
|
'org/team1',
|
||||||
|
'org/team2',
|
||||||
|
'team3'
|
||||||
|
])
|
||||||
|
expect(array.length).toEqual(3)
|
||||||
|
expect(array[0]).toEqual('team1')
|
||||||
|
expect(array[1]).toEqual('team2')
|
||||||
|
expect(array[2]).toEqual('team3')
|
||||||
|
})
|
||||||
|
|
||||||
test('getRepoPath successfully returns the path to the repository', async () => {
|
test('getRepoPath successfully returns the path to the repository', async () => {
|
||||||
expect(utils.getRepoPath()).toEqual(process.env['GITHUB_WORKSPACE'])
|
expect(utils.getRepoPath()).toEqual(process.env['GITHUB_WORKSPACE'])
|
||||||
expect(utils.getRepoPath('foo')).toEqual(
|
expect(utils.getRepoPath('foo')).toEqual(
|
||||||
@ -50,6 +62,30 @@ describe('utils tests', () => {
|
|||||||
)
|
)
|
||||||
expect(remote3.protocol).toEqual('SSH')
|
expect(remote3.protocol).toEqual('SSH')
|
||||||
expect(remote3.repository).toEqual('peter-evans/create-pull-request')
|
expect(remote3.repository).toEqual('peter-evans/create-pull-request')
|
||||||
|
|
||||||
|
const remote4 = utils.getRemoteDetail(
|
||||||
|
'https://github.com/peter-evans/create-pull-request.git'
|
||||||
|
)
|
||||||
|
expect(remote4.protocol).toEqual('HTTPS')
|
||||||
|
expect(remote4.repository).toEqual('peter-evans/create-pull-request')
|
||||||
|
|
||||||
|
const remote5 = utils.getRemoteDetail(
|
||||||
|
'https://github.com/peter-evans/ungit'
|
||||||
|
)
|
||||||
|
expect(remote5.protocol).toEqual('HTTPS')
|
||||||
|
expect(remote5.repository).toEqual('peter-evans/ungit')
|
||||||
|
|
||||||
|
const remote6 = utils.getRemoteDetail(
|
||||||
|
'https://github.com/peter-evans/ungit.git'
|
||||||
|
)
|
||||||
|
expect(remote6.protocol).toEqual('HTTPS')
|
||||||
|
expect(remote6.repository).toEqual('peter-evans/ungit')
|
||||||
|
|
||||||
|
const remote7 = utils.getRemoteDetail(
|
||||||
|
'git@github.com:peter-evans/ungit.git'
|
||||||
|
)
|
||||||
|
expect(remote7.protocol).toEqual('SSH')
|
||||||
|
expect(remote7.repository).toEqual('peter-evans/ungit')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('getRemoteDetail fails to parse a remote URL', async () => {
|
test('getRemoteDetail fails to parse a remote URL', async () => {
|
||||||
@ -58,7 +94,7 @@ describe('utils tests', () => {
|
|||||||
utils.getRemoteDetail(remoteUrl)
|
utils.getRemoteDetail(remoteUrl)
|
||||||
// Fail the test if an error wasn't thrown
|
// Fail the test if an error wasn't thrown
|
||||||
expect(true).toEqual(false)
|
expect(true).toEqual(false)
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
expect(e.message).toEqual(
|
expect(e.message).toEqual(
|
||||||
`The format of '${remoteUrl}' is not a valid GitHub repository URL`
|
`The format of '${remoteUrl}' is not a valid GitHub repository URL`
|
||||||
)
|
)
|
||||||
@ -66,11 +102,28 @@ describe('utils tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('getRemoteUrl successfully returns remote URLs', async () => {
|
test('getRemoteUrl successfully returns remote URLs', async () => {
|
||||||
const url1 = utils.getRemoteUrl('HTTPS', 'peter-evans/create-pull-request')
|
const url1 = utils.getRemoteUrl(
|
||||||
|
'HTTPS',
|
||||||
|
'github.com',
|
||||||
|
'peter-evans/create-pull-request'
|
||||||
|
)
|
||||||
expect(url1).toEqual('https://github.com/peter-evans/create-pull-request')
|
expect(url1).toEqual('https://github.com/peter-evans/create-pull-request')
|
||||||
|
|
||||||
const url2 = utils.getRemoteUrl('SSH', 'peter-evans/create-pull-request')
|
const url2 = utils.getRemoteUrl(
|
||||||
|
'SSH',
|
||||||
|
'github.com',
|
||||||
|
'peter-evans/create-pull-request'
|
||||||
|
)
|
||||||
expect(url2).toEqual('git@github.com:peter-evans/create-pull-request.git')
|
expect(url2).toEqual('git@github.com:peter-evans/create-pull-request.git')
|
||||||
|
|
||||||
|
const url3 = utils.getRemoteUrl(
|
||||||
|
'HTTPS',
|
||||||
|
'mygithubserver.com',
|
||||||
|
'peter-evans/create-pull-request'
|
||||||
|
)
|
||||||
|
expect(url3).toEqual(
|
||||||
|
'https://mygithubserver.com/peter-evans/create-pull-request'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('secondsSinceEpoch returns the number of seconds since the Epoch', async () => {
|
test('secondsSinceEpoch returns the number of seconds since the Epoch', async () => {
|
||||||
@ -104,7 +157,7 @@ describe('utils tests', () => {
|
|||||||
utils.parseDisplayNameEmail(displayNameEmail1)
|
utils.parseDisplayNameEmail(displayNameEmail1)
|
||||||
// Fail the test if an error wasn't thrown
|
// Fail the test if an error wasn't thrown
|
||||||
expect(true).toEqual(false)
|
expect(true).toEqual(false)
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
expect(e.message).toEqual(
|
expect(e.message).toEqual(
|
||||||
`The format of '${displayNameEmail1}' is not a valid email address with display name`
|
`The format of '${displayNameEmail1}' is not a valid email address with display name`
|
||||||
)
|
)
|
||||||
@ -115,7 +168,7 @@ describe('utils tests', () => {
|
|||||||
utils.parseDisplayNameEmail(displayNameEmail2)
|
utils.parseDisplayNameEmail(displayNameEmail2)
|
||||||
// Fail the test if an error wasn't thrown
|
// Fail the test if an error wasn't thrown
|
||||||
expect(true).toEqual(false)
|
expect(true).toEqual(false)
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
expect(e.message).toEqual(
|
expect(e.message).toEqual(
|
||||||
`The format of '${displayNameEmail2}' is not a valid email address with display name`
|
`The format of '${displayNameEmail2}' is not a valid email address with display name`
|
||||||
)
|
)
|
||||||
|
17
action.yml
17
action.yml
@ -8,6 +8,11 @@ inputs:
|
|||||||
description: >
|
description: >
|
||||||
Relative path under $GITHUB_WORKSPACE to the repository.
|
Relative path under $GITHUB_WORKSPACE to the repository.
|
||||||
Defaults to $GITHUB_WORKSPACE.
|
Defaults to $GITHUB_WORKSPACE.
|
||||||
|
add-paths:
|
||||||
|
description: >
|
||||||
|
A comma or newline-separated list of file paths to commit.
|
||||||
|
Paths should follow git's pathspec syntax.
|
||||||
|
Defaults to adding all new and modified files.
|
||||||
commit-message:
|
commit-message:
|
||||||
description: 'The message to use when committing changes.'
|
description: 'The message to use when committing changes.'
|
||||||
default: '[create-pull-request] automated change'
|
default: '[create-pull-request] automated change'
|
||||||
@ -49,6 +54,8 @@ inputs:
|
|||||||
body:
|
body:
|
||||||
description: 'The body of the pull request.'
|
description: 'The body of the pull request.'
|
||||||
default: 'Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action'
|
default: 'Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action'
|
||||||
|
body-path:
|
||||||
|
description: 'The path to a file containing the pull request body. Takes precedence over `body`.'
|
||||||
labels:
|
labels:
|
||||||
description: 'A comma or newline separated list of labels.'
|
description: 'A comma or newline separated list of labels.'
|
||||||
assignees:
|
assignees:
|
||||||
@ -62,13 +69,19 @@ inputs:
|
|||||||
milestone:
|
milestone:
|
||||||
description: 'The number of the milestone to associate the pull request with.'
|
description: 'The number of the milestone to associate the pull request with.'
|
||||||
draft:
|
draft:
|
||||||
description: 'Create a draft pull request'
|
description: 'Create a draft pull request. It is not possible to change draft status after creation except through the web interface'
|
||||||
default: false
|
default: false
|
||||||
outputs:
|
outputs:
|
||||||
pull-request-number:
|
pull-request-number:
|
||||||
description: 'The pull request number'
|
description: 'The pull request number'
|
||||||
|
pull-request-url:
|
||||||
|
description: 'The URL of the pull request.'
|
||||||
|
pull-request-operation:
|
||||||
|
description: 'The pull request operation performed by the action, `created`, `updated` or `closed`.'
|
||||||
|
pull-request-head-sha:
|
||||||
|
description: 'The commit SHA of the pull request branch.'
|
||||||
runs:
|
runs:
|
||||||
using: 'node12'
|
using: 'node16'
|
||||||
main: 'dist/index.js'
|
main: 'dist/index.js'
|
||||||
branding:
|
branding:
|
||||||
icon: 'git-pull-request'
|
icon: 'git-pull-request'
|
||||||
|
9696
dist/index.js
vendored
9696
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
@ -29,37 +29,37 @@
|
|||||||
orientation: "vertical-reverse"
|
orientation: "vertical-reverse"
|
||||||
});
|
});
|
||||||
|
|
||||||
const master = gitgraph.branch("master");
|
const main = gitgraph.branch("main");
|
||||||
master.commit("Last commit on base");
|
main.commit("Last commit on base");
|
||||||
const localMaster = gitgraph.branch("<#1> master (local)");
|
const localMain = gitgraph.branch("<#1> main (local)");
|
||||||
localMaster.commit({
|
localMain.commit({
|
||||||
subject: "<uncommitted changes>",
|
subject: "<uncommitted changes>",
|
||||||
body: "Changes to the local base during the workflow",
|
body: "Changes to the local base during the workflow",
|
||||||
})
|
})
|
||||||
const remotePatch = gitgraph.branch("create-pull-request/patch");
|
const remotePatch = gitgraph.branch("create-pull-request/patch");
|
||||||
remotePatch.merge({
|
remotePatch.merge({
|
||||||
branch: localMaster,
|
branch: localMain,
|
||||||
commitOptions: {
|
commitOptions: {
|
||||||
subject: "[create-pull-request] automated change",
|
subject: "[create-pull-request] automated change",
|
||||||
body: "Changes pushed to create the remote branch",
|
body: "Changes pushed to create the remote branch",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
master.commit("New commit on base");
|
main.commit("New commit on base");
|
||||||
|
|
||||||
const localMaster2 = gitgraph.branch("<#2> master (local)");
|
const localMain2 = gitgraph.branch("<#2> main (local)");
|
||||||
localMaster2.commit({
|
localMain2.commit({
|
||||||
subject: "<uncommitted changes>",
|
subject: "<uncommitted changes>",
|
||||||
body: "Changes to the updated local base during the workflow",
|
body: "Changes to the updated local base during the workflow",
|
||||||
})
|
})
|
||||||
remotePatch.merge({
|
remotePatch.merge({
|
||||||
branch: localMaster2,
|
branch: localMain2,
|
||||||
commitOptions: {
|
commitOptions: {
|
||||||
subject: "[create-pull-request] automated change",
|
subject: "[create-pull-request] automated change",
|
||||||
body: "Changes force pushed to update the remote branch",
|
body: "Changes force pushed to update the remote branch",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
master.merge(remotePatch);
|
main.merge(remotePatch);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 108 KiB |
@ -16,6 +16,7 @@ This document covers terminology, how the action works, general usage guidelines
|
|||||||
- [Push using SSH (deploy keys)](#push-using-ssh-deploy-keys)
|
- [Push using SSH (deploy keys)](#push-using-ssh-deploy-keys)
|
||||||
- [Push pull request branches to a fork](#push-pull-request-branches-to-a-fork)
|
- [Push pull request branches to a fork](#push-pull-request-branches-to-a-fork)
|
||||||
- [Authenticating with GitHub App generated tokens](#authenticating-with-github-app-generated-tokens)
|
- [Authenticating with GitHub App generated tokens](#authenticating-with-github-app-generated-tokens)
|
||||||
|
- [GPG commit signature verification](#gpg-commit-signature-verification)
|
||||||
- [Running in a container or on self-hosted runners](#running-in-a-container-or-on-self-hosted-runners)
|
- [Running in a container or on self-hosted runners](#running-in-a-container-or-on-self-hosted-runners)
|
||||||
|
|
||||||
## Terminology
|
## Terminology
|
||||||
@ -35,7 +36,7 @@ For each [event type](https://docs.github.com/en/actions/reference/events-that-t
|
|||||||
The default can be overridden by specifying a `ref` on checkout.
|
The default can be overridden by specifying a `ref` on checkout.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: develop
|
ref: develop
|
||||||
```
|
```
|
||||||
@ -72,7 +73,7 @@ jobs:
|
|||||||
example:
|
example:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
```
|
```
|
||||||
|
|
||||||
There may be use cases where it makes sense to execute the workflow on a branch that is not the base of the pull request. In these cases, the base branch can be specified with the `base` action input. The action will attempt to rebase changes made during the workflow on to the actual base.
|
There may be use cases where it makes sense to execute the workflow on a branch that is not the base of the pull request. In these cases, the base branch can be specified with the `base` action input. The action will attempt to rebase changes made during the workflow on to the actual base.
|
||||||
@ -87,7 +88,7 @@ In these cases, you *must supply* the `base` input so the action can rebase chan
|
|||||||
Workflows triggered by [`pull_request`](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request) events will by default check out a merge commit. Set the `base` input as follows to base the new pull request on the current pull request's branch.
|
Workflows triggered by [`pull_request`](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request) events will by default check out a merge commit. Set the `base` input as follows to base the new pull request on the current pull request's branch.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- uses: peter-evans/create-pull-request@v3
|
- uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
base: ${{ github.head_ref }}
|
base: ${{ github.head_ref }}
|
||||||
```
|
```
|
||||||
@ -95,9 +96,9 @@ Workflows triggered by [`pull_request`](https://docs.github.com/en/actions/refer
|
|||||||
Workflows triggered by [`release`](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#release) events will by default check out a tag. For most use cases, you will need to set the `base` input to the branch name of the tagged commit.
|
Workflows triggered by [`release`](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#release) events will by default check out a tag. For most use cases, you will need to set the `base` input to the branch name of the tagged commit.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- uses: peter-evans/create-pull-request@v3
|
- uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
base: master
|
base: main
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restrictions on repository forks
|
### Restrictions on repository forks
|
||||||
@ -129,21 +130,23 @@ jobs:
|
|||||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For further reading regarding the security of pull requests, see this GitHub blog post titled [Keeping your GitHub Actions and workflows secure: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
|
||||||
|
|
||||||
### Triggering further workflow runs
|
### Triggering further workflow runs
|
||||||
|
|
||||||
Pull requests created by the action using the default `GITHUB_TOKEN` cannot trigger other workflows. If you have `on: pull_request` or `on: push` workflows acting as checks on pull requests, they will not run.
|
Pull requests created by the action using the default `GITHUB_TOKEN` cannot trigger other workflows. If you have `on: pull_request` or `on: push` workflows acting as checks on pull requests, they will not run.
|
||||||
|
|
||||||
> When you use the repository's GITHUB_TOKEN to perform tasks on behalf of the GitHub Actions app, events triggered by the GITHUB_TOKEN will not create a new workflow run.
|
> When you use the repository's `GITHUB_TOKEN` to perform tasks, events triggered by the `GITHUB_TOKEN` will not create a new workflow run. This prevents you from accidentally creating recursive workflow runs. For example, if a workflow run pushes code using the repository's `GITHUB_TOKEN`, a new workflow will not run even when the repository contains a workflow configured to run when `push` events occur.
|
||||||
|
|
||||||
[GitHub Actions: Events that trigger workflows](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token)
|
[GitHub Actions: Triggering a workflow from a workflow](https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow)
|
||||||
|
|
||||||
#### Workarounds to trigger further workflow runs
|
#### Workarounds to trigger further workflow runs
|
||||||
|
|
||||||
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. To prevent merging of pull requests without checks erroneously, use [branch protection rules](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests).
|
||||||
|
|
||||||
- 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/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow). 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.
|
||||||
|
|
||||||
@ -161,7 +164,7 @@ Alternatively, use the action directly and reference the commit hash for the ver
|
|||||||
- uses: thirdparty/foo-action@172ec762f2ac8e050062398456fccd30444f8f30
|
- uses: thirdparty/foo-action@172ec762f2ac8e050062398456fccd30444f8f30
|
||||||
```
|
```
|
||||||
|
|
||||||
This action uses [ncc](https://github.com/vercel/ncc) to compile the Node.js code and dependencies into a single JavaScript file under the [dist](https://github.com/peter-evans/create-pull-request/tree/master/dist) directory.
|
This action uses [ncc](https://github.com/vercel/ncc) to compile the Node.js code and dependencies into a single JavaScript file under the [dist](https://github.com/peter-evans/create-pull-request/tree/main/dist) directory.
|
||||||
|
|
||||||
## Advanced usage
|
## Advanced usage
|
||||||
|
|
||||||
@ -170,14 +173,14 @@ This action uses [ncc](https://github.com/vercel/ncc) to compile the Node.js cod
|
|||||||
Checking out a branch from a different repository from where the workflow is executing will make *that repository* the target for the created pull request. In this case, a `repo` scoped [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) is required.
|
Checking out a branch from a different repository from where the workflow is executing will make *that repository* the target for the created pull request. In this case, a `repo` scoped [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) is required.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
repository: owner/repo
|
repository: owner/repo
|
||||||
|
|
||||||
# Make changes to pull request here
|
# Make changes to pull request here
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@v3
|
- uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
```
|
```
|
||||||
@ -197,22 +200,23 @@ How to use SSH (deploy keys) with create-pull-request action:
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
|
||||||
# Make changes to pull request here
|
# 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@v5
|
||||||
```
|
```
|
||||||
|
|
||||||
### Push pull request branches to a fork
|
### Push pull request branches to a fork
|
||||||
|
|
||||||
Instead of pushing pull request branches to the repository you want to update, you can push them to a fork of that repository.
|
Instead of pushing pull request branches to the repository you want to update, you can push them to a fork of that repository.
|
||||||
This allows you to employ the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) by using a dedicated user acting as a [machine account](https://docs.github.com/en/github/site-policy/github-terms-of-service#3-account-requirements).
|
This allows you to employ the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege) by using a dedicated user acting as a [machine account](https://docs.github.com/en/github/site-policy/github-terms-of-service#3-account-requirements).
|
||||||
This user has no access to the main repository.
|
This user only has `read` access to the main repository.
|
||||||
It will use their own fork to push code and create the pull request.
|
It will use their own fork to push code and create the pull request.
|
||||||
|
Note that if you choose to use this method (not give the machine account `write` access to the repository) the following inputs cannot be used: `labels`, `assignees`, `reviewers`, `team-reviewers` and `milestone`.
|
||||||
|
|
||||||
1. Create a new GitHub user and login.
|
1. Create a new GitHub user and login.
|
||||||
2. Fork the repository that you will be creating pull requests in.
|
2. Fork the repository that you will be creating pull requests in.
|
||||||
@ -222,16 +226,18 @@ It will use their own fork to push code and create the pull request.
|
|||||||
6. As shown in the following example workflow, set the `push-to-fork` input to the full repository name of the fork.
|
6. As shown in the following example workflow, set the `push-to-fork` input to the full repository name of the fork.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
# Make changes to pull request here
|
# Make changes to pull request here
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@v3
|
- uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.MACHINE_USER_PAT }}
|
token: ${{ secrets.MACHINE_USER_PAT }}
|
||||||
push-to-fork: machine-user/fork-of-repository
|
push-to-fork: machine-user/fork-of-repository
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: You can also combine `push-to-fork` with [creating pull requests in a remote repository](#creating-pull-requests-in-a-remote-repository).
|
||||||
|
|
||||||
### Authenticating with GitHub App generated tokens
|
### Authenticating with GitHub App generated tokens
|
||||||
|
|
||||||
A GitHub App can be created for the sole purpose of generating tokens for use with GitHub actions.
|
A GitHub App can be created for the sole purpose of generating tokens for use with GitHub actions.
|
||||||
@ -245,6 +251,8 @@ GitHub App generated tokens are more secure than using a PAT because GitHub App
|
|||||||
- Uncheck `Active` under `Webhook`. You do not need to enter a `Webhook URL`.
|
- Uncheck `Active` under `Webhook`. You do not need to enter a `Webhook URL`.
|
||||||
- Under `Repository permissions: Contents` select `Access: Read & write`.
|
- Under `Repository permissions: Contents` select `Access: Read & write`.
|
||||||
- Under `Repository permissions: Pull requests` select `Access: Read & write`.
|
- Under `Repository permissions: Pull requests` select `Access: Read & write`.
|
||||||
|
- Under `Organization permissions: Members` select `Access: Read-only`.
|
||||||
|
- **NOTE**: Only needed if you would like add teams as reviewers to PRs.
|
||||||
|
|
||||||
2. Create a Private key from the App settings page and store it securely.
|
2. Create a Private key from the App settings page and store it securely.
|
||||||
|
|
||||||
@ -256,7 +264,7 @@ GitHub App generated tokens are more secure than using a PAT because GitHub App
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: tibdex/github-app-token@v1
|
- uses: tibdex/github-app-token@v1
|
||||||
id: generate-token
|
id: generate-token
|
||||||
@ -267,11 +275,53 @@ GitHub App generated tokens are more secure than using a PAT because GitHub App
|
|||||||
# Make changes to pull request here
|
# 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@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### GPG commit signature verification
|
||||||
|
|
||||||
|
The action can use GPG to sign commits with a GPG key that you generate yourself.
|
||||||
|
|
||||||
|
1. Follow GitHub's guide to [generate a new GPG key](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key).
|
||||||
|
|
||||||
|
2. [Add the public key](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-gpg-key-to-your-github-account) to the user account associated with the [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) that you will use with the action.
|
||||||
|
|
||||||
|
3. Copy the private key to your clipboard, replacing `email@example.com` with the email address of your GPG key.
|
||||||
|
```
|
||||||
|
# macOS
|
||||||
|
gpg --armor --export-secret-key email@example.com | pbcopy
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Paste the private key into a repository secret where the workflow will run. e.g. `GPG_PRIVATE_KEY`
|
||||||
|
|
||||||
|
5. Create another repository secret for the key's passphrase, if applicable. e.g. `GPG_PASSPHRASE`
|
||||||
|
|
||||||
|
6. The following example workflow shows how to use [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) to import your GPG key and allow the action to sign commits.
|
||||||
|
|
||||||
|
Note that the `committer` email address *MUST* match the email address used to create your GPG key.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: crazy-max/ghaction-import-gpg@v3
|
||||||
|
with:
|
||||||
|
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
|
git-user-signingkey: true
|
||||||
|
git-commit-gpgsign: true
|
||||||
|
|
||||||
|
# Make changes to pull request here
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.PAT }}
|
||||||
|
committer: example <email@example.com>
|
||||||
|
```
|
||||||
|
|
||||||
### Running in a container or on self-hosted runners
|
### Running in a container or on self-hosted runners
|
||||||
|
|
||||||
This action can be run inside a container, or on [self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners), by installing the necessary dependencies.
|
This action can be run inside a container, or on [self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners), by installing the necessary dependencies.
|
||||||
@ -291,12 +341,12 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: apk --no-cache add git
|
run: apk --no-cache add git
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
# Make changes to pull request here
|
# 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@v5
|
||||||
```
|
```
|
||||||
|
|
||||||
**Ubuntu container example:**
|
**Ubuntu container example:**
|
||||||
@ -314,10 +364,10 @@ jobs:
|
|||||||
add-apt-repository -y ppa:git-core/ppa
|
add-apt-repository -y ppa:git-core/ppa
|
||||||
apt-get install -y git
|
apt-get install -y git
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
# Make changes to pull request here
|
# 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@v5
|
||||||
```
|
```
|
||||||
|
148
docs/examples.md
148
docs/examples.md
@ -19,8 +19,9 @@
|
|||||||
- [autopep8](#autopep8)
|
- [autopep8](#autopep8)
|
||||||
- [Misc workflow tips](#misc-workflow-tips)
|
- [Misc workflow tips](#misc-workflow-tips)
|
||||||
- [Filtering push events](#filtering-push-events)
|
- [Filtering push events](#filtering-push-events)
|
||||||
|
- [Bypassing git hooks](#bypassing-git-hooks)
|
||||||
- [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)
|
- [Using a markdown template](#using-a-markdown-template)
|
||||||
- [Debugging GitHub Actions](#debugging-github-actions)
|
- [Debugging GitHub Actions](#debugging-github-actions)
|
||||||
|
|
||||||
|
|
||||||
@ -37,19 +38,19 @@ name: Update AUTHORS
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
updateAuthors:
|
updateAuthors:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Update AUTHORS
|
- name: Update AUTHORS
|
||||||
run: |
|
run: |
|
||||||
git log --format='%aN <%aE>%n%cN <%cE>' | sort -u > AUTHORS
|
git log --format='%aN <%aE>%n%cN <%cE>' | sort -u > AUTHORS
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
commit-message: update authors
|
commit-message: update authors
|
||||||
title: Update AUTHORS
|
title: Update AUTHORS
|
||||||
@ -61,27 +62,27 @@ jobs:
|
|||||||
|
|
||||||
This is a use case where a branch should be kept up to date with another by opening a pull request to update it. The pull request should then be updated with new changes until it is merged or closed.
|
This is a use case where a branch should be kept up to date with another by opening a pull request to update it. The pull request should then be updated with new changes until it is merged or closed.
|
||||||
|
|
||||||
In this example scenario, a branch called `production` should be updated via pull request to keep it in sync with `master`. Merging the pull request is effectively promoting those changes to production.
|
In this example scenario, a branch called `production` should be updated via pull request to keep it in sync with `main`. Merging the pull request is effectively promoting those changes to production.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
name: Create production promotion pull request
|
name: Create production promotion pull request
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
productionPromotion:
|
productionPromotion:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: production
|
ref: production
|
||||||
- name: Reset promotion branch
|
- name: Reset promotion branch
|
||||||
run: |
|
run: |
|
||||||
git fetch origin master:master
|
git fetch origin main:main
|
||||||
git reset --hard master
|
git reset --hard main
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
branch: production-promotion
|
branch: production-promotion
|
||||||
```
|
```
|
||||||
@ -95,7 +96,7 @@ This pattern will work well for updating any kind of static content based on the
|
|||||||
Raises a pull request to update the `CHANGELOG.md` file based on the tagged commit of the release.
|
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.
|
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`.
|
This workflow assumes the tagged release was made on a default branch called `main`.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
name: Update Changelog
|
name: Update Changelog
|
||||||
@ -106,7 +107,7 @@ jobs:
|
|||||||
updateChangelog:
|
updateChangelog:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Update Changelog
|
- name: Update Changelog
|
||||||
@ -116,13 +117,13 @@ jobs:
|
|||||||
./git-chglog -o CHANGELOG.md
|
./git-chglog -o CHANGELOG.md
|
||||||
rm git-chglog
|
rm git-chglog
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
commit-message: update changelog
|
commit-message: update changelog
|
||||||
title: Update Changelog
|
title: Update Changelog
|
||||||
body: Update changelog to reflect release changes
|
body: Update changelog to reflect release changes
|
||||||
branch: update-changelog
|
branch: update-changelog
|
||||||
base: master
|
base: main
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use case: Create a pull request to update X periodically
|
## Use case: Create a pull request to update X periodically
|
||||||
@ -144,16 +145,16 @@ jobs:
|
|||||||
update-dep:
|
update-dep:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '12.x'
|
node-version: '16.x'
|
||||||
- name: Update dependencies
|
- name: Update dependencies
|
||||||
run: |
|
run: |
|
||||||
npx -p npm-check-updates ncu -u
|
npx -p npm-check-updates ncu -u
|
||||||
npm install
|
npm install
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
commit-message: Update dependencies
|
commit-message: Update dependencies
|
||||||
@ -173,17 +174,17 @@ The above workflow works best in combination with a build workflow triggered on
|
|||||||
name: CI
|
name: CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [main]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run test
|
- run: npm run test
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
@ -204,16 +205,17 @@ jobs:
|
|||||||
update-dep:
|
update-dep:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
java-version: 1.8
|
java-version: 1.8
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
- name: Perform dependency resolution and write new lockfiles
|
- name: Perform dependency resolution and write new lockfiles
|
||||||
run: ./gradlew dependencies --write-locks
|
run: ./gradlew dependencies --write-locks
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
commit-message: Update dependencies
|
commit-message: Update dependencies
|
||||||
@ -241,14 +243,14 @@ jobs:
|
|||||||
update-dep:
|
update-dep:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Update dependencies
|
- name: Update dependencies
|
||||||
run: |
|
run: |
|
||||||
cargo install cargo-edit
|
cargo install cargo-edit
|
||||||
cargo update
|
cargo update
|
||||||
cargo upgrade --to-lockfile
|
cargo upgrade --to-lockfile
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
commit-message: Update dependencies
|
commit-message: Update dependencies
|
||||||
@ -276,12 +278,14 @@ jobs:
|
|||||||
updateSwagger:
|
updateSwagger:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Get Latest Swagger UI Release
|
- name: Get Latest Swagger UI Release
|
||||||
id: swagger-ui
|
id: swagger-ui
|
||||||
run: |
|
run: |
|
||||||
echo ::set-output name=release_tag::$(curl -sL https://api.github.com/repos/swagger-api/swagger-ui/releases/latest | jq -r ".tag_name")
|
release_tag=$(curl -sL https://api.github.com/repos/swagger-api/swagger-ui/releases/latest | jq -r ".tag_name")
|
||||||
echo ::set-output name=current_tag::$(<swagger-ui.version)
|
echo "release_tag=$release_tag" >> $GITHUB_OUTPUT
|
||||||
|
current_tag=$(<swagger-ui.version)
|
||||||
|
echo "current_tag=$current_tag" >> $GITHUB_OUTPUT
|
||||||
- name: Update Swagger UI
|
- name: Update Swagger UI
|
||||||
if: steps.swagger-ui.outputs.current_tag != steps.swagger-ui.outputs.release_tag
|
if: steps.swagger-ui.outputs.current_tag != steps.swagger-ui.outputs.release_tag
|
||||||
env:
|
env:
|
||||||
@ -304,7 +308,7 @@ jobs:
|
|||||||
# Update current release
|
# Update current release
|
||||||
echo ${{ steps.swagger-ui.outputs.release_tag }} > swagger-ui.version
|
echo ${{ steps.swagger-ui.outputs.release_tag }} > swagger-ui.version
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
commit-message: Update swagger-ui to ${{ steps.swagger-ui.outputs.release_tag }}
|
commit-message: Update swagger-ui to ${{ steps.swagger-ui.outputs.release_tag }}
|
||||||
title: Update SwaggerUI to ${{ steps.swagger-ui.outputs.release_tag }}
|
title: Update SwaggerUI to ${{ steps.swagger-ui.outputs.release_tag }}
|
||||||
@ -324,7 +328,7 @@ jobs:
|
|||||||
This example is designed to be run in a seperate repository from the fork repository itself.
|
This example is designed to be run in a seperate repository from the fork repository itself.
|
||||||
The aim of this is to prevent committing anything to the fork's default branch would cause it to differ from the upstream.
|
The aim of this is to prevent committing anything to the fork's default branch would cause it to differ from the upstream.
|
||||||
|
|
||||||
In the following example workflow, `owner/repo` is the upstream repository and `fork-owner/repo` is the fork. It assumes the default branch of the upstream repository is called `master`.
|
In the following example workflow, `owner/repo` is the upstream repository and `fork-owner/repo` is the fork. It assumes the default branch of the upstream repository is called `main`.
|
||||||
|
|
||||||
The [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) should have `repo` scope. Additionally, if the upstream makes changes to the `.github/workflows` directory, the action will be unable to push the changes to a branch and throw the error "_(refusing to allow a GitHub App to create or update workflow `.github/workflows/xxx.yml` without `workflows` permission)_". To allow these changes to be pushed to the fork, add the `workflow` scope to the PAT. Of course, allowing this comes with the risk that the workflow changes from the upstream could run and do something unexpected. Disabling GitHub Actions in the fork is highly recommended to prevent this.
|
The [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) should have `repo` scope. Additionally, if the upstream makes changes to the `.github/workflows` directory, the action will be unable to push the changes to a branch and throw the error "_(refusing to allow a GitHub App to create or update workflow `.github/workflows/xxx.yml` without `workflows` permission)_". To allow these changes to be pushed to the fork, add the `workflow` scope to the PAT. Of course, allowing this comes with the risk that the workflow changes from the upstream could run and do something unexpected. Disabling GitHub Actions in the fork is highly recommended to prevent this.
|
||||||
|
|
||||||
@ -339,16 +343,16 @@ jobs:
|
|||||||
updateFork:
|
updateFork:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: fork-owner/repo
|
repository: fork-owner/repo
|
||||||
- name: Reset the default branch with upstream changes
|
- name: Reset the default branch with upstream changes
|
||||||
run: |
|
run: |
|
||||||
git remote add upstream https://github.com/owner/repo.git
|
git remote add upstream https://github.com/owner/repo.git
|
||||||
git fetch upstream master:upstream-master
|
git fetch upstream main:upstream-main
|
||||||
git reset --hard upstream-master
|
git reset --hard upstream-main
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
branch: upstream-changes
|
branch: upstream-changes
|
||||||
@ -367,7 +371,7 @@ jobs:
|
|||||||
format:
|
format:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Download website
|
- name: Download website
|
||||||
run: |
|
run: |
|
||||||
wget \
|
wget \
|
||||||
@ -381,7 +385,7 @@ jobs:
|
|||||||
--domains quotes.toscrape.com \
|
--domains quotes.toscrape.com \
|
||||||
http://quotes.toscrape.com/
|
http://quotes.toscrape.com/
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
commit-message: update local website copy
|
commit-message: update local website copy
|
||||||
title: Automated Updates to Local Website Copy
|
title: Automated Updates to Local Website Copy
|
||||||
@ -426,7 +430,7 @@ An `on: repository_dispatch` workflow can be triggered from another workflow wit
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
- name: Repository Dispatch
|
- name: Repository Dispatch
|
||||||
uses: peter-evans/repository-dispatch@v1
|
uses: peter-evans/repository-dispatch@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.REPO_ACCESS_TOKEN }}
|
token: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
repository: username/my-repo
|
repository: username/my-repo
|
||||||
@ -463,7 +467,7 @@ jobs:
|
|||||||
if: startsWith(github.head_ref, 'autopep8-patches') == false && github.event.pull_request.head.repo.full_name == github.repository
|
if: startsWith(github.head_ref, 'autopep8-patches') == false && github.event.pull_request.head.repo.full_name == github.repository
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.head_ref }}
|
ref: ${{ github.head_ref }}
|
||||||
- name: autopep8
|
- name: autopep8
|
||||||
@ -473,10 +477,12 @@ jobs:
|
|||||||
args: --exit-code --recursive --in-place --aggressive --aggressive .
|
args: --exit-code --recursive --in-place --aggressive --aggressive .
|
||||||
- name: Set autopep8 branch name
|
- name: Set autopep8 branch name
|
||||||
id: vars
|
id: vars
|
||||||
run: echo ::set-output name=branch-name::"autopep8-patches/${{ github.head_ref }}"
|
run: |
|
||||||
|
branch-name="autopep8-patches/${{ github.head_ref }}"
|
||||||
|
echo "branch-name=$branch-name" >> $GITHUB_OUTPUT
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: steps.autopep8.outputs.exit-code == 2
|
if: steps.autopep8.outputs.exit-code == 2
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
commit-message: autopep8 action fixes
|
commit-message: autopep8 action fixes
|
||||||
title: Fixes by autopep8 action
|
title: Fixes by autopep8 action
|
||||||
@ -510,54 +516,72 @@ jobs:
|
|||||||
if: startsWith(github.ref, 'refs/heads/')
|
if: startsWith(github.ref, 'refs/heads/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
...
|
...
|
||||||
|
|
||||||
someOtherJob:
|
someOtherJob:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Bypassing git hooks
|
||||||
|
|
||||||
|
If you have git hooks that prevent the action from working correctly you can remove them before running the action.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# Remove git hooks
|
||||||
|
- run: rm -rf .git/hooks
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
```
|
||||||
|
|
||||||
### Dynamic configuration using variables
|
### Dynamic configuration using variables
|
||||||
|
|
||||||
The following examples show how configuration for the action can be dynamically defined in a previous workflow step.
|
The following examples show how configuration for the action can be dynamically defined in a previous workflow step.
|
||||||
|
Note that the step where output variables are defined must have an id.
|
||||||
The recommended method is to use [`set-output`](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-output-parameter). Note that the step where output variables are defined must have an id.
|
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- name: Set output variables
|
- name: Set output variables
|
||||||
id: vars
|
id: vars
|
||||||
run: |
|
run: |
|
||||||
echo ::set-output name=pr_title::"[Test] Add report file $(date +%d-%m-%Y)"
|
pr_title="[Test] Add report file $(date +%d-%m-%Y)"
|
||||||
echo ::set-output name=pr_body::"This PR was auto-generated on $(date +%d-%m-%Y) \
|
pr_body="This PR was auto-generated on $(date +%d-%m-%Y) \
|
||||||
by [create-pull-request](https://github.com/peter-evans/create-pull-request)."
|
by [create-pull-request](https://github.com/peter-evans/create-pull-request)."
|
||||||
|
echo "pr_title=$pr_title" >> $GITHUB_OUTPUT
|
||||||
|
echo "pr_body=$pr_body" >> $GITHUB_OUTPUT
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
title: ${{ steps.vars.outputs.pr_title }}
|
title: ${{ steps.vars.outputs.pr_title }}
|
||||||
body: ${{ steps.vars.outputs.pr_body }}
|
body: ${{ steps.vars.outputs.pr_body }}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setting the pull request body from a file
|
### Using a markdown template
|
||||||
|
|
||||||
This example shows how file content can be read into a variable and passed to the action.
|
In this example, a markdown template file is added to the repository at `.github/pull-request-template.md` with the following content.
|
||||||
The content must be [escaped to preserve newlines](https://github.community/t/set-output-truncates-multiline-strings/16852/3).
|
```
|
||||||
|
This is a test pull request template
|
||||||
|
Render template variables such as {{ .foo }} and {{ .bar }}.
|
||||||
|
```
|
||||||
|
|
||||||
|
The template is rendered using the [render-template](https://github.com/chuhlomin/render-template) action and the result is used to create the pull request.
|
||||||
```yml
|
```yml
|
||||||
- id: get-pr-body
|
- name: Render template
|
||||||
run: |
|
id: template
|
||||||
body=$(cat pr-body.txt)
|
uses: chuhlomin/render-template@v1.4
|
||||||
body="${body//'%'/'%25'}"
|
with:
|
||||||
body="${body//$'\n'/'%0A'}"
|
template: .github/pull-request-template.md
|
||||||
body="${body//$'\r'/'%0D'}"
|
vars: |
|
||||||
echo ::set-output name=body::$body
|
foo: this
|
||||||
|
bar: that
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v5
|
||||||
with:
|
with:
|
||||||
body: ${{ steps.get-pr-body.outputs.body }}
|
body: ${{ steps.template.outputs.result }}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debugging GitHub Actions
|
### Debugging GitHub Actions
|
||||||
|
@ -1,6 +1,37 @@
|
|||||||
|
## Updating from `v4` to `v5`
|
||||||
|
|
||||||
|
### Behaviour changes
|
||||||
|
|
||||||
|
- The action will no longer leave the local repository checked out on the pull request `branch`. Instead, it will leave the repository checked out on the branch or commit that it was when the action started.
|
||||||
|
- When using `add-paths`, uncommitted changes will no longer be destroyed. They will be stashed and restored at the end of the action run.
|
||||||
|
|
||||||
|
### What's new
|
||||||
|
|
||||||
|
- Adds input `body-path`, the path to a file containing the pull request body.
|
||||||
|
- At the end of the action run the local repository is now checked out on the branch or commit that it was when the action started.
|
||||||
|
- Any uncommitted tracked or untracked changes are now stashed and restored at the end of the action run. Currently, this can only occur when using the `add-paths` input, which allows for changes to not be committed. Previously, any uncommitted changes would be destroyed.
|
||||||
|
- The proxy implementation has been revised but is not expected to have any change in behaviour. It continues to support the standard environment variables `http_proxy`, `https_proxy` and `no_proxy`.
|
||||||
|
- Now sets the git `safe.directory` configuration for the local repository path. The configuration is removed when the action completes. Fixes issue https://github.com/peter-evans/create-pull-request/issues/1170.
|
||||||
|
- Now determines the git directory path using the `git rev-parse --git-dir` command. This allows users with custom repository configurations to use the action.
|
||||||
|
- Improved handling of the `team-reviewers` input and associated errors.
|
||||||
|
|
||||||
|
## Updating from `v3` to `v4`
|
||||||
|
|
||||||
|
### Behaviour changes
|
||||||
|
|
||||||
|
- The `add-paths` input no longer accepts `-A` as a valid value. When committing all new and modified files the `add-paths` input should be omitted.
|
||||||
|
|
||||||
|
- If using self-hosted runners or GitHub Enterprise Server, there are minimum requirements for `v4` to run. See "What's new" below for details.
|
||||||
|
|
||||||
|
### What's new
|
||||||
|
|
||||||
|
- Updated runtime to Node.js 16
|
||||||
|
- The action now requires a minimum version of v2.285.0 for the [Actions Runner](https://github.com/actions/runner/releases/tag/v2.285.0).
|
||||||
|
- If using GitHub Enterprise Server, the action requires [GHES 3.4](https://docs.github.com/en/enterprise-server@3.4/admin/release-notes) or later.
|
||||||
|
|
||||||
## Updating from `v2` to `v3`
|
## Updating from `v2` to `v3`
|
||||||
|
|
||||||
### Breaking changes
|
### Behaviour changes
|
||||||
|
|
||||||
- The `author` input now defaults to the user who triggered the workflow run. This default is set via [action.yml](../action.yml) as `${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>`, where `github.actor` is the GitHub user account associated with the run. For example, `peter-evans <peter-evans@users.noreply.github.com>`.
|
- The `author` input now defaults to the user who triggered the workflow run. This default is set via [action.yml](../action.yml) as `${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>`, where `github.actor` is the GitHub user account associated with the run. For example, `peter-evans <peter-evans@users.noreply.github.com>`.
|
||||||
|
|
||||||
@ -31,7 +62,7 @@
|
|||||||
push-to-fork: machine-user/fork-of-repository
|
push-to-fork: machine-user/fork-of-repository
|
||||||
```
|
```
|
||||||
|
|
||||||
### New features
|
### What's new
|
||||||
|
|
||||||
- The action has been converted to Typescript giving it a significant performance improvement.
|
- The action has been converted to Typescript giving it a significant performance improvement.
|
||||||
|
|
||||||
@ -48,7 +79,7 @@
|
|||||||
|
|
||||||
## Updating from `v1` to `v2`
|
## Updating from `v1` to `v2`
|
||||||
|
|
||||||
### Breaking changes
|
### Behaviour changes
|
||||||
|
|
||||||
- `v2` now expects repositories to be checked out with `actions/checkout@v2`
|
- `v2` now expects repositories to be checked out with `actions/checkout@v2`
|
||||||
|
|
||||||
@ -66,8 +97,8 @@
|
|||||||
|
|
||||||
If neither `author` or `committer` are set the action will default to making commits as the GitHub Actions bot user.
|
If neither `author` or `committer` are set the action will default to making commits as the GitHub Actions bot user.
|
||||||
|
|
||||||
### New features
|
### What's new
|
||||||
|
|
||||||
- Unpushed commits made during the workflow before the action runs will now be considered as changes to be raised in the pull request. See [Controlling commits](https://github.com/peter-evans/create-pull-request#controlling-commits) for details.
|
- Unpushed commits made during the workflow before the action runs will now be considered as changes to be raised in the pull request. See [Create your own commits](https://github.com/peter-evans/create-pull-request#create-your-own-commits) for details.
|
||||||
- New commits made to the pull request base will now be taken into account when pull requests are updated.
|
- New commits made to the pull request base will now be taken into account when pull requests are updated.
|
||||||
- If an updated pull request no longer differs from its base it will automatically be closed and the pull request branch deleted.
|
- If an updated pull request no longer differs from its base it will automatically be closed and the pull request branch deleted.
|
||||||
|
11513
package-lock.json
generated
11513
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
45
package.json
45
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "create-pull-request",
|
"name": "create-pull-request",
|
||||||
"version": "3.0.0",
|
"version": "5.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Creates a pull request for changes to your repository in the actions workspace",
|
"description": "Creates a pull request for changes to your repository in the actions workspace",
|
||||||
"main": "lib/main.js",
|
"main": "lib/main.js",
|
||||||
@ -29,26 +29,31 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/peter-evans/create-pull-request",
|
"homepage": "https://github.com/peter-evans/create-pull-request",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "1.2.6",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/exec": "1.0.4",
|
"@actions/exec": "^1.1.1",
|
||||||
"@octokit/core": "3.1.2",
|
"@octokit/core": "^4.2.0",
|
||||||
"@octokit/plugin-paginate-rest": "2.4.0",
|
"@octokit/plugin-paginate-rest": "^5.0.1",
|
||||||
"@octokit/plugin-rest-endpoint-methods": "4.2.0",
|
"@octokit/plugin-rest-endpoint-methods": "^6.8.1",
|
||||||
"uuid": "8.3.0"
|
"https-proxy-agent": "^5.0.1",
|
||||||
|
"proxy-from-env": "^1.1.0",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "26.0.14",
|
"@types/jest": "^29.5.1",
|
||||||
"@types/node": "14.10.3",
|
"@types/node": "^18.16.0",
|
||||||
"@typescript-eslint/parser": "4.1.1",
|
"@typescript-eslint/parser": "^5.59.1",
|
||||||
"@vercel/ncc": "0.24.1",
|
"@vercel/ncc": "^0.36.1",
|
||||||
"eslint": "7.9.0",
|
"eslint": "^8.39.0",
|
||||||
"eslint-plugin-github": "4.1.1",
|
"eslint-import-resolver-typescript": "^3.5.5",
|
||||||
"eslint-plugin-jest": "24.0.1",
|
"eslint-plugin-github": "^4.7.0",
|
||||||
"jest": "26.4.2",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"jest-circus": "26.4.2",
|
"eslint-plugin-jest": "^27.2.1",
|
||||||
"js-yaml": "3.14.0",
|
"jest": "^29.5.0",
|
||||||
"prettier": "2.1.2",
|
"jest-circus": "^29.4.2",
|
||||||
"ts-jest": "26.3.0",
|
"jest-environment-jsdom": "^29.5.0",
|
||||||
"typescript": "4.0.2"
|
"js-yaml": "^4.1.0",
|
||||||
|
"prettier": "^2.8.8",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import {v4 as uuidv4} from 'uuid'
|
|||||||
|
|
||||||
const CHERRYPICK_EMPTY =
|
const CHERRYPICK_EMPTY =
|
||||||
'The previous cherry-pick is now empty, possibly due to conflict resolution.'
|
'The previous cherry-pick is now empty, possibly due to conflict resolution.'
|
||||||
|
const NOTHING_TO_COMMIT = 'nothing to commit, working tree clean'
|
||||||
|
|
||||||
export enum WorkingBaseType {
|
export enum WorkingBaseType {
|
||||||
Branch = 'branch',
|
Branch = 'branch',
|
||||||
@ -33,24 +34,48 @@ export async function tryFetch(
|
|||||||
branch: string
|
branch: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await git.fetch([`${branch}:refs/remotes/${remote}/${branch}`], remote)
|
await git.fetch([`${branch}:refs/remotes/${remote}/${branch}`], remote, [
|
||||||
|
'--force'
|
||||||
|
])
|
||||||
return true
|
return true
|
||||||
} catch {
|
} catch {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the number of commits that branch2 is ahead of branch1
|
||||||
|
async function commitsAhead(
|
||||||
|
git: GitCommandManager,
|
||||||
|
branch1: string,
|
||||||
|
branch2: string
|
||||||
|
): Promise<number> {
|
||||||
|
const result = await git.revList(
|
||||||
|
[`${branch1}...${branch2}`],
|
||||||
|
['--right-only', '--count']
|
||||||
|
)
|
||||||
|
return Number(result)
|
||||||
|
}
|
||||||
|
|
||||||
// Return true if branch2 is ahead of branch1
|
// Return true if branch2 is ahead of branch1
|
||||||
async function isAhead(
|
async function isAhead(
|
||||||
git: GitCommandManager,
|
git: GitCommandManager,
|
||||||
branch1: string,
|
branch1: string,
|
||||||
branch2: string
|
branch2: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
return (await commitsAhead(git, branch1, branch2)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of commits that branch2 is behind branch1
|
||||||
|
async function commitsBehind(
|
||||||
|
git: GitCommandManager,
|
||||||
|
branch1: string,
|
||||||
|
branch2: string
|
||||||
|
): Promise<number> {
|
||||||
const result = await git.revList(
|
const result = await git.revList(
|
||||||
[`${branch1}...${branch2}`],
|
[`${branch1}...${branch2}`],
|
||||||
['--right-only', '--count']
|
['--left-only', '--count']
|
||||||
)
|
)
|
||||||
return Number(result) > 0
|
return Number(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if branch2 is behind branch1
|
// Return true if branch2 is behind branch1
|
||||||
@ -59,11 +84,7 @@ async function isBehind(
|
|||||||
branch1: string,
|
branch1: string,
|
||||||
branch2: string
|
branch2: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const result = await git.revList(
|
return (await commitsBehind(git, branch1, branch2)) > 0
|
||||||
[`${branch1}...${branch2}`],
|
|
||||||
['--left-only', '--count']
|
|
||||||
)
|
|
||||||
return Number(result) > 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if branch2 is even with branch1
|
// Return true if branch2 is even with branch1
|
||||||
@ -85,13 +106,21 @@ function splitLines(multilineString: string): string[] {
|
|||||||
.filter(x => x !== '')
|
.filter(x => x !== '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CreateOrUpdateBranchResult {
|
||||||
|
action: string
|
||||||
|
base: string
|
||||||
|
hasDiffWithBase: boolean
|
||||||
|
headSha: string
|
||||||
|
}
|
||||||
|
|
||||||
export async function createOrUpdateBranch(
|
export async function createOrUpdateBranch(
|
||||||
git: GitCommandManager,
|
git: GitCommandManager,
|
||||||
commitMessage: string,
|
commitMessage: string,
|
||||||
base: string,
|
base: string,
|
||||||
branch: string,
|
branch: string,
|
||||||
branchRemoteName: string,
|
branchRemoteName: string,
|
||||||
signoff: boolean
|
signoff: boolean,
|
||||||
|
addPaths: string[]
|
||||||
): Promise<CreateOrUpdateBranchResult> {
|
): Promise<CreateOrUpdateBranchResult> {
|
||||||
// Get the working base.
|
// Get the working base.
|
||||||
// When a ref, it may or may not be the actual base.
|
// When a ref, it may or may not be the actual base.
|
||||||
@ -110,28 +139,55 @@ export async function createOrUpdateBranch(
|
|||||||
const result: CreateOrUpdateBranchResult = {
|
const result: CreateOrUpdateBranchResult = {
|
||||||
action: 'none',
|
action: 'none',
|
||||||
base: base,
|
base: base,
|
||||||
hasDiffWithBase: false
|
hasDiffWithBase: false,
|
||||||
|
headSha: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the working base changes to a temporary branch
|
// Save the working base changes to a temporary branch
|
||||||
const tempBranch = uuidv4()
|
const tempBranch = uuidv4()
|
||||||
await git.checkout(tempBranch, 'HEAD')
|
await git.checkout(tempBranch, 'HEAD')
|
||||||
// Commit any uncommitted changes
|
// Commit any uncommitted changes
|
||||||
if (await git.isDirty(true)) {
|
if (await git.isDirty(true, addPaths)) {
|
||||||
core.info('Uncommitted changes found. Adding a commit.')
|
core.info('Uncommitted changes found. Adding a commit.')
|
||||||
await git.exec(['add', '-A'])
|
const aopts = ['add']
|
||||||
const params = ['-m', commitMessage]
|
if (addPaths.length > 0) {
|
||||||
|
aopts.push(...['--', ...addPaths])
|
||||||
|
} else {
|
||||||
|
aopts.push('-A')
|
||||||
|
}
|
||||||
|
await git.exec(aopts, true)
|
||||||
|
const popts = ['-m', commitMessage]
|
||||||
if (signoff) {
|
if (signoff) {
|
||||||
params.push('--signoff')
|
popts.push('--signoff')
|
||||||
}
|
}
|
||||||
await git.commit(params)
|
const commitResult = await git.commit(popts, true)
|
||||||
|
// 'nothing to commit' can occur when core.autocrlf is set to true
|
||||||
|
if (
|
||||||
|
commitResult.exitCode != 0 &&
|
||||||
|
!commitResult.stdout.includes(NOTHING_TO_COMMIT)
|
||||||
|
) {
|
||||||
|
throw new Error(`Unexpected error: ${commitResult.stderr}`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stash any uncommitted tracked and untracked changes
|
||||||
|
const stashed = await git.stashPush(['--include-untracked'])
|
||||||
|
|
||||||
// Perform fetch and reset the working base
|
// Perform fetch and reset the working base
|
||||||
// Commits made during the workflow will be removed
|
// Commits made during the workflow will be removed
|
||||||
if (workingBaseType == WorkingBaseType.Branch) {
|
if (workingBaseType == WorkingBaseType.Branch) {
|
||||||
core.info(`Resetting working base branch '${workingBase}' to its remote`)
|
core.info(`Resetting working base branch '${workingBase}'`)
|
||||||
await git.fetch([`${workingBase}:${workingBase}`], baseRemote, ['--force'])
|
if (branchRemoteName == 'fork') {
|
||||||
|
// If pushing to a fork we must fetch with 'unshallow' to avoid the following error on git push
|
||||||
|
// ! [remote rejected] HEAD -> tests/push-branch-to-fork (shallow update not allowed)
|
||||||
|
await git.fetch([`${workingBase}:${workingBase}`], baseRemote, [
|
||||||
|
'--force'
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
// If the remote is 'origin' we can git reset
|
||||||
|
await git.checkout(workingBase)
|
||||||
|
await git.exec(['reset', '--hard', `${baseRemote}/${workingBase}`])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the working base is not the base, rebase the temp branch commits
|
// If the working base is not the base, rebase the temp branch commits
|
||||||
@ -168,7 +224,7 @@ export async function createOrUpdateBranch(
|
|||||||
// The pull request branch does not exist
|
// The pull request branch does not exist
|
||||||
core.info(`Pull request branch '${branch}' does not exist yet.`)
|
core.info(`Pull request branch '${branch}' does not exist yet.`)
|
||||||
// Create the pull request branch
|
// Create the pull request branch
|
||||||
await git.checkout(branch, 'HEAD')
|
await git.checkout(branch, tempBranch)
|
||||||
// Check if the pull request branch is ahead of the base
|
// Check if the pull request branch is ahead of the base
|
||||||
result.hasDiffWithBase = await isAhead(git, base, branch)
|
result.hasDiffWithBase = await isAhead(git, base, branch)
|
||||||
if (result.hasDiffWithBase) {
|
if (result.hasDiffWithBase) {
|
||||||
@ -194,10 +250,16 @@ export async function createOrUpdateBranch(
|
|||||||
// branches after merging. In particular, it catches a case where the branch was
|
// 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
|
// 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.
|
// to have a diff with the base due to different commits for the same changes.
|
||||||
|
// - If the number of commits ahead of the base branch differs between the branch and
|
||||||
|
// temp branch. This catches a case where the base branch has been force pushed to
|
||||||
|
// a new commit.
|
||||||
// For changes on base this reset is equivalent to a rebase of the pull request branch.
|
// For changes on base this reset is equivalent to a rebase of the pull request branch.
|
||||||
|
const tempBranchCommitsAhead = await commitsAhead(git, base, tempBranch)
|
||||||
|
const branchCommitsAhead = await commitsAhead(git, base, branch)
|
||||||
if (
|
if (
|
||||||
(await git.hasDiff([`${branch}..${tempBranch}`])) ||
|
(await git.hasDiff([`${branch}..${tempBranch}`])) ||
|
||||||
!(await isAhead(git, base, tempBranch))
|
branchCommitsAhead != tempBranchCommitsAhead ||
|
||||||
|
!(tempBranchCommitsAhead > 0) // !isAhead
|
||||||
) {
|
) {
|
||||||
core.info(`Resetting '${branch}'`)
|
core.info(`Resetting '${branch}'`)
|
||||||
// Alternatively, git switch -C branch tempBranch
|
// Alternatively, git switch -C branch tempBranch
|
||||||
@ -221,14 +283,19 @@ export async function createOrUpdateBranch(
|
|||||||
result.hasDiffWithBase = await isAhead(git, base, branch)
|
result.hasDiffWithBase = await isAhead(git, base, branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the pull request branch SHA
|
||||||
|
result.headSha = await git.revParse('HEAD')
|
||||||
|
|
||||||
// Delete the temporary branch
|
// Delete the temporary branch
|
||||||
await git.exec(['branch', '--delete', '--force', tempBranch])
|
await git.exec(['branch', '--delete', '--force', tempBranch])
|
||||||
|
|
||||||
|
// Checkout the working base to leave the local repository as it was found
|
||||||
|
await git.checkout(workingBase)
|
||||||
|
|
||||||
|
// Restore any stashed changes
|
||||||
|
if (stashed) {
|
||||||
|
await git.stashPop()
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateOrUpdateBranchResult {
|
|
||||||
action: string
|
|
||||||
base: string
|
|
||||||
hasDiffWithBase: boolean
|
|
||||||
}
|
|
||||||
|
@ -12,6 +12,7 @@ import * as utils from './utils'
|
|||||||
export interface Inputs {
|
export interface Inputs {
|
||||||
token: string
|
token: string
|
||||||
path: string
|
path: string
|
||||||
|
addPaths: string[]
|
||||||
commitMessage: string
|
commitMessage: string
|
||||||
committer: string
|
committer: string
|
||||||
author: string
|
author: string
|
||||||
@ -23,6 +24,7 @@ export interface Inputs {
|
|||||||
pushToFork: string
|
pushToFork: string
|
||||||
title: string
|
title: string
|
||||||
body: string
|
body: string
|
||||||
|
bodyPath: string
|
||||||
labels: string[]
|
labels: string[]
|
||||||
assignees: string[]
|
assignees: string[]
|
||||||
reviewers: string[]
|
reviewers: string[]
|
||||||
@ -34,14 +36,33 @@ export interface Inputs {
|
|||||||
export async function createPullRequest(inputs: Inputs): Promise<void> {
|
export async function createPullRequest(inputs: Inputs): Promise<void> {
|
||||||
let gitAuthHelper
|
let gitAuthHelper
|
||||||
try {
|
try {
|
||||||
|
if (!inputs.token) {
|
||||||
|
throw new Error(`Input 'token' not supplied. Unable to continue.`)
|
||||||
|
}
|
||||||
|
if (inputs.bodyPath) {
|
||||||
|
if (!utils.fileExistsSync(inputs.bodyPath)) {
|
||||||
|
throw new Error(`File '${inputs.bodyPath}' does not exist.`)
|
||||||
|
}
|
||||||
|
// Update the body input with the contents of the file
|
||||||
|
inputs.body = utils.readFile(inputs.bodyPath)
|
||||||
|
}
|
||||||
|
// 65536 characters is the maximum allowed for the pull request body.
|
||||||
|
if (inputs.body.length > 65536) {
|
||||||
|
core.warning(
|
||||||
|
`Pull request body is too long. Truncating to 65536 characters.`
|
||||||
|
)
|
||||||
|
inputs.body = inputs.body.substring(0, 65536)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the repository path
|
// Get the repository path
|
||||||
const repoPath = utils.getRepoPath(inputs.path)
|
const repoPath = utils.getRepoPath(inputs.path)
|
||||||
// Create a git command manager
|
// Create a git command manager
|
||||||
const git = await GitCommandManager.create(repoPath)
|
const git = await GitCommandManager.create(repoPath)
|
||||||
|
|
||||||
// Save and unset the extraheader auth config if it exists
|
// Save and unset the extraheader auth config if it exists
|
||||||
core.startGroup('Save persisted git credentials')
|
core.startGroup('Prepare git configuration')
|
||||||
gitAuthHelper = new GitAuthHelper(git)
|
gitAuthHelper = new GitAuthHelper(git)
|
||||||
|
await gitAuthHelper.addSafeDirectory()
|
||||||
await gitAuthHelper.savePersistedAuth()
|
await gitAuthHelper.savePersistedAuth()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
@ -59,6 +80,9 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
|||||||
: baseRemote.repository
|
: baseRemote.repository
|
||||||
if (inputs.pushToFork) {
|
if (inputs.pushToFork) {
|
||||||
// Check if the supplied fork is really a fork of the base
|
// Check if the supplied fork is really a fork of the base
|
||||||
|
core.info(
|
||||||
|
`Checking if '${branchRepository}' is a fork of '${baseRemote.repository}'`
|
||||||
|
)
|
||||||
const parentRepository = await githubHelper.getRepositoryParent(
|
const parentRepository = await githubHelper.getRepositoryParent(
|
||||||
branchRepository
|
branchRepository
|
||||||
)
|
)
|
||||||
@ -70,6 +94,7 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
|||||||
// Add a remote for the fork
|
// Add a remote for the fork
|
||||||
const remoteUrl = utils.getRemoteUrl(
|
const remoteUrl = utils.getRemoteUrl(
|
||||||
baseRemote.protocol,
|
baseRemote.protocol,
|
||||||
|
baseRemote.hostname,
|
||||||
branchRepository
|
branchRepository
|
||||||
)
|
)
|
||||||
await git.exec(['remote', 'add', 'fork', remoteUrl])
|
await git.exec(['remote', 'add', 'fork', remoteUrl])
|
||||||
@ -106,6 +131,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
|
||||||
@ -167,7 +198,8 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
|||||||
inputs.base,
|
inputs.base,
|
||||||
inputs.branch,
|
inputs.branch,
|
||||||
branchRemoteName,
|
branchRemoteName,
|
||||||
inputs.signoff
|
inputs.signoff,
|
||||||
|
inputs.addPaths
|
||||||
)
|
)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
@ -179,7 +211,7 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
|||||||
await git.push([
|
await git.push([
|
||||||
'--force-with-lease',
|
'--force-with-lease',
|
||||||
branchRemoteName,
|
branchRemoteName,
|
||||||
`HEAD:refs/heads/${inputs.branch}`
|
`${inputs.branch}:refs/heads/${inputs.branch}`
|
||||||
])
|
])
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
@ -189,11 +221,27 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
|||||||
|
|
||||||
if (result.hasDiffWithBase) {
|
if (result.hasDiffWithBase) {
|
||||||
// Create or update the pull request
|
// Create or update the pull request
|
||||||
await githubHelper.createOrUpdatePullRequest(
|
core.startGroup('Create or update the pull request')
|
||||||
|
const pull = await githubHelper.createOrUpdatePullRequest(
|
||||||
inputs,
|
inputs,
|
||||||
baseRemote.repository,
|
baseRemote.repository,
|
||||||
branchRepository
|
branchRepository
|
||||||
)
|
)
|
||||||
|
core.endGroup()
|
||||||
|
|
||||||
|
// Set outputs
|
||||||
|
core.startGroup('Setting outputs')
|
||||||
|
core.setOutput('pull-request-number', pull.number)
|
||||||
|
core.setOutput('pull-request-url', pull.html_url)
|
||||||
|
if (pull.created) {
|
||||||
|
core.setOutput('pull-request-operation', 'created')
|
||||||
|
} else if (result.action == 'updated') {
|
||||||
|
core.setOutput('pull-request-operation', 'updated')
|
||||||
|
}
|
||||||
|
core.setOutput('pull-request-head-sha', result.headSha)
|
||||||
|
// Deprecated
|
||||||
|
core.exportVariable('PULL_REQUEST_NUMBER', pull.number)
|
||||||
|
core.endGroup()
|
||||||
} else {
|
} else {
|
||||||
// There is no longer a diff with the base
|
// There is no longer a diff with the base
|
||||||
// Check we are in a state where a branch exists
|
// Check we are in a state where a branch exists
|
||||||
@ -209,16 +257,21 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
|
|||||||
branchRemoteName,
|
branchRemoteName,
|
||||||
`refs/heads/${inputs.branch}`
|
`refs/heads/${inputs.branch}`
|
||||||
])
|
])
|
||||||
|
// Set outputs
|
||||||
|
core.startGroup('Setting outputs')
|
||||||
|
core.setOutput('pull-request-operation', 'closed')
|
||||||
|
core.endGroup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(error.message)
|
core.setFailed(utils.getErrorMessage(error))
|
||||||
} finally {
|
} finally {
|
||||||
// Remove auth and restore persisted auth config if it existed
|
// Remove auth and restore persisted auth config if it existed
|
||||||
core.startGroup('Restore persisted git credentials')
|
core.startGroup('Restore git configuration')
|
||||||
await gitAuthHelper.removeAuth()
|
await gitAuthHelper.removeAuth()
|
||||||
await gitAuthHelper.restorePersistedAuth()
|
await gitAuthHelper.restorePersistedAuth()
|
||||||
|
await gitAuthHelper.removeSafeDirectory()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,14 @@ import * as fs from 'fs'
|
|||||||
import {GitCommandManager} from './git-command-manager'
|
import {GitCommandManager} from './git-command-manager'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {URL} from 'url'
|
import {URL} from 'url'
|
||||||
|
import * as utils from './utils'
|
||||||
|
|
||||||
export class GitAuthHelper {
|
export class GitAuthHelper {
|
||||||
private git: GitCommandManager
|
private git: GitCommandManager
|
||||||
private gitConfigPath: string
|
private gitConfigPath = ''
|
||||||
|
private workingDirectory: string
|
||||||
|
private safeDirectoryConfigKey = 'safe.directory'
|
||||||
|
private safeDirectoryAdded = false
|
||||||
private extraheaderConfigKey: string
|
private extraheaderConfigKey: string
|
||||||
private extraheaderConfigPlaceholderValue = 'AUTHORIZATION: basic ***'
|
private extraheaderConfigPlaceholderValue = 'AUTHORIZATION: basic ***'
|
||||||
private extraheaderConfigValueRegex = '^AUTHORIZATION:'
|
private extraheaderConfigValueRegex = '^AUTHORIZATION:'
|
||||||
@ -14,15 +18,38 @@ export class GitAuthHelper {
|
|||||||
|
|
||||||
constructor(git: GitCommandManager) {
|
constructor(git: GitCommandManager) {
|
||||||
this.git = git
|
this.git = git
|
||||||
this.gitConfigPath = path.join(
|
this.workingDirectory = this.git.getWorkingDirectory()
|
||||||
this.git.getWorkingDirectory(),
|
|
||||||
'.git',
|
|
||||||
'config'
|
|
||||||
)
|
|
||||||
const serverUrl = this.getServerUrl()
|
const serverUrl = this.getServerUrl()
|
||||||
this.extraheaderConfigKey = `http.${serverUrl.origin}/.extraheader`
|
this.extraheaderConfigKey = `http.${serverUrl.origin}/.extraheader`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addSafeDirectory(): Promise<void> {
|
||||||
|
const exists = await this.git.configExists(
|
||||||
|
this.safeDirectoryConfigKey,
|
||||||
|
this.workingDirectory,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
if (!exists) {
|
||||||
|
await this.git.config(
|
||||||
|
this.safeDirectoryConfigKey,
|
||||||
|
this.workingDirectory,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
this.safeDirectoryAdded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeSafeDirectory(): Promise<void> {
|
||||||
|
if (this.safeDirectoryAdded) {
|
||||||
|
await this.git.tryConfigUnset(
|
||||||
|
this.safeDirectoryConfigKey,
|
||||||
|
this.workingDirectory,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async savePersistedAuth(): Promise<void> {
|
async savePersistedAuth(): Promise<void> {
|
||||||
// Save and unset persisted extraheader credential in git config if it exists
|
// Save and unset persisted extraheader credential in git config if it exists
|
||||||
this.persistedExtraheaderConfigValue = await this.getAndUnset()
|
this.persistedExtraheaderConfigValue = await this.getAndUnset()
|
||||||
@ -34,7 +61,7 @@ export class GitAuthHelper {
|
|||||||
await this.setExtraheaderConfig(this.persistedExtraheaderConfigValue)
|
await this.setExtraheaderConfig(this.persistedExtraheaderConfigValue)
|
||||||
core.info('Persisted git credentials restored')
|
core.info('Persisted git credentials restored')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
core.warning(e)
|
core.warning(utils.getErrorMessage(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,6 +132,10 @@ export class GitAuthHelper {
|
|||||||
find: string,
|
find: string,
|
||||||
replace: string
|
replace: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
if (this.gitConfigPath.length === 0) {
|
||||||
|
const gitDir = await this.git.getGitDirectory()
|
||||||
|
this.gitConfigPath = path.join(this.workingDirectory, gitDir, 'config')
|
||||||
|
}
|
||||||
let content = (await fs.promises.readFile(this.gitConfigPath)).toString()
|
let content = (await fs.promises.readFile(this.gitConfigPath)).toString()
|
||||||
const index = content.indexOf(find)
|
const index = content.indexOf(find)
|
||||||
if (index < 0 || index != content.lastIndexOf(find)) {
|
if (index < 0 || index != content.lastIndexOf(find)) {
|
||||||
@ -115,12 +146,6 @@ export class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getServerUrl(): URL {
|
private getServerUrl(): URL {
|
||||||
// todo: remove GITHUB_URL after support for GHES Alpha is no longer needed
|
return new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com')
|
||||||
// See https://github.com/actions/checkout/blob/main/src/url-helper.ts#L22-L29
|
|
||||||
return new URL(
|
|
||||||
process.env['GITHUB_SERVER_URL'] ||
|
|
||||||
process.env['GITHUB_URL'] ||
|
|
||||||
'https://github.com'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ export class GitCommandManager {
|
|||||||
} else {
|
} else {
|
||||||
args.push(ref)
|
args.push(ref)
|
||||||
}
|
}
|
||||||
|
// https://github.com/git/git/commit/a047fafc7866cc4087201e284dc1f53e8f9a32d5
|
||||||
|
args.push('--')
|
||||||
await this.exec(args)
|
await this.exec(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +53,10 @@ export class GitCommandManager {
|
|||||||
return await this.exec(args, allowAllExitCodes)
|
return await this.exec(args, allowAllExitCodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
async commit(options?: string[]): Promise<void> {
|
async commit(
|
||||||
|
options?: string[],
|
||||||
|
allowAllExitCodes = false
|
||||||
|
): Promise<GitOutput> {
|
||||||
const args = ['commit']
|
const args = ['commit']
|
||||||
if (this.identityGitOptions) {
|
if (this.identityGitOptions) {
|
||||||
args.unshift(...this.identityGitOptions)
|
args.unshift(...this.identityGitOptions)
|
||||||
@ -61,20 +66,21 @@ export class GitCommandManager {
|
|||||||
args.push(...options)
|
args.push(...options)
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.exec(args)
|
return await this.exec(args, allowAllExitCodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
async config(
|
async config(
|
||||||
configKey: string,
|
configKey: string,
|
||||||
configValue: string,
|
configValue: string,
|
||||||
globalConfig?: boolean
|
globalConfig?: boolean,
|
||||||
|
add?: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.exec([
|
const args: string[] = ['config', globalConfig ? '--global' : '--local']
|
||||||
'config',
|
if (add) {
|
||||||
globalConfig ? '--global' : '--local',
|
args.push('--add')
|
||||||
configKey,
|
}
|
||||||
configValue
|
args.push(...[configKey, configValue])
|
||||||
])
|
await this.exec(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
async configExists(
|
async configExists(
|
||||||
@ -140,6 +146,10 @@ export class GitCommandManager {
|
|||||||
return output.stdout.trim().split(`${configKey} `)[1]
|
return output.stdout.trim().split(`${configKey} `)[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGitDirectory(): Promise<string> {
|
||||||
|
return this.revParse('--git-dir')
|
||||||
|
}
|
||||||
|
|
||||||
getWorkingDirectory(): string {
|
getWorkingDirectory(): string {
|
||||||
return this.workingDirectory
|
return this.workingDirectory
|
||||||
}
|
}
|
||||||
@ -153,17 +163,22 @@ export class GitCommandManager {
|
|||||||
return output.exitCode === 1
|
return output.exitCode === 1
|
||||||
}
|
}
|
||||||
|
|
||||||
async isDirty(untracked: boolean): Promise<boolean> {
|
async isDirty(untracked: boolean, pathspec?: string[]): Promise<boolean> {
|
||||||
|
const pathspecArgs = pathspec ? ['--', ...pathspec] : []
|
||||||
// Check untracked changes
|
// Check untracked changes
|
||||||
if (untracked && (await this.status(['--porcelain', '-unormal']))) {
|
const sargs = ['--porcelain', '-unormal']
|
||||||
|
sargs.push(...pathspecArgs)
|
||||||
|
if (untracked && (await this.status(sargs))) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Check working index changes
|
// Check working index changes
|
||||||
if (await this.hasDiff()) {
|
if (await this.hasDiff(pathspecArgs)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Check staged changes
|
// Check staged changes
|
||||||
if (await this.hasDiff(['--staged'])) {
|
const dargs = ['--staged']
|
||||||
|
dargs.push(...pathspecArgs)
|
||||||
|
if (await this.hasDiff(dargs)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -200,6 +215,23 @@ export class GitCommandManager {
|
|||||||
return output.stdout.trim()
|
return output.stdout.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async stashPush(options?: string[]): Promise<boolean> {
|
||||||
|
const args = ['stash', 'push']
|
||||||
|
if (options) {
|
||||||
|
args.push(...options)
|
||||||
|
}
|
||||||
|
const output = await this.exec(args)
|
||||||
|
return output.stdout.trim() !== 'No local changes to save'
|
||||||
|
}
|
||||||
|
|
||||||
|
async stashPop(options?: string[]): Promise<void> {
|
||||||
|
const args = ['stash', 'pop']
|
||||||
|
if (options) {
|
||||||
|
args.push(...options)
|
||||||
|
}
|
||||||
|
await this.exec(args)
|
||||||
|
}
|
||||||
|
|
||||||
async status(options?: string[]): Promise<string> {
|
async status(options?: string[]): Promise<string> {
|
||||||
const args = ['status']
|
const args = ['status']
|
||||||
if (options) {
|
if (options) {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {Inputs} from './create-pull-request'
|
import {Inputs} from './create-pull-request'
|
||||||
import {Octokit, OctokitOptions} from './octokit-client'
|
import {Octokit, OctokitOptions} from './octokit-client'
|
||||||
|
import * as utils from './utils'
|
||||||
|
|
||||||
const ERROR_PR_REVIEW_FROM_AUTHOR =
|
const ERROR_PR_REVIEW_TOKEN_SCOPE =
|
||||||
'Review cannot be requested from pull request author'
|
'Validation Failed: "Could not resolve to a node with the global id of'
|
||||||
|
|
||||||
interface Repository {
|
interface Repository {
|
||||||
owner: string
|
owner: string
|
||||||
@ -13,6 +14,7 @@ interface Repository {
|
|||||||
interface Pull {
|
interface Pull {
|
||||||
number: number
|
number: number
|
||||||
html_url: string
|
html_url: string
|
||||||
|
created: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GitHubHelper {
|
export class GitHubHelper {
|
||||||
@ -23,6 +25,7 @@ export class GitHubHelper {
|
|||||||
if (token) {
|
if (token) {
|
||||||
options.auth = `${token}`
|
options.auth = `${token}`
|
||||||
}
|
}
|
||||||
|
options.baseUrl = process.env['GITHUB_API_URL'] || 'https://api.github.com'
|
||||||
this.octokit = new Octokit(options)
|
this.octokit = new Octokit(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,11 +40,16 @@ export class GitHubHelper {
|
|||||||
private async createOrUpdate(
|
private async createOrUpdate(
|
||||||
inputs: Inputs,
|
inputs: Inputs,
|
||||||
baseRepository: string,
|
baseRepository: string,
|
||||||
headBranch: string
|
headRepository: string
|
||||||
): Promise<Pull> {
|
): Promise<Pull> {
|
||||||
|
const [headOwner] = headRepository.split('/')
|
||||||
|
const headBranch = `${headOwner}:${inputs.branch}`
|
||||||
|
const headBranchFull = `${headRepository}:${inputs.branch}`
|
||||||
|
|
||||||
// Try to create the pull request
|
// Try to create the pull request
|
||||||
try {
|
try {
|
||||||
const {data: pull} = await this.octokit.pulls.create({
|
core.info(`Attempting creation of pull request`)
|
||||||
|
const {data: pull} = await this.octokit.rest.pulls.create({
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
title: inputs.title,
|
title: inputs.title,
|
||||||
head: headBranch,
|
head: headBranch,
|
||||||
@ -54,42 +62,46 @@ export class GitHubHelper {
|
|||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
number: pull.number,
|
number: pull.number,
|
||||||
html_url: pull.html_url
|
html_url: pull.html_url,
|
||||||
|
created: true
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (
|
if (
|
||||||
!e.message ||
|
utils.getErrorMessage(e).includes(`A pull request already exists for`)
|
||||||
!e.message.includes(`A pull request already exists for ${headBranch}`)
|
|
||||||
) {
|
) {
|
||||||
|
core.info(`A pull request already exists for ${headBranch}`)
|
||||||
|
} else {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the pull request that exists for this branch and base
|
// Update the pull request that exists for this branch and base
|
||||||
const {data: pulls} = await this.octokit.pulls.list({
|
core.info(`Fetching existing pull request`)
|
||||||
|
const {data: pulls} = await this.octokit.rest.pulls.list({
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
state: 'open',
|
state: 'open',
|
||||||
head: headBranch,
|
head: headBranchFull,
|
||||||
base: inputs.base
|
base: inputs.base
|
||||||
})
|
})
|
||||||
const {data: pull} = await this.octokit.pulls.update({
|
core.info(`Attempting update of pull request`)
|
||||||
|
const {data: pull} = await this.octokit.rest.pulls.update({
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
pull_number: pulls[0].number,
|
pull_number: pulls[0].number,
|
||||||
title: inputs.title,
|
title: inputs.title,
|
||||||
body: inputs.body,
|
body: inputs.body
|
||||||
draft: inputs.draft
|
|
||||||
})
|
})
|
||||||
core.info(
|
core.info(
|
||||||
`Updated pull request #${pull.number} (${headBranch} => ${inputs.base})`
|
`Updated pull request #${pull.number} (${headBranch} => ${inputs.base})`
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
number: pull.number,
|
number: pull.number,
|
||||||
html_url: pull.html_url
|
html_url: pull.html_url,
|
||||||
|
created: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRepositoryParent(headRepository: string): Promise<string> {
|
async getRepositoryParent(headRepository: string): Promise<string> {
|
||||||
const {data: headRepo} = await this.octokit.repos.get({
|
const {data: headRepo} = await this.octokit.rest.repos.get({
|
||||||
...this.parseRepository(headRepository)
|
...this.parseRepository(headRepository)
|
||||||
})
|
})
|
||||||
if (!headRepo.parent) {
|
if (!headRepo.parent) {
|
||||||
@ -104,40 +116,39 @@ export class GitHubHelper {
|
|||||||
inputs: Inputs,
|
inputs: Inputs,
|
||||||
baseRepository: string,
|
baseRepository: string,
|
||||||
headRepository: string
|
headRepository: string
|
||||||
): Promise<void> {
|
): Promise<Pull> {
|
||||||
const [headOwner] = headRepository.split('/')
|
|
||||||
const headBranch = `${headOwner}:${inputs.branch}`
|
|
||||||
|
|
||||||
// Create or update the pull request
|
// Create or update the pull request
|
||||||
const pull = await this.createOrUpdate(inputs, baseRepository, headBranch)
|
const pull = await this.createOrUpdate(
|
||||||
|
inputs,
|
||||||
|
baseRepository,
|
||||||
|
headRepository
|
||||||
|
)
|
||||||
|
|
||||||
// Set outputs
|
// Apply milestone
|
||||||
core.startGroup('Setting outputs')
|
|
||||||
core.setOutput('pull-request-number', pull.number)
|
|
||||||
core.setOutput('pull-request-url', pull.html_url)
|
|
||||||
// Deprecated
|
|
||||||
core.exportVariable('PULL_REQUEST_NUMBER', pull.number)
|
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
// Set milestone, labels and assignees
|
|
||||||
const updateIssueParams = {}
|
|
||||||
if (inputs.milestone) {
|
if (inputs.milestone) {
|
||||||
updateIssueParams['milestone'] = inputs.milestone
|
|
||||||
core.info(`Applying milestone '${inputs.milestone}'`)
|
core.info(`Applying milestone '${inputs.milestone}'`)
|
||||||
}
|
await this.octokit.rest.issues.update({
|
||||||
if (inputs.labels.length > 0) {
|
|
||||||
updateIssueParams['labels'] = inputs.labels
|
|
||||||
core.info(`Applying labels '${inputs.labels}'`)
|
|
||||||
}
|
|
||||||
if (inputs.assignees.length > 0) {
|
|
||||||
updateIssueParams['assignees'] = inputs.assignees
|
|
||||||
core.info(`Applying assignees '${inputs.assignees}'`)
|
|
||||||
}
|
|
||||||
if (Object.keys(updateIssueParams).length > 0) {
|
|
||||||
await this.octokit.issues.update({
|
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
issue_number: pull.number,
|
issue_number: pull.number,
|
||||||
...updateIssueParams
|
milestone: inputs.milestone
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Apply labels
|
||||||
|
if (inputs.labels.length > 0) {
|
||||||
|
core.info(`Applying labels '${inputs.labels}'`)
|
||||||
|
await this.octokit.rest.issues.addLabels({
|
||||||
|
...this.parseRepository(baseRepository),
|
||||||
|
issue_number: pull.number,
|
||||||
|
labels: inputs.labels
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Apply assignees
|
||||||
|
if (inputs.assignees.length > 0) {
|
||||||
|
core.info(`Applying assignees '${inputs.assignees}'`)
|
||||||
|
await this.octokit.rest.issues.addAssignees({
|
||||||
|
...this.parseRepository(baseRepository),
|
||||||
|
issue_number: pull.number,
|
||||||
|
assignees: inputs.assignees
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,23 +159,27 @@ export class GitHubHelper {
|
|||||||
core.info(`Requesting reviewers '${inputs.reviewers}'`)
|
core.info(`Requesting reviewers '${inputs.reviewers}'`)
|
||||||
}
|
}
|
||||||
if (inputs.teamReviewers.length > 0) {
|
if (inputs.teamReviewers.length > 0) {
|
||||||
requestReviewersParams['team_reviewers'] = inputs.teamReviewers
|
const teams = utils.stripOrgPrefixFromTeams(inputs.teamReviewers)
|
||||||
core.info(`Requesting team reviewers '${inputs.teamReviewers}'`)
|
requestReviewersParams['team_reviewers'] = teams
|
||||||
|
core.info(`Requesting team reviewers '${teams}'`)
|
||||||
}
|
}
|
||||||
if (Object.keys(requestReviewersParams).length > 0) {
|
if (Object.keys(requestReviewersParams).length > 0) {
|
||||||
try {
|
try {
|
||||||
await this.octokit.pulls.requestReviewers({
|
await this.octokit.rest.pulls.requestReviewers({
|
||||||
...this.parseRepository(baseRepository),
|
...this.parseRepository(baseRepository),
|
||||||
pull_number: pull.number,
|
pull_number: pull.number,
|
||||||
...requestReviewersParams
|
...requestReviewersParams
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message && e.message.includes(ERROR_PR_REVIEW_FROM_AUTHOR)) {
|
if (utils.getErrorMessage(e).includes(ERROR_PR_REVIEW_TOKEN_SCOPE)) {
|
||||||
core.warning(ERROR_PR_REVIEW_FROM_AUTHOR)
|
core.error(
|
||||||
} else {
|
`Unable to request reviewers. If requesting team reviewers a 'repo' scoped PAT is required.`
|
||||||
|
)
|
||||||
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return pull
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/main.ts
10
src/main.ts
@ -8,29 +8,31 @@ async function run(): Promise<void> {
|
|||||||
const inputs: Inputs = {
|
const inputs: Inputs = {
|
||||||
token: core.getInput('token'),
|
token: core.getInput('token'),
|
||||||
path: core.getInput('path'),
|
path: core.getInput('path'),
|
||||||
|
addPaths: utils.getInputAsArray('add-paths'),
|
||||||
commitMessage: core.getInput('commit-message'),
|
commitMessage: core.getInput('commit-message'),
|
||||||
committer: core.getInput('committer'),
|
committer: core.getInput('committer'),
|
||||||
author: core.getInput('author'),
|
author: core.getInput('author'),
|
||||||
signoff: core.getInput('signoff') === 'true',
|
signoff: core.getBooleanInput('signoff'),
|
||||||
branch: core.getInput('branch'),
|
branch: core.getInput('branch'),
|
||||||
deleteBranch: core.getInput('delete-branch') === 'true',
|
deleteBranch: core.getBooleanInput('delete-branch'),
|
||||||
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'),
|
||||||
title: core.getInput('title'),
|
title: core.getInput('title'),
|
||||||
body: core.getInput('body'),
|
body: core.getInput('body'),
|
||||||
|
bodyPath: core.getInput('body-path'),
|
||||||
labels: utils.getInputAsArray('labels'),
|
labels: utils.getInputAsArray('labels'),
|
||||||
assignees: utils.getInputAsArray('assignees'),
|
assignees: utils.getInputAsArray('assignees'),
|
||||||
reviewers: utils.getInputAsArray('reviewers'),
|
reviewers: utils.getInputAsArray('reviewers'),
|
||||||
teamReviewers: utils.getInputAsArray('team-reviewers'),
|
teamReviewers: utils.getInputAsArray('team-reviewers'),
|
||||||
milestone: Number(core.getInput('milestone')),
|
milestone: Number(core.getInput('milestone')),
|
||||||
draft: core.getInput('draft') === 'true'
|
draft: core.getBooleanInput('draft')
|
||||||
}
|
}
|
||||||
core.debug(`Inputs: ${inspect(inputs)}`)
|
core.debug(`Inputs: ${inspect(inputs)}`)
|
||||||
|
|
||||||
await createPullRequest(inputs)
|
await createPullRequest(inputs)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(error.message)
|
core.setFailed(utils.getErrorMessage(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,23 @@
|
|||||||
import {Octokit as Core} from '@octokit/core'
|
import {Octokit as Core} from '@octokit/core'
|
||||||
import {paginateRest} from '@octokit/plugin-paginate-rest'
|
import {paginateRest} from '@octokit/plugin-paginate-rest'
|
||||||
import {restEndpointMethods} from '@octokit/plugin-rest-endpoint-methods'
|
import {restEndpointMethods} from '@octokit/plugin-rest-endpoint-methods'
|
||||||
|
import {HttpsProxyAgent} from 'https-proxy-agent'
|
||||||
|
import {getProxyForUrl} from 'proxy-from-env'
|
||||||
export {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods'
|
export {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods'
|
||||||
export {OctokitOptions} from '@octokit/core/dist-types/types'
|
export {OctokitOptions} from '@octokit/core/dist-types/types'
|
||||||
|
|
||||||
export const Octokit = Core.plugin(paginateRest, restEndpointMethods)
|
export const Octokit = Core.plugin(
|
||||||
|
paginateRest,
|
||||||
|
restEndpointMethods,
|
||||||
|
autoProxyAgent
|
||||||
|
)
|
||||||
|
|
||||||
|
// Octokit plugin to support the standard environment variables http_proxy, https_proxy and no_proxy
|
||||||
|
function autoProxyAgent(octokit: Core) {
|
||||||
|
octokit.hook.before('request', options => {
|
||||||
|
const proxy = getProxyForUrl(options.baseUrl)
|
||||||
|
if (proxy) {
|
||||||
|
options.request.agent = new HttpsProxyAgent(proxy)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
59
src/utils.ts
59
src/utils.ts
@ -16,6 +16,16 @@ export function getStringAsArray(str: string): string[] {
|
|||||||
.filter(x => x !== '')
|
.filter(x => x !== '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function stripOrgPrefixFromTeams(teams: string[]): string[] {
|
||||||
|
return teams.map(team => {
|
||||||
|
const slashIndex = team.lastIndexOf('/')
|
||||||
|
if (slashIndex > 0) {
|
||||||
|
return team.substring(slashIndex + 1)
|
||||||
|
}
|
||||||
|
return team
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function getRepoPath(relativePath?: string): string {
|
export function getRepoPath(relativePath?: string): string {
|
||||||
let githubWorkspacePath = process.env['GITHUB_WORKSPACE']
|
let githubWorkspacePath = process.env['GITHUB_WORKSPACE']
|
||||||
if (!githubWorkspacePath) {
|
if (!githubWorkspacePath) {
|
||||||
@ -32,6 +42,7 @@ export function getRepoPath(relativePath?: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface RemoteDetail {
|
interface RemoteDetail {
|
||||||
|
hostname: string
|
||||||
protocol: string
|
protocol: string
|
||||||
repository: string
|
repository: string
|
||||||
}
|
}
|
||||||
@ -39,12 +50,25 @@ interface RemoteDetail {
|
|||||||
export function getRemoteDetail(remoteUrl: string): RemoteDetail {
|
export function getRemoteDetail(remoteUrl: string): RemoteDetail {
|
||||||
// Parse the protocol and github repository from a URL
|
// Parse the protocol and github repository from a URL
|
||||||
// e.g. HTTPS, peter-evans/create-pull-request
|
// e.g. HTTPS, peter-evans/create-pull-request
|
||||||
const httpsUrlPattern = /^https:\/\/.*@?github.com\/(.+\/.+)$/i
|
const githubUrl = process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
||||||
const sshUrlPattern = /^git@github.com:(.+\/.+).git$/i
|
|
||||||
|
const githubServerMatch = githubUrl.match(/^https?:\/\/(.+)$/i)
|
||||||
|
if (!githubServerMatch) {
|
||||||
|
throw new Error('Could not parse GitHub Server name')
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostname = githubServerMatch[1]
|
||||||
|
|
||||||
|
const httpsUrlPattern = new RegExp(
|
||||||
|
'^https?://.*@?' + hostname + '/(.+/.+?)(\\.git)?$',
|
||||||
|
'i'
|
||||||
|
)
|
||||||
|
const sshUrlPattern = new RegExp('^git@' + hostname + ':(.+/.+)\\.git$', 'i')
|
||||||
|
|
||||||
const httpsMatch = remoteUrl.match(httpsUrlPattern)
|
const httpsMatch = remoteUrl.match(httpsUrlPattern)
|
||||||
if (httpsMatch) {
|
if (httpsMatch) {
|
||||||
return {
|
return {
|
||||||
|
hostname,
|
||||||
protocol: 'HTTPS',
|
protocol: 'HTTPS',
|
||||||
repository: httpsMatch[1]
|
repository: httpsMatch[1]
|
||||||
}
|
}
|
||||||
@ -53,6 +77,7 @@ export function getRemoteDetail(remoteUrl: string): RemoteDetail {
|
|||||||
const sshMatch = remoteUrl.match(sshUrlPattern)
|
const sshMatch = remoteUrl.match(sshUrlPattern)
|
||||||
if (sshMatch) {
|
if (sshMatch) {
|
||||||
return {
|
return {
|
||||||
|
hostname,
|
||||||
protocol: 'SSH',
|
protocol: 'SSH',
|
||||||
repository: sshMatch[1]
|
repository: sshMatch[1]
|
||||||
}
|
}
|
||||||
@ -63,10 +88,14 @@ export function getRemoteDetail(remoteUrl: string): RemoteDetail {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRemoteUrl(protocol: string, repository: string): string {
|
export function getRemoteUrl(
|
||||||
|
protocol: string,
|
||||||
|
hostname: string,
|
||||||
|
repository: string
|
||||||
|
): string {
|
||||||
return protocol == 'HTTPS'
|
return protocol == 'HTTPS'
|
||||||
? `https://github.com/${repository}`
|
? `https://${hostname}/${repository}`
|
||||||
: `git@github.com:${repository}.git`
|
: `git@${hostname}:${repository}.git`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function secondsSinceEpoch(): number {
|
export function secondsSinceEpoch(): number {
|
||||||
@ -122,12 +151,14 @@ export function fileExistsSync(path: string): boolean {
|
|||||||
try {
|
try {
|
||||||
stats = fs.statSync(path)
|
stats = fs.statSync(path)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'ENOENT') {
|
if (hasErrorCode(error) && error.code === 'ENOENT') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Encountered an error when checking whether path '${path}' exists: ${error.message}`
|
`Encountered an error when checking whether path '${path}' exists: ${getErrorMessage(
|
||||||
|
error
|
||||||
|
)}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,3 +168,17 @@ export function fileExistsSync(path: string): boolean {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readFile(path: string): string {
|
||||||
|
return fs.readFileSync(path, 'utf-8')
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
function hasErrorCode(error: any): error is {code: string} {
|
||||||
|
return typeof (error && error.code) === 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getErrorMessage(error: unknown) {
|
||||||
|
if (error instanceof Error) return error.message
|
||||||
|
return String(error)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user