Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@ -4,6 +4,7 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
day: "tuesday"
|
||||||
labels:
|
labels:
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
|
|
||||||
@ -11,5 +12,9 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
allow:
|
day: "tuesday"
|
||||||
- dependency-name: "@actions/*"
|
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@v2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
|
||||||
|
pull-request-number: ${{ github.event.pull_request.number }}
|
||||||
|
merge-method: squash
|
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -77,7 +77,7 @@ 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@v2
|
uses: peter-evans/close-pull@v2
|
||||||
@ -123,6 +123,7 @@ jobs:
|
|||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v4
|
uses: peter-evans/create-pull-request@v4
|
||||||
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: |
|
||||||
|
6
.github/workflows/slash-command-dispatch.yml
vendored
6
.github/workflows/slash-command-dispatch.yml
vendored
@ -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-major-version.yml
vendored
Normal file
31
.github/workflows/update-major-version.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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
|
@ -59,7 +59,7 @@ All inputs are **optional**. If not set, sensible defaults will be used.
|
|||||||
| `author` | The author name and email address in the format `Display Name <email@address.com>`. Defaults to the user who triggered the workflow run. | `${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>` |
|
| `author` | The author name and email address in the format `Display Name <email@address.com>`. Defaults to the user who triggered the workflow run. | `${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>` |
|
||||||
| `signoff` | Add [`Signed-off-by`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---signoff) line by the committer at the end of the commit log message. | `false` |
|
| `signoff` | Add [`Signed-off-by`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---signoff) line by the committer at the end of the commit log message. | `false` |
|
||||||
| `branch` | The pull request branch name. | `create-pull-request/patch` |
|
| `branch` | The pull request branch name. | `create-pull-request/patch` |
|
||||||
| `delete-branch` | Delete the `branch` when closing pull requests, and when undeleted after merging. Recommend `true`. | `false` |
|
| `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. | |
|
||||||
|
@ -14,7 +14,7 @@ const REMOTE_NAME = 'origin'
|
|||||||
const TRACKED_FILE = 'a/tracked-file.txt'
|
const TRACKED_FILE = 'a/tracked-file.txt'
|
||||||
const UNTRACKED_FILE = 'b/untracked-file.txt'
|
const UNTRACKED_FILE = 'b/untracked-file.txt'
|
||||||
|
|
||||||
const DEFAULT_BRANCH = 'tests/master'
|
const DEFAULT_BRANCH = 'tests/main'
|
||||||
const NOT_BASE_BRANCH = 'tests/branch-that-is-not-the-base'
|
const NOT_BASE_BRANCH = 'tests/branch-that-is-not-the-base'
|
||||||
const NOT_EXIST_BRANCH = 'tests/branch-that-does-not-exist'
|
const NOT_EXIST_BRANCH = 'tests/branch-that-does-not-exist'
|
||||||
|
|
||||||
@ -108,10 +108,10 @@ describe('create-or-update-branch tests', () => {
|
|||||||
// Check there are no local changes that might be destroyed by running these tests
|
// Check there are no local changes that might be destroyed by running these tests
|
||||||
expect(await git.isDirty(true)).toBeFalsy()
|
expect(await git.isDirty(true)).toBeFalsy()
|
||||||
// Fetch the default branch
|
// Fetch the default branch
|
||||||
await git.fetch(['master:refs/remotes/origin/master'])
|
await git.fetch(['main:refs/remotes/origin/main'])
|
||||||
|
|
||||||
// Create a "not base branch" for the test run
|
// Create a "not base branch" for the test run
|
||||||
await git.checkout('master')
|
await git.checkout('main')
|
||||||
await git.checkout(NOT_BASE_BRANCH, 'HEAD')
|
await git.checkout(NOT_BASE_BRANCH, 'HEAD')
|
||||||
await createFile(TRACKED_FILE)
|
await createFile(TRACKED_FILE)
|
||||||
await git.exec(['add', '-A'])
|
await git.exec(['add', '-A'])
|
||||||
@ -123,7 +123,7 @@ describe('create-or-update-branch tests', () => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
// Create a new default branch for the test run with a tracked file
|
// Create a new default branch for the test run with a tracked file
|
||||||
await git.checkout('master')
|
await git.checkout('main')
|
||||||
await git.checkout(DEFAULT_BRANCH, 'HEAD')
|
await git.checkout(DEFAULT_BRANCH, 'HEAD')
|
||||||
await createFile(TRACKED_FILE)
|
await createFile(TRACKED_FILE)
|
||||||
await git.exec(['add', '-A'])
|
await git.exec(['add', '-A'])
|
||||||
@ -631,6 +631,69 @@ describe('create-or-update-branch tests', () => {
|
|||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('tests create, force push of base branch, and update with identical changes', async () => {
|
||||||
|
// If the base branch is force pushed to a different commit when there is an open
|
||||||
|
// pull request, the branch must be reset to rebase the changes on the base.
|
||||||
|
|
||||||
|
// Create tracked and untracked file changes
|
||||||
|
const changes = await createChanges()
|
||||||
|
const commitMessage = uuidv4()
|
||||||
|
const result = await createOrUpdateBranch(
|
||||||
|
git,
|
||||||
|
commitMessage,
|
||||||
|
'',
|
||||||
|
BRANCH,
|
||||||
|
REMOTE_NAME,
|
||||||
|
false,
|
||||||
|
ADD_PATHS_DEFAULT
|
||||||
|
)
|
||||||
|
expect(result.action).toEqual('created')
|
||||||
|
expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked)
|
||||||
|
expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked)
|
||||||
|
expect(
|
||||||
|
await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE])
|
||||||
|
).toBeTruthy()
|
||||||
|
|
||||||
|
// Push pull request branch to remote
|
||||||
|
await git.push([
|
||||||
|
'--force-with-lease',
|
||||||
|
REMOTE_NAME,
|
||||||
|
`HEAD:refs/heads/${BRANCH}`
|
||||||
|
])
|
||||||
|
|
||||||
|
await afterTest(false)
|
||||||
|
await beforeTest()
|
||||||
|
|
||||||
|
// Force push the base branch to a different commit
|
||||||
|
const amendedCommitMessage = uuidv4()
|
||||||
|
await git.commit(['--amend', '-m', amendedCommitMessage])
|
||||||
|
await git.push([
|
||||||
|
'--force',
|
||||||
|
REMOTE_NAME,
|
||||||
|
`HEAD:refs/heads/${DEFAULT_BRANCH}`
|
||||||
|
])
|
||||||
|
|
||||||
|
// Create the same tracked and untracked file changes (no change on update)
|
||||||
|
const _changes = await createChanges(changes.tracked, changes.untracked)
|
||||||
|
const _commitMessage = uuidv4()
|
||||||
|
const _result = await createOrUpdateBranch(
|
||||||
|
git,
|
||||||
|
_commitMessage,
|
||||||
|
'',
|
||||||
|
BRANCH,
|
||||||
|
REMOTE_NAME,
|
||||||
|
false,
|
||||||
|
ADD_PATHS_DEFAULT
|
||||||
|
)
|
||||||
|
expect(_result.action).toEqual('updated')
|
||||||
|
expect(_result.hasDiffWithBase).toBeTruthy()
|
||||||
|
expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked)
|
||||||
|
expect(await getFileContent(UNTRACKED_FILE)).toEqual(_changes.untracked)
|
||||||
|
expect(
|
||||||
|
await gitLogMatches([_commitMessage, amendedCommitMessage])
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
it('tests create and update with commits on the working base (during the workflow)', async () => {
|
it('tests create and update with commits on the working base (during the workflow)', async () => {
|
||||||
// Create commits on the working base
|
// Create commits on the working base
|
||||||
const commits = await createCommits(git)
|
const commits = await createCommits(git)
|
||||||
@ -1519,6 +1582,75 @@ describe('create-or-update-branch tests', () => {
|
|||||||
).toBeTruthy()
|
).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('tests create, force push of base branch, and update with identical changes (WBNB)', async () => {
|
||||||
|
// If the base branch is force pushed to a different commit when there is an open
|
||||||
|
// pull request, the branch must be reset to rebase the changes on the base.
|
||||||
|
|
||||||
|
// Set the working base to a branch that is not the pull request base
|
||||||
|
await git.checkout(NOT_BASE_BRANCH)
|
||||||
|
|
||||||
|
// Create tracked and untracked file changes
|
||||||
|
const changes = await createChanges()
|
||||||
|
const commitMessage = uuidv4()
|
||||||
|
const result = await createOrUpdateBranch(
|
||||||
|
git,
|
||||||
|
commitMessage,
|
||||||
|
BASE,
|
||||||
|
BRANCH,
|
||||||
|
REMOTE_NAME,
|
||||||
|
false,
|
||||||
|
ADD_PATHS_DEFAULT
|
||||||
|
)
|
||||||
|
expect(result.action).toEqual('created')
|
||||||
|
expect(await getFileContent(TRACKED_FILE)).toEqual(changes.tracked)
|
||||||
|
expect(await getFileContent(UNTRACKED_FILE)).toEqual(changes.untracked)
|
||||||
|
expect(
|
||||||
|
await gitLogMatches([commitMessage, INIT_COMMIT_MESSAGE])
|
||||||
|
).toBeTruthy()
|
||||||
|
|
||||||
|
// Push pull request branch to remote
|
||||||
|
await git.push([
|
||||||
|
'--force-with-lease',
|
||||||
|
REMOTE_NAME,
|
||||||
|
`HEAD:refs/heads/${BRANCH}`
|
||||||
|
])
|
||||||
|
|
||||||
|
await afterTest(false)
|
||||||
|
await beforeTest()
|
||||||
|
|
||||||
|
// Force push the base branch to a different commit
|
||||||
|
const amendedCommitMessage = uuidv4()
|
||||||
|
await git.commit(['--amend', '-m', amendedCommitMessage])
|
||||||
|
await git.push([
|
||||||
|
'--force',
|
||||||
|
REMOTE_NAME,
|
||||||
|
`HEAD:refs/heads/${DEFAULT_BRANCH}`
|
||||||
|
])
|
||||||
|
|
||||||
|
// Set the working base to a branch that is not the pull request base
|
||||||
|
await git.checkout(NOT_BASE_BRANCH)
|
||||||
|
|
||||||
|
// Create the same tracked and untracked file changes (no change on update)
|
||||||
|
const _changes = await createChanges(changes.tracked, changes.untracked)
|
||||||
|
const _commitMessage = uuidv4()
|
||||||
|
const _result = await createOrUpdateBranch(
|
||||||
|
git,
|
||||||
|
_commitMessage,
|
||||||
|
BASE,
|
||||||
|
BRANCH,
|
||||||
|
REMOTE_NAME,
|
||||||
|
false,
|
||||||
|
ADD_PATHS_DEFAULT
|
||||||
|
)
|
||||||
|
expect(_result.action).toEqual('updated')
|
||||||
|
expect(_result.hasDiffWithBase).toBeTruthy()
|
||||||
|
expect(await getFileContent(TRACKED_FILE)).toEqual(_changes.tracked)
|
||||||
|
expect(await getFileContent(UNTRACKED_FILE)).toEqual(_changes.untracked)
|
||||||
|
expect(
|
||||||
|
await gitLogMatches([_commitMessage, amendedCommitMessage])
|
||||||
|
).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
it('tests create and update with commits on the working base (during the workflow) (WBNB)', async () => {
|
it('tests create and update with commits on the working base (during the workflow) (WBNB)', async () => {
|
||||||
// Set the working base to a branch that is not the pull request base
|
// Set the working base to a branch that is not the pull request base
|
||||||
await git.checkout(NOT_BASE_BRANCH)
|
await git.checkout(NOT_BASE_BRANCH)
|
||||||
|
@ -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 &
|
||||||
|
|
||||||
|
@ -90,11 +90,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 () => {
|
||||||
|
2261
dist/index.js
vendored
2261
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
@ -146,7 +146,7 @@ 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. 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 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.
|
||||||
|
|
||||||
@ -214,8 +214,9 @@ How to use SSH (deploy keys) with create-pull-request action:
|
|||||||
|
|
||||||
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.
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
- [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)
|
- [Setting the pull request body from a file](#setting-the-pull-request-body-from-a-file)
|
||||||
- [Using a markdown template](#using-a-markdown-template)
|
- [Using a markdown template](#using-a-markdown-template)
|
||||||
@ -282,8 +283,10 @@ jobs:
|
|||||||
- 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:
|
||||||
@ -475,7 +478,9 @@ 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@v4
|
uses: peter-evans/create-pull-request@v4
|
||||||
@ -522,19 +527,32 @@ jobs:
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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@v4
|
||||||
|
```
|
||||||
|
|
||||||
### 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@v4
|
uses: peter-evans/create-pull-request@v4
|
||||||
with:
|
with:
|
||||||
@ -545,16 +563,15 @@ The recommended method is to use [`set-output`](https://docs.github.com/en/actio
|
|||||||
### Setting the pull request body from a file
|
### Setting the pull request body from a file
|
||||||
|
|
||||||
This example shows how file content can be read into a variable and passed to the action.
|
This example shows how file content can be read into a variable and passed to the action.
|
||||||
The content must be [escaped to preserve newlines](https://github.community/t/set-output-truncates-multiline-strings/16852/3).
|
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
- id: get-pr-body
|
- id: get-pr-body
|
||||||
run: |
|
run: |
|
||||||
body=$(cat pr-body.txt)
|
body=$(cat pr-body.txt)
|
||||||
body="${body//'%'/'%25'}"
|
delimiter="$(openssl rand -hex 8)"
|
||||||
body="${body//$'\n'/'%0A'}"
|
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
||||||
body="${body//$'\r'/'%0D'}"
|
echo "$body" >> $GITHUB_OUTPUT
|
||||||
echo ::set-output name=body::$body
|
echo "$delimiter" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@v4
|
uses: peter-evans/create-pull-request@v4
|
||||||
|
8735
package-lock.json
generated
8735
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@ -31,28 +31,28 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/exec": "^1.1.1",
|
"@actions/exec": "^1.1.1",
|
||||||
"@octokit/core": "^3.5.1",
|
"@octokit/core": "^4.2.0",
|
||||||
"@octokit/plugin-paginate-rest": "^2.17.0",
|
"@octokit/plugin-paginate-rest": "^5.0.1",
|
||||||
"@octokit/plugin-rest-endpoint-methods": "^5.13.0",
|
"@octokit/plugin-rest-endpoint-methods": "^6.8.1",
|
||||||
"proxy-agent": "^5.0.0",
|
"proxy-agent": "^5.0.0",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.5.0",
|
"@types/jest": "^29.4.1",
|
||||||
"@types/node": "^16.11.11",
|
"@types/node": "^18.15.3",
|
||||||
"@typescript-eslint/parser": "^5.5.0",
|
"@typescript-eslint/parser": "^5.55.0",
|
||||||
"@vercel/ncc": "^0.32.0",
|
"@vercel/ncc": "^0.36.1",
|
||||||
"eslint": "^8.3.0",
|
"eslint": "^8.36.0",
|
||||||
"eslint-import-resolver-typescript": "^2.5.0",
|
"eslint-import-resolver-typescript": "^3.5.3",
|
||||||
"eslint-plugin-github": "^4.3.5",
|
"eslint-plugin-github": "^4.6.1",
|
||||||
"eslint-plugin-import": "^2.25.3",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"eslint-plugin-jest": "^26.1.5",
|
"eslint-plugin-jest": "^27.2.1",
|
||||||
"jest": "^28.1.0",
|
"jest": "^29.5.0",
|
||||||
"jest-circus": "^28.1.0",
|
"jest-circus": "^29.4.2",
|
||||||
"jest-environment-jsdom": "^28.1.0",
|
"jest-environment-jsdom": "^29.5.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"prettier": "^2.5.0",
|
"prettier": "^2.8.4",
|
||||||
"ts-jest": "^28.0.2",
|
"ts-jest": "^29.0.5",
|
||||||
"typescript": "^4.5.2"
|
"typescript": "^4.9.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,17 +43,39 @@ export async function tryFetch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
@ -62,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
|
||||||
@ -226,10 +244,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
|
||||||
|
@ -35,6 +35,10 @@ 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.`)
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -74,6 +78,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])
|
||||||
|
22
src/utils.ts
22
src/utils.ts
@ -32,6 +32,7 @@ export function getRepoPath(relativePath?: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface RemoteDetail {
|
interface RemoteDetail {
|
||||||
|
hostname: string
|
||||||
protocol: string
|
protocol: string
|
||||||
repository: string
|
repository: string
|
||||||
}
|
}
|
||||||
@ -46,18 +47,18 @@ export function getRemoteDetail(remoteUrl: string): RemoteDetail {
|
|||||||
throw new Error('Could not parse GitHub Server name')
|
throw new Error('Could not parse GitHub Server name')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hostname = githubServerMatch[1]
|
||||||
|
|
||||||
const httpsUrlPattern = new RegExp(
|
const httpsUrlPattern = new RegExp(
|
||||||
'^https?://.*@?' + githubServerMatch[1] + '/(.+/.+?)(\\.git)?$',
|
'^https?://.*@?' + hostname + '/(.+/.+?)(\\.git)?$',
|
||||||
'i'
|
|
||||||
)
|
|
||||||
const sshUrlPattern = new RegExp(
|
|
||||||
'^git@' + githubServerMatch[1] + ':(.+/.+)\\.git$',
|
|
||||||
'i'
|
'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]
|
||||||
}
|
}
|
||||||
@ -66,6 +67,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]
|
||||||
}
|
}
|
||||||
@ -76,10 +78,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 {
|
||||||
|
Reference in New Issue
Block a user