Compare commits

..

1 Commits

Author SHA1 Message Date
Peter Evans 66468b3fc2 fix: handle ambiguous argument failure on diff stat 2024-09-05 08:05:38 +00:00
26 changed files with 29060 additions and 4952 deletions
+3 -12
View File
@@ -3,27 +3,18 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
interval: "weekly"
day: "tuesday"
labels:
- "dependencies"
groups:
github-actions:
applies-to: version-updates
patterns:
- "*"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
interval: "weekly"
day: "tuesday"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
labels:
- "dependencies"
groups:
npm:
applies-to: version-updates
patterns:
- "*"
+13 -13
View File
@@ -19,21 +19,21 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
node-version: 20.x
cache: npm
- run: npm ci
- run: npm run build
- run: npm run format-check
- run: npm run lint
- run: npm run test
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v4
with:
name: action.yml
path: action.yml
@@ -46,16 +46,16 @@ jobs:
matrix:
target: [built, committed]
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
ref: main
- if: matrix.target == 'built' || github.event_name == 'pull_request'
uses: actions/download-artifact@v7
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- if: matrix.target == 'built' || github.event_name == 'pull_request'
uses: actions/download-artifact@v7
uses: actions/download-artifact@v4
with:
name: action.yml
path: .
@@ -92,7 +92,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Find Comment
uses: peter-evans/find-comment@v4
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.number }}
@@ -101,7 +101,7 @@ jobs:
- if: steps.fc.outputs.comment-id == ''
name: Create comment
uses: peter-evans/create-or-update-comment@v5
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.number }}
body: |
@@ -118,13 +118,13 @@ jobs:
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v7
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
commit-message: 'build: update distribution'
+4 -4
View File
@@ -6,7 +6,7 @@ jobs:
createPullRequest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Make changes to pull request
run: date +%s > report.txt
@@ -29,8 +29,8 @@ jobs:
labels: |
report
automated pr
assignees: retepsnave
reviewers: retepsnave
assignees: peter-evans
reviewers: peter-evans
milestone: 1
draft: false
branch: example-patches
@@ -42,7 +42,7 @@ jobs:
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
- name: Add reaction
uses: peter-evans/create-or-update-comment@v5
uses: peter-evans/create-or-update-comment@v4
with:
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
+7 -1
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Slash Command Dispatch
uses: peter-evans/slash-command-dispatch@v5
uses: peter-evans/slash-command-dispatch@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
config: >
@@ -18,6 +18,12 @@ jobs:
"repository": "peter-evans/create-pull-request-tests",
"named_args": true
},
{
"command": "testv5",
"permission": "admin",
"repository": "peter-evans/create-pull-request-tests",
"named_args": true
},
{
"command": "clean",
"permission": "admin",
+2 -2
View File
@@ -11,14 +11,14 @@ on:
type: choice
description: The major version tag to update
options:
- v6
- v7
- v8
jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
fetch-depth: 0
+34 -14
View File
@@ -15,27 +15,27 @@ Create Pull Request action will:
- tracked (modified) files
- commits made during the workflow that have not been pushed
2. Commit all changes to a new branch, or update an existing pull request branch.
3. Create or update a pull request to merge the branch into the base—the branch checked out in the workflow.
3. Create a pull request to merge the new branch into the base—the branch checked out in the workflow.
## Documentation
- [Concepts, guidelines and advanced usage](docs/concepts-guidelines.md)
- [Examples](docs/examples.md)
- [Updating between versions](docs/updating.md)
- [Updating to v7](docs/updating.md)
- [Common issues](docs/common-issues.md)
## Usage
```yml
- uses: actions/checkout@v6
- uses: actions/checkout@v4
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
```
You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v8.x.x`
You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v7.x.x`
### Workflow permissions
@@ -53,7 +53,7 @@ All inputs are **optional**. If not set, sensible defaults will be used.
| `token` | The token that the action will use to create and update the pull request. See [token](#token). | `GITHUB_TOKEN` |
| `branch-token` | The token that the action will use to create and update the branch. See [branch-token](#branch-token). | Defaults to the value of `token` |
| `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. See [Add specific paths](#add-specific-paths). | If no paths are specified, all new and modified files are added. |
| `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. See [commit-message](#commit-message). | `[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 on github.com. | `github-actions[bot] <41898282+github-actions[bot]@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_id }}+${{ github.actor }}@users.noreply.github.com>` |
@@ -99,7 +99,7 @@ Other token options:
#### branch-token
The action first creates a branch, and then creates a pull request for the branch.
For some rare use cases it can be useful, or even necessary, to use different tokens for these operations.
For some rare use cases it can be useful, or even neccessary, to use different tokens for these operations.
It is not advisable to use this input unless you know you need to.
#### commit-message
@@ -131,7 +131,7 @@ If you want branches to be deleted immediately on merge then you should use GitH
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@v8
uses: peter-evans/create-pull-request@v7
env:
https_proxy: http://<proxy_address>:<port>
```
@@ -153,7 +153,7 @@ Note that in order to read the step outputs the action step must have an id.
```yml
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
- name: Check outputs
if: ${{ steps.cpr.outputs.pull-request-number }}
run: |
@@ -216,7 +216,7 @@ File changes that do not match one of the paths will be stashed and restored aft
```yml
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
add-paths: |
*.java
@@ -230,7 +230,7 @@ Note that the repository must be checked out on a branch with a remote, it won't
```yml
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Create commits
run: |
git config user.name 'Peter Evans'
@@ -243,9 +243,29 @@ Note that the repository must be checked out on a branch with a remote, it won't
- name: Uncommitted change
run: date +%s > report.txt
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
```
<!--
### 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.
```yml
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v7
- name: Create or Update Project Card
if: ${{ steps.cpr.outputs.pull-request-number }}
uses: peter-evans/create-or-update-project-card@v2
with:
project-name: My project
column-name: My column
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.
@@ -263,14 +283,14 @@ jobs:
createPullRequest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Make changes to pull request
run: date +%s > report.txt
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT }}
commit-message: Update report
+14 -25
View File
@@ -250,15 +250,11 @@ describe('create-or-update-branch tests', () => {
expect(branchCommits.length).toEqual(1)
expect(branchCommits[0].subject).toEqual('Test changes')
expect(branchCommits[0].changes.length).toEqual(3)
expect(branchCommits[0].changes[0].mode).toEqual('100755')
expect(branchCommits[0].changes[0].path).toEqual(UNTRACKED_EXE_FILE)
expect(branchCommits[0].changes[0].status).toEqual('A')
expect(branchCommits[0].changes[1].mode).toEqual('100644')
expect(branchCommits[0].changes[1].path).toEqual(TRACKED_FILE)
expect(branchCommits[0].changes[1].status).toEqual('M')
expect(branchCommits[0].changes[2].mode).toEqual('100644')
expect(branchCommits[0].changes[2].path).toEqual(UNTRACKED_FILE)
expect(branchCommits[0].changes[2].status).toEqual('A')
expect(branchCommits[0].changes).toEqual([
{mode: '100755', path: UNTRACKED_EXE_FILE, status: 'A'},
{mode: '100644', path: TRACKED_FILE, status: 'M'},
{mode: '100644', path: UNTRACKED_FILE, status: 'A'}
])
})
it('tests buildBranchCommits with addition and deletion', async () => {
@@ -276,15 +272,11 @@ describe('create-or-update-branch tests', () => {
expect(branchCommits.length).toEqual(1)
expect(branchCommits[0].subject).toEqual('Test changes')
expect(branchCommits[0].changes.length).toEqual(3)
expect(branchCommits[0].changes[0].mode).toEqual('100644')
expect(branchCommits[0].changes[0].path).toEqual(TRACKED_FILE)
expect(branchCommits[0].changes[0].status).toEqual('D')
expect(branchCommits[0].changes[1].mode).toEqual('100644')
expect(branchCommits[0].changes[1].path).toEqual(UNTRACKED_FILE)
expect(branchCommits[0].changes[1].status).toEqual('A')
expect(branchCommits[0].changes[2].mode).toEqual('100644')
expect(branchCommits[0].changes[2].path).toEqual(TRACKED_FILE_NEW_PATH)
expect(branchCommits[0].changes[2].status).toEqual('A')
expect(branchCommits[0].changes).toEqual([
{mode: '100644', path: TRACKED_FILE, status: 'D'},
{mode: '100644', path: UNTRACKED_FILE, status: 'A'},
{mode: '100644', path: TRACKED_FILE_NEW_PATH, status: 'A'}
])
})
it('tests buildBranchCommits with multiple commits', async () => {
@@ -302,13 +294,10 @@ describe('create-or-update-branch tests', () => {
expect(branchCommits[i].subject).toEqual(`Test changes ${i}`)
expect(branchCommits[i].changes.length).toEqual(2)
const untrackedFileStatus = i == 0 ? 'A' : 'M'
expect(branchCommits[i].changes[0].mode).toEqual('100644')
expect(branchCommits[i].changes[0].path).toEqual(TRACKED_FILE)
expect(branchCommits[i].changes[0].status).toEqual('M')
expect(branchCommits[i].changes[1].mode).toEqual('100644')
expect(branchCommits[i].changes[1].path).toEqual(UNTRACKED_FILE)
expect(branchCommits[i].changes[1].status).toEqual(untrackedFileStatus)
expect(branchCommits[i].changes).toEqual([
{mode: '100644', path: TRACKED_FILE, status: 'M'},
{mode: '100644', path: UNTRACKED_FILE, status: untrackedFileStatus}
])
}
})
+3 -6
View File
@@ -19,17 +19,14 @@ git clone git://127.0.0.1/repos/test-base.git /git/local/repos/test-base
cd /git/local/repos/test-base
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
echo "#test-base" > README→TEMP.md
echo "#test-base" > README.md
git add .
git commit -m "initial commit"
git commit --allow-empty -m "empty commit for tests"
echo "#test-base :sparkles:" > README→TEMP.md
echo "#test-base :sparkles:" > README.md
git add .
git commit -m "add sparkles" -m "Change description:
- updates README→TEMP.md to add sparkles to the title"
mv README→TEMP.md README.md
git add .
git commit -m "rename readme"
- updates README.md to add sparkles to the title"
git push -u
git log -1 --pretty=oneline
git config --global --unset user.email
+10 -21
View File
@@ -11,16 +11,15 @@ describe('git-command-manager integration tests', () => {
})
it('tests getCommit', async () => {
const initialCommit = await git.getCommit('HEAD^^^')
const emptyCommit = await git.getCommit('HEAD^^')
const modifiedCommit = await git.getCommit('HEAD^')
const initialCommit = await git.getCommit('HEAD^^')
const emptyCommit = await git.getCommit('HEAD^')
const headCommit = await git.getCommit('HEAD')
expect(initialCommit.subject).toEqual('initial commit')
expect(initialCommit.signed).toBeFalsy()
expect(initialCommit.changes[0].mode).toEqual('100644')
expect(initialCommit.changes[0].status).toEqual('A')
expect(initialCommit.changes[0].path).toEqual('README→TEMP.md') // filename contains unicode
expect(initialCommit.changes).toEqual([
{mode: '100644', status: 'A', path: 'README.md'}
])
expect(emptyCommit.subject).toEqual('empty commit for tests')
expect(emptyCommit.tree).toEqual(initialCommit.tree) // empty commits have no tree and reference the parent's
@@ -28,21 +27,11 @@ describe('git-command-manager integration tests', () => {
expect(emptyCommit.signed).toBeFalsy()
expect(emptyCommit.changes).toEqual([])
expect(modifiedCommit.subject).toEqual('add sparkles')
expect(modifiedCommit.parents[0]).toEqual(emptyCommit.sha)
expect(modifiedCommit.signed).toBeFalsy()
expect(modifiedCommit.changes[0].mode).toEqual('100644')
expect(modifiedCommit.changes[0].status).toEqual('M')
expect(modifiedCommit.changes[0].path).toEqual('README→TEMP.md')
expect(headCommit.subject).toEqual('rename readme')
expect(headCommit.parents[0]).toEqual(modifiedCommit.sha)
expect(headCommit.subject).toEqual('add sparkles')
expect(headCommit.parents[0]).toEqual(emptyCommit.sha)
expect(headCommit.signed).toBeFalsy()
expect(headCommit.changes[0].mode).toEqual('100644')
expect(headCommit.changes[0].status).toEqual('A')
expect(headCommit.changes[0].path).toEqual('README.md')
expect(headCommit.changes[1].mode).toEqual('100644')
expect(headCommit.changes[1].status).toEqual('D')
expect(headCommit.changes[1].path).toEqual('README→TEMP.md')
expect(headCommit.changes).toEqual([
{mode: '100644', status: 'M', path: 'README.md'}
])
})
})
+5 -82
View File
@@ -1,7 +1,5 @@
import {GitCommandManager} from '../lib/git-command-manager'
import {GitConfigHelper} from '../lib/git-config-helper'
import * as fs from 'fs'
import * as path from 'path'
const REPO_PATH = '/git/local/repos/test-base'
@@ -9,104 +7,29 @@ const extraheaderConfigKey = 'http.https://127.0.0.1/.extraheader'
describe('git-config-helper integration tests', () => {
let git: GitCommandManager
let originalRunnerTemp: string | undefined
beforeAll(async () => {
git = await GitCommandManager.create(REPO_PATH)
})
beforeEach(async () => {
// Save original RUNNER_TEMP
originalRunnerTemp = process.env['RUNNER_TEMP']
// Create a temp directory for tests
const tempDir = await fs.promises.mkdtemp('/tmp/cpr-test-')
process.env['RUNNER_TEMP'] = tempDir
process.env['GITHUB_WORKSPACE'] = REPO_PATH
})
afterEach(async () => {
// Clean up RUNNER_TEMP
const runnerTemp = process.env['RUNNER_TEMP']
if (runnerTemp && runnerTemp.startsWith('/tmp/cpr-test-')) {
await fs.promises.rm(runnerTemp, {recursive: true, force: true})
}
// Restore original RUNNER_TEMP
if (originalRunnerTemp !== undefined) {
process.env['RUNNER_TEMP'] = originalRunnerTemp
} else {
delete process.env['RUNNER_TEMP']
}
})
it('tests save and restore with no persisted auth', async () => {
const gitConfigHelper = await GitConfigHelper.create(git)
await gitConfigHelper.close()
})
it('tests configure and removal of auth using credentials file', async () => {
const runnerTemp = process.env['RUNNER_TEMP']!
it('tests configure and removal of auth', async () => {
const gitConfigHelper = await GitConfigHelper.create(git)
await gitConfigHelper.configureToken('github-token')
// Verify credentials file was created in RUNNER_TEMP
const files = await fs.promises.readdir(runnerTemp)
const credentialsFiles = files.filter(
f => f.startsWith('git-credentials-') && f.endsWith('.config')
)
expect(credentialsFiles.length).toBe(1)
// Verify credentials file contains the auth token
const credentialsPath = path.join(runnerTemp, credentialsFiles[0])
const credentialsContent = await fs.promises.readFile(
credentialsPath,
'utf8'
)
expect(credentialsContent).toContain(
expect(await git.configExists(extraheaderConfigKey)).toBeTruthy()
expect(await git.getConfigValue(extraheaderConfigKey)).toEqual(
'AUTHORIZATION: basic eC1hY2Nlc3MtdG9rZW46Z2l0aHViLXRva2Vu'
)
// Verify includeIf entries were added to local config
const includeIfKeys = await git.tryGetConfigKeys('^includeIf\\.gitdir:')
expect(includeIfKeys.length).toBeGreaterThan(0)
// Count credential includes pointing to this action's credentials file
let credentialIncludesForThisAction = 0
for (const key of includeIfKeys) {
const values = await git.tryGetConfigValues(key)
for (const value of values) {
if (value === credentialsPath) {
credentialIncludesForThisAction++
}
}
}
expect(credentialIncludesForThisAction).toBeGreaterThan(0)
await gitConfigHelper.close()
// Verify credentials file was removed
const filesAfter = await fs.promises.readdir(runnerTemp)
const credentialsFilesAfter = filesAfter.filter(
f => f.startsWith('git-credentials-') && f.endsWith('.config')
)
expect(credentialsFilesAfter.length).toBe(0)
// Verify includeIf entries pointing to our specific credentials file were removed
const includeIfKeysAfter = await git.tryGetConfigKeys(
'^includeIf\\.gitdir:'
)
let credentialIncludesForThisActionAfter = 0
for (const key of includeIfKeysAfter) {
const values = await git.tryGetConfigValues(key)
for (const value of values) {
if (value === credentialsPath) {
credentialIncludesForThisActionAfter++
}
}
}
expect(credentialIncludesForThisActionAfter).toBe(0)
expect(await git.configExists(extraheaderConfigKey)).toBeFalsy()
})
it('tests save and restore of persisted auth (old-style)', async () => {
it('tests save and restore of persisted auth', async () => {
const extraheaderConfigValue = 'AUTHORIZATION: basic ***persisted-auth***'
await git.config(extraheaderConfigKey, extraheaderConfigValue)
+1 -1
View File
@@ -94,7 +94,7 @@ outputs:
pull-request-branch:
description: 'The pull request branch name'
runs:
using: 'node24'
using: 'node20'
main: 'dist/index.js'
branding:
icon: 'git-pull-request'
-16
View File
@@ -1,16 +0,0 @@
"use strict";
exports.id = 790;
exports.ids = [790];
exports.modules = {
/***/ 790:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
var y=Object.defineProperty;var c=(R,o)=>y(R,"name",{value:o,configurable:!0});const node=__webpack_require__(117);__webpack_require__(7067),__webpack_require__(4708),__webpack_require__(8522),__webpack_require__(7075),__webpack_require__(4573),__webpack_require__(7975),__webpack_require__(3465),__webpack_require__(3136),__webpack_require__(7030),__webpack_require__(3024),__webpack_require__(6760);let s=0;const S={START_BOUNDARY:s++,HEADER_FIELD_START:s++,HEADER_FIELD:s++,HEADER_VALUE_START:s++,HEADER_VALUE:s++,HEADER_VALUE_ALMOST_DONE:s++,HEADERS_ALMOST_DONE:s++,PART_DATA_START:s++,PART_DATA:s++,END:s++};let f=1;const F={PART_BOUNDARY:f,LAST_BOUNDARY:f*=2},LF=10,CR=13,SPACE=32,HYPHEN=45,COLON=58,A=97,Z=122,lower=c(R=>R|32,"lower"),noop=c(()=>{},"noop"),g=class g{constructor(o){this.index=0,this.flags=0,this.onHeaderEnd=noop,this.onHeaderField=noop,this.onHeadersEnd=noop,this.onHeaderValue=noop,this.onPartBegin=noop,this.onPartData=noop,this.onPartEnd=noop,this.boundaryChars={},o=`\r
--`+o;const t=new Uint8Array(o.length);for(let n=0;n<o.length;n++)t[n]=o.charCodeAt(n),this.boundaryChars[t[n]]=!0;this.boundary=t,this.lookbehind=new Uint8Array(this.boundary.length+8),this.state=S.START_BOUNDARY}write(o){let t=0;const n=o.length;let E=this.index,{lookbehind:l,boundary:h,boundaryChars:H,index:e,state:a,flags:d}=this;const b=this.boundary.length,m=b-1,O=o.length;let r,P;const u=c(D=>{this[D+"Mark"]=t},"mark"),i=c(D=>{delete this[D+"Mark"]},"clear"),T=c((D,p,_,N)=>{(p===void 0||p!==_)&&this[D](N&&N.subarray(p,_))},"callback"),L=c((D,p)=>{const _=D+"Mark";_ in this&&(p?(T(D,this[_],t,o),delete this[_]):(T(D,this[_],o.length,o),this[_]=0))},"dataCallback");for(t=0;t<n;t++)switch(r=o[t],a){case S.START_BOUNDARY:if(e===h.length-2){if(r===HYPHEN)d|=F.LAST_BOUNDARY;else if(r!==CR)return;e++;break}else if(e-1===h.length-2){if(d&F.LAST_BOUNDARY&&r===HYPHEN)a=S.END,d=0;else if(!(d&F.LAST_BOUNDARY)&&r===LF)e=0,T("onPartBegin"),a=S.HEADER_FIELD_START;else return;break}r!==h[e+2]&&(e=-2),r===h[e+2]&&e++;break;case S.HEADER_FIELD_START:a=S.HEADER_FIELD,u("onHeaderField"),e=0;case S.HEADER_FIELD:if(r===CR){i("onHeaderField"),a=S.HEADERS_ALMOST_DONE;break}if(e++,r===HYPHEN)break;if(r===COLON){if(e===1)return;L("onHeaderField",!0),a=S.HEADER_VALUE_START;break}if(P=lower(r),P<A||P>Z)return;break;case S.HEADER_VALUE_START:if(r===SPACE)break;u("onHeaderValue"),a=S.HEADER_VALUE;case S.HEADER_VALUE:r===CR&&(L("onHeaderValue",!0),T("onHeaderEnd"),a=S.HEADER_VALUE_ALMOST_DONE);break;case S.HEADER_VALUE_ALMOST_DONE:if(r!==LF)return;a=S.HEADER_FIELD_START;break;case S.HEADERS_ALMOST_DONE:if(r!==LF)return;T("onHeadersEnd"),a=S.PART_DATA_START;break;case S.PART_DATA_START:a=S.PART_DATA,u("onPartData");case S.PART_DATA:if(E=e,e===0){for(t+=m;t<O&&!(o[t]in H);)t+=b;t-=m,r=o[t]}if(e<h.length)h[e]===r?(e===0&&L("onPartData",!0),e++):e=0;else if(e===h.length)e++,r===CR?d|=F.PART_BOUNDARY:r===HYPHEN?d|=F.LAST_BOUNDARY:e=0;else if(e-1===h.length)if(d&F.PART_BOUNDARY){if(e=0,r===LF){d&=~F.PART_BOUNDARY,T("onPartEnd"),T("onPartBegin"),a=S.HEADER_FIELD_START;break}}else d&F.LAST_BOUNDARY&&r===HYPHEN?(T("onPartEnd"),a=S.END,d=0):e=0;if(e>0)l[e-1]=r;else if(E>0){const D=new Uint8Array(l.buffer,l.byteOffset,l.byteLength);T("onPartData",0,E,D),E=0,u("onPartData"),t--}break;case S.END:break;default:throw new Error(`Unexpected state entered: ${a}`)}L("onHeaderField"),L("onHeaderValue"),L("onPartData"),this.index=e,this.state=a,this.flags=d}end(){if(this.state===S.HEADER_FIELD_START&&this.index===0||this.state===S.PART_DATA&&this.index===this.boundary.length)this.onPartEnd();else if(this.state!==S.END)throw new Error("MultipartParser.end(): stream ended unexpectedly")}};c(g,"MultipartParser");let MultipartParser=g;function _fileName(R){const o=R.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);if(!o)return;const t=o[2]||o[3]||"";let n=t.slice(t.lastIndexOf("\\")+1);return n=n.replace(/%22/g,'"'),n=n.replace(/&#(\d{4});/g,(E,l)=>String.fromCharCode(l)),n}c(_fileName,"_fileName");async function toFormData(R,o){if(!/multipart/i.test(o))throw new TypeError("Failed to fetch");const t=o.match(/boundary=(?:"([^"]+)"|([^;]+))/i);if(!t)throw new TypeError("no or bad content-type header, no multipart boundary");const n=new MultipartParser(t[1]||t[2]);let E,l,h,H,e,a;const d=[],b=new node.FormData,m=c(i=>{h+=u.decode(i,{stream:!0})},"onPartData"),O=c(i=>{d.push(i)},"appendToFile"),r=c(()=>{const i=new node.File(d,a,{type:e});b.append(H,i)},"appendFileToFormData"),P=c(()=>{b.append(H,h)},"appendEntryToFormData"),u=new TextDecoder("utf-8");u.decode(),n.onPartBegin=function(){n.onPartData=m,n.onPartEnd=P,E="",l="",h="",H="",e="",a=null,d.length=0},n.onHeaderField=function(i){E+=u.decode(i,{stream:!0})},n.onHeaderValue=function(i){l+=u.decode(i,{stream:!0})},n.onHeaderEnd=function(){if(l+=u.decode(),E=E.toLowerCase(),E==="content-disposition"){const i=l.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);i&&(H=i[2]||i[3]||""),a=_fileName(l),a&&(n.onPartData=O,n.onPartEnd=r)}else E==="content-type"&&(e=l);l="",E=""};for await(const i of R)n.write(i);return n.end(),b}c(toFormData,"toFormData"),exports.toFormData=toFormData;
/***/ })
};
;
+27987 -2592
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -37,7 +37,7 @@ So the straightforward solution is to just not install them during the workflow
- If hooks are automatically enabled by a framework, use an option provided by the framework to disable them. For example, for Husky users, they can be disabled with the `--ignore-scripts` flag, or by setting the `HUSKY` environment variable when the action runs.
```yml
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
env:
HUSKY: '0'
```
+36 -37
View File
@@ -25,7 +25,7 @@ This document covers terminology, how the action works, general usage guidelines
## Terminology
[Pull requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#about-pull-requests) are proposed changes to a repository branch that can be reviewed by a repository's collaborators before being accepted or rejected.
[Pull requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#about-pull-requests) are proposed changes to a repository branch that can be reviewed by a repository's collaborators before being accepted or rejected.
A pull request references two branches:
@@ -40,7 +40,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.
```yml
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
ref: develop
```
@@ -77,7 +77,7 @@ jobs:
example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
```
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.
@@ -92,7 +92,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.
```yml
- uses: peter-evans/create-pull-request@v8
- uses: peter-evans/create-pull-request@v7
with:
base: ${{ github.head_ref }}
```
@@ -100,7 +100,7 @@ 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.
```yml
- uses: peter-evans/create-pull-request@v8
- uses: peter-evans/create-pull-request@v7
with:
base: main
```
@@ -150,7 +150,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).
- Create draft pull requests by setting the `draft: always-true` input, and configure your workflow to trigger `ready_for_review` in `on: pull_request`. The workflow will run when users manually click the "Ready for review" button on the draft pull requests. If the pull request is updated by the action, the `always-true` mode ensures that the pull request will be converted back to a draft.
- Create draft pull requests by setting the `draft: always-true` input, and configure your workflow to trigger `on: ready_for_review`. The workflow will run when users manually click the "Ready for review" button on the draft pull requests. If the pull request is updated by the action, the `always-true` mode ensures that the pull request will be converted back to a draft.
- Use a [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). It's advisable to use 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, rather than creating a PAT on a personal user account. 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.
@@ -179,14 +179,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, the `GITHUB_TOKEN` will not work and one of the other [token options](../README.md#token) must be used.
```yml
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
token: ${{ secrets.PAT }}
repository: owner/repo
# Make changes to pull request here
- uses: peter-evans/create-pull-request@v8
- uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT }}
```
@@ -197,9 +197,8 @@ Checking out a branch from a different repository from where the workflow is exe
Allowing the action to push with a configured deploy key will trigger `on: push` workflows. This makes it an alternative to using a PAT to trigger checks for pull requests.
> [!NOTE]
> - You cannot use deploy keys alone to [create a pull request in a remote repository](#creating-pull-requests-in-a-remote-repository) because then using a PAT would become a requirement.
> You cannot use deploy keys alone to [create a pull request in a remote repository](#creating-pull-requests-in-a-remote-repository) because then using a PAT would become a requirement.
> This method only makes sense if creating a pull request in the repository where the workflow is running.
> - You cannot use deploy keys with [commit signature verification for bots](#commit-signature-verification-for-bots) (`sign-commits: true`).
How to use SSH (deploy keys) with create-pull-request action:
@@ -210,14 +209,14 @@ How to use SSH (deploy keys) with create-pull-request action:
```yml
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
```
### Push pull request branches to a fork
@@ -232,17 +231,17 @@ It will use their own fork to push code and create the pull request.
1. Create a new GitHub user and login.
2. Fork the repository that you will be creating pull requests in.
3. Create a Classic [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with `repo` and `workflow` scopes.
3. Create a Classic [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with `repo` scope.
4. Logout and log back into your main user account.
5. Add a secret to your repository containing the above PAT.
6. As shown in the following example workflow, set the `push-to-fork` input to the full repository name of the fork.
```yaml
- uses: actions/checkout@v6
- uses: actions/checkout@v4
# Make changes to pull request here
- uses: peter-evans/create-pull-request@v8
- uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.MACHINE_USER_PAT }}
push-to-fork: machine-user/fork-of-repository
@@ -272,7 +271,7 @@ The `token` input will then default to the repository's `GITHUB_TOKEN`, which wi
The following is an example of pushing to a fork using GitHub App tokens.
```yaml
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v1
id: generate-token
with:
app-id: ${{ secrets.APP_ID }}
@@ -280,12 +279,12 @@ The following is an example of pushing to a fork using GitHub App tokens.
owner: owner
repositories: fork-of-repo
- uses: actions/checkout@v6
- uses: actions/checkout@v4
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
branch-token: ${{ steps.generate-token.outputs.token }}
push-to-fork: owner/fork-of-repo
@@ -304,7 +303,7 @@ GitHub App generated tokens can be configured with fine-grained permissions and
- Uncheck `Active` under `Webhook`. You do not need to enter a `Webhook URL`.
- Under `Repository permissions: Contents` select `Access: Read & write`.
- Under `Repository permissions: Pull requests` select `Access: Read & write`.
- Under `Repository permissions: Workflows` select `Access: Read & write`.
- Under `Repository permissions: Workflows` select `Access: Read-only`.
- **NOTE**: Only needed if pull requests could contain changes to Actions workflows.
- Under `Organization permissions: Members` select `Access: Read-only`.
- **NOTE**: Only needed if you would like add teams as reviewers to PRs.
@@ -319,18 +318,18 @@ GitHub App generated tokens can be configured with fine-grained permissions and
```yaml
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v1
id: generate-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
- uses: actions/checkout@v4
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.generate-token.outputs.token }}
```
@@ -342,7 +341,7 @@ For this case a token must be generated from the GitHub App installation of the
In the following example, a pull request is being created in remote repo `owner/repo`.
```yaml
steps:
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v1
id: generate-token
with:
app-id: ${{ secrets.APP_ID }}
@@ -350,7 +349,7 @@ In the following example, a pull request is being created in remote repo `owner/
owner: owner
repositories: repo
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
token: ${{ steps.generate-token.outputs.token }} # necessary if the repo is private
repository: owner/repo
@@ -358,7 +357,7 @@ In the following example, a pull request is being created in remote repo `owner/
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.generate-token.outputs.token }}
```
@@ -374,7 +373,7 @@ The action supports two methods to sign commits, [commit signature verification
The action can sign commits as `github-actions[bot]` when using the repository's default `GITHUB_TOKEN`, or your own bot when using [GitHub App tokens](#authenticating-with-github-app-generated-tokens).
> [!IMPORTANT]
> [!IMPORTANT]
> - When setting `sign-commits: true` the action will ignore the `committer` and `author` inputs.
> - If you attempt to use a [Personal Access Token (PAT)](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) the action will create the pull request, but commits will *not* be signed. Commit signing is only supported with bot generated tokens.
> - The GitHub API has a 40MiB limit when creating git blobs. An error will be raised if there are files in the pull request larger than this. If you hit this limit, use [GPG commit signature verification](#gpg-commit-signature-verification) instead.
@@ -382,12 +381,12 @@ The action can sign commits as `github-actions[bot]` when using the repository's
In this example the `token` input is not supplied, so the action will use the repository's default `GITHUB_TOKEN`. This will sign commits as `github-actions[bot]`.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
sign-commits: true
```
@@ -395,9 +394,9 @@ In this example the `token` input is not supplied, so the action will use the re
In this example, the `token` input is generated using a GitHub App. This will sign commits as `<application-name>[bot]`.
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: actions/create-github-app-token@v2
- uses: actions/create-github-app-token@v1
id: generate-token
with:
app-id: ${{ secrets.APP_ID }}
@@ -406,7 +405,7 @@ In this example, the `token` input is generated using a GitHub App. This will si
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ steps.generate-token.outputs.token }}
sign-commits: true
@@ -437,7 +436,7 @@ The action can use GPG to sign commits with a GPG key that you generate yourself
```yaml
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: crazy-max/ghaction-import-gpg@v5
with:
@@ -449,7 +448,7 @@ The action can use GPG to sign commits with a GPG key that you generate yourself
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT }}
committer: example <email@example.com>
@@ -474,12 +473,12 @@ jobs:
- name: Install dependencies
run: apk --no-cache add git
- uses: actions/checkout@v6
- uses: actions/checkout@v4
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
```
**Ubuntu container example:**
@@ -497,10 +496,10 @@ jobs:
add-apt-repository -y ppa:git-core/ppa
apt-get install -y git
- uses: actions/checkout@v6
- uses: actions/checkout@v4
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
```
+27 -54
View File
@@ -22,7 +22,7 @@
- [Dynamic configuration using variables](#dynamic-configuration-using-variables)
- [Using a markdown template](#using-a-markdown-template)
- [Debugging GitHub Actions](#debugging-github-actions)
- [Show an annotation message for a created pull request](#show-an-annotation-message-for-a-created-pull-request)
## Use case: Create a pull request to update X on push
@@ -42,14 +42,14 @@ jobs:
updateAuthors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Update AUTHORS
run: |
git log --format='%aN <%aE>%n%cN <%cE>' | sort -u > AUTHORS
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
commit-message: update authors
title: Update AUTHORS
@@ -73,7 +73,7 @@ jobs:
productionPromotion:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
ref: production
- name: Reset promotion branch
@@ -81,7 +81,7 @@ jobs:
git fetch origin main:main
git reset --hard main
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
branch: production-promotion
```
@@ -106,7 +106,7 @@ jobs:
updateChangelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Update Changelog
@@ -116,7 +116,7 @@ jobs:
./git-chglog -o CHANGELOG.md
rm git-chglog
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
commit-message: update changelog
title: Update Changelog
@@ -144,7 +144,7 @@ jobs:
update-dep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: '16.x'
@@ -153,7 +153,7 @@ jobs:
npx -p npm-check-updates ncu -u
npm install
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT }}
commit-message: Update dependencies
@@ -180,7 +180,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 16.x
@@ -204,7 +204,7 @@ jobs:
update-dep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: actions/setup-java@v2
with:
distribution: 'temurin'
@@ -214,7 +214,7 @@ jobs:
- name: Perform dependency resolution and write new lockfiles
run: ./gradlew dependencies --write-locks
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT }}
commit-message: Update dependencies
@@ -242,14 +242,14 @@ jobs:
update-dep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Update dependencies
run: |
cargo install cargo-edit
cargo update
cargo upgrade --to-lockfile
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT }}
commit-message: Update dependencies
@@ -277,7 +277,7 @@ jobs:
updateSwagger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Get Latest Swagger UI Release
id: swagger-ui
run: |
@@ -307,7 +307,7 @@ jobs:
# Update current release
echo ${{ steps.swagger-ui.outputs.release_tag }} > swagger-ui.version
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
commit-message: Update swagger-ui to ${{ steps.swagger-ui.outputs.release_tag }}
title: Update SwaggerUI to ${{ steps.swagger-ui.outputs.release_tag }}
@@ -324,7 +324,7 @@ jobs:
### Keep a fork up-to-date with its upstream
This example is designed to be run in a separate 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.
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`.
@@ -342,7 +342,7 @@ jobs:
updateFork:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
repository: fork-owner/repo
- name: Reset the default branch with upstream changes
@@ -351,7 +351,7 @@ jobs:
git fetch upstream main:upstream-main
git reset --hard upstream-main
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.PAT }}
branch: upstream-changes
@@ -370,7 +370,7 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Download website
run: |
wget \
@@ -384,7 +384,7 @@ jobs:
--domains quotes.toscrape.com \
http://quotes.toscrape.com/
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
commit-message: update local website copy
title: Automated Updates to Local Website Copy
@@ -466,7 +466,7 @@ jobs:
if: startsWith(github.head_ref, 'autopep8-patches') == false && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- name: autopep8
@@ -481,7 +481,7 @@ jobs:
echo "branch-name=$branch-name" >> $GITHUB_OUTPUT
- name: Create Pull Request
if: steps.autopep8.outputs.exit-code == 2
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
commit-message: autopep8 action fixes
title: Fixes by autopep8 action
@@ -515,13 +515,13 @@ jobs:
if: startsWith(github.ref, 'refs/heads/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
...
someOtherJob:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
...
```
@@ -540,7 +540,7 @@ Note that the step where output variables are defined must have an id.
echo "pr_title=$pr_title" >> $GITHUB_OUTPUT
echo "pr_body=$pr_body" >> $GITHUB_OUTPUT
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
title: ${{ steps.vars.outputs.pr_title }}
body: ${{ steps.vars.outputs.pr_body }}
@@ -566,7 +566,7 @@ The template is rendered using the [render-template](https://github.com/chuhlomi
bar: that
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
body: ${{ steps.template.outputs.result }}
```
@@ -612,30 +612,3 @@ To enable step debug logging set the secret `ACTIONS_STEP_DEBUG` to `true` in th
MATRIX_CONTEXT: ${{ toJson(matrix) }}
run: echo "$MATRIX_CONTEXT"
```
### Show an annotation message for a created pull request
Showing an annotation message for a created or updated pull request allows you to confirm the pull request easily, such as by visiting the link. This can be achieved by adding a step that uses the [`notice` workflow command](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions?tool=bash#setting-a-notice-message).
For example:
```yml
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v8
- name: Show message for created Pull Request
if: ${{ steps.cpr.outputs.pull-request-url && steps.cpr.outputs.pull-request-operation != 'none' }}
shell: bash
env:
PR_URL: ${{ steps.cpr.outputs.pull-request-url }}
PR_OPERATION: ${{ steps.cpr.outputs.pull-request-operation }}
run: |
echo "::notice::${PR_URL} was ${PR_OPERATION}."
```
In this example, when a pull request is created, you will be able to see the following message on an action run page (e.g., `/actions/runs/12812393039`):
```
https://github.com/peter-evans/create-pull-request/pull/1 was created.
```
-11
View File
@@ -1,14 +1,3 @@
## Updating from `v7` to `v8`
### Behaviour changes
- If using self-hosted runners or GitHub Enterprise Server, there are minimum requirements for `v8` to run. See "What's new" below for details.
### What's new
- Updated runtime to Node.js 24
- The action now requires a minimum version of [v2.327.1](https://github.com/actions/runner/releases/tag/v2.327.1) for the Actions runner. Update self-hosted runners to v2.327.1 or later to ensure compatibility.
## Updating from `v6` to `v7`
### Behaviour changes
+780 -1555
View File
File diff suppressed because it is too large Load Diff
+20 -24
View File
@@ -1,12 +1,9 @@
{
"name": "create-pull-request",
"version": "8.0.0",
"version": "7.0.0",
"private": true,
"description": "Creates a pull request for changes to your repository in the actions workspace",
"main": "lib/main.js",
"engines": {
"node": ">=24.4.0"
},
"scripts": {
"build": "tsc && ncc build",
"format": "prettier --write '**/*.ts'",
@@ -32,36 +29,35 @@
},
"homepage": "https://github.com/peter-evans/create-pull-request",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"@octokit/core": "^6.1.6",
"@octokit/plugin-paginate-rest": "^11.6.0",
"@octokit/plugin-rest-endpoint-methods": "^13.5.0",
"@octokit/plugin-retry": "^7.2.1",
"@octokit/plugin-throttling": "^9.6.1",
"node-fetch-native": "^1.6.7",
"p-limit": "^6.2.0",
"@octokit/core": "^6.1.2",
"@octokit/plugin-paginate-rest": "^11.3.3",
"@octokit/plugin-rest-endpoint-methods": "^13.2.4",
"@octokit/plugin-throttling": "^9.3.1",
"p-limit": "^6.1.0",
"proxy-from-env": "^1.1.0",
"undici": "^6.19.8",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^18.19.130",
"@types/jest": "^29.5.12",
"@types/node": "^18.19.48",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"@vercel/ncc": "^0.38.4",
"eslint": "^8.57.1",
"eslint-import-resolver-typescript": "^3.10.1",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.7.0",
"jest-circus": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"js-yaml": "^4.1.1",
"prettier": "^3.7.4",
"ts-jest": "^29.4.6",
"typescript": "^5.9.3",
"undici": "^6.23.0"
"js-yaml": "^4.1.0",
"prettier": "^3.3.3",
"ts-jest": "^29.2.5",
"typescript": "^5.5.4"
}
}
+2 -2
View File
@@ -19,7 +19,7 @@ export async function getWorkingBaseAndType(
): Promise<[string, WorkingBaseType]> {
const symbolicRefResult = await git.exec(
['symbolic-ref', 'HEAD', '--short'],
{allowAllExitCodes: true}
true
)
if (symbolicRefResult.exitCode == 0) {
// A ref is checked out
@@ -200,7 +200,7 @@ export async function createOrUpdateBranch(
} else {
aopts.push('-A')
}
await git.exec(aopts, {allowAllExitCodes: true})
await git.exec(aopts, true)
const popts = ['-m', commitMessage]
if (signoff) {
popts.push('--signoff')
+6 -15
View File
@@ -121,20 +121,12 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
`The 'base' and 'branch' for a pull request must be different branches. Unable to continue.`
)
}
if (utils.isSelfHosted()) {
// 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
try {
await git.exec(['remote', 'prune', branchRemoteName])
} catch (error) {
core.warning(
`Failed to prune remote '${branchRemoteName}': ${(error as Error).message}`
)
}
}
// 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()
// Apply the branch suffix if set
@@ -219,7 +211,6 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
const stashed = await git.stashPush(['--include-untracked'])
await git.checkout(inputs.branch)
const pushSignedCommitsResult = await ghBranch.pushSignedCommits(
git,
result.branchCommits,
result.baseCommit,
repoPath,
+27 -158
View File
@@ -2,7 +2,6 @@ import * as exec from '@actions/exec'
import * as io from '@actions/io'
import * as utils from './utils'
import * as path from 'path'
import stream, {Writable} from 'stream'
const tagsRefSpec = '+refs/tags/*:refs/tags/*'
@@ -15,19 +14,12 @@ export type Commit = {
body: string
changes: {
mode: string
dstSha: string
status: 'A' | 'M' | 'D'
path: string
}[]
unparsedChanges: string[]
}
export type ExecOpts = {
allowAllExitCodes?: boolean
encoding?: 'utf8' | 'base64'
suppressGitCmdOutput?: boolean
}
export class GitCommandManager {
private gitPath: string
private workingDirectory: string
@@ -73,7 +65,7 @@ export class GitCommandManager {
args.push(...options)
}
return await this.exec(args, {allowAllExitCodes: allowAllExitCodes})
return await this.exec(args, allowAllExitCodes)
}
async commit(
@@ -89,22 +81,16 @@ export class GitCommandManager {
args.push(...options)
}
return await this.exec(args, {allowAllExitCodes: allowAllExitCodes})
return await this.exec(args, allowAllExitCodes)
}
async config(
configKey: string,
configValue: string,
globalConfig?: boolean,
add?: boolean,
configFile?: string
add?: boolean
): Promise<void> {
const args: string[] = ['config']
if (configFile) {
args.push('--file', configFile)
} else {
args.push(globalConfig ? '--global' : '--local')
}
const args: string[] = ['config', globalConfig ? '--global' : '--local']
if (add) {
args.push('--add')
}
@@ -126,7 +112,7 @@ export class GitCommandManager {
configKey,
configValue
],
{allowAllExitCodes: true}
true
)
return output.exitCode === 0
}
@@ -169,20 +155,13 @@ export class GitCommandManager {
async getCommit(ref: string): Promise<Commit> {
const endOfBody = '###EOB###'
const output = await this.exec(
[
'-c',
'core.quotePath=false',
'show',
'--raw',
'--cc',
'--no-renames',
'--no-abbrev',
`--format=%H%n%T%n%P%n%G?%n%s%n%b%n${endOfBody}`,
ref
],
{suppressGitCmdOutput: true}
)
const output = await this.exec([
'show',
'--raw',
'--cc',
`--format=%H%n%T%n%P%n%G?%n%s%n%b%n${endOfBody}`,
ref
])
const lines = output.stdout.split('\n')
const endOfBodyIndex = lines.lastIndexOf(endOfBody)
const detailLines = lines.slice(0, endOfBodyIndex)
@@ -197,14 +176,13 @@ export class GitCommandManager {
body: detailLines.slice(5, endOfBodyIndex).join('\n'),
changes: lines.slice(endOfBodyIndex + 2, -1).map(line => {
const change = line.match(
/^:(\d{6}) (\d{6}) \w{40} (\w{40}) ([AMD])\s+(.*)$/
/^:(\d{6}) (\d{6}) \w{7} \w{7} ([AMD])\s+(.*)$/
)
if (change) {
return {
mode: change[4] === 'D' ? change[1] : change[2],
dstSha: change[3],
status: change[4],
path: change[5]
mode: change[3] === 'D' ? change[1] : change[2],
status: change[3],
path: change[4]
}
} else {
unparsedChanges.push(line)
@@ -238,7 +216,7 @@ export class GitCommandManager {
if (options) {
args.push(...options)
}
const output = await this.exec(args, {allowAllExitCodes: true})
const output = await this.exec(args, true)
return output.exitCode === 1
}
@@ -294,15 +272,6 @@ export class GitCommandManager {
return output.stdout.trim()
}
async showFileAtRefBase64(ref: string, path: string): Promise<string> {
const args = ['show', `${ref}:${path}`]
const output = await this.exec(args, {
encoding: 'base64',
suppressGitCmdOutput: true
})
return output.stdout.trim()
}
async stashPush(options?: string[]): Promise<boolean> {
const args = ['stash', 'push']
if (options) {
@@ -351,76 +320,15 @@ export class GitCommandManager {
configKey,
configValue
],
{allowAllExitCodes: true}
true
)
return output.exitCode === 0
}
async tryConfigUnsetValue(
configKey: string,
configValue: string,
globalConfig?: boolean,
configFile?: string
): Promise<boolean> {
const args = ['config']
if (configFile) {
args.push('--file', configFile)
} else {
args.push(globalConfig ? '--global' : '--local')
}
args.push('--fixed-value', '--unset', configKey, configValue)
const output = await this.exec(args, {allowAllExitCodes: true})
return output.exitCode === 0
}
async tryGetConfigValues(
configKey: string,
globalConfig?: boolean,
configFile?: string
): Promise<string[]> {
const args = ['config']
if (configFile) {
args.push('--file', configFile)
} else {
args.push(globalConfig ? '--global' : '--local')
}
args.push('--get-all', configKey)
const output = await this.exec(args, {allowAllExitCodes: true})
if (output.exitCode !== 0) {
return []
}
return output.stdout
.trim()
.split('\n')
.filter(value => value.trim())
}
async tryGetConfigKeys(
pattern: string,
globalConfig?: boolean,
configFile?: string
): Promise<string[]> {
const args = ['config']
if (configFile) {
args.push('--file', configFile)
} else {
args.push(globalConfig ? '--global' : '--local')
}
args.push('--name-only', '--get-regexp', pattern)
const output = await this.exec(args, {allowAllExitCodes: true})
if (output.exitCode !== 0) {
return []
}
return output.stdout
.trim()
.split('\n')
.filter(key => key.trim())
}
async tryGetRemoteUrl(): Promise<string> {
const output = await this.exec(
['config', '--local', '--get', 'remote.origin.url'],
{allowAllExitCodes: true}
true
)
if (output.exitCode !== 0) {
@@ -435,30 +343,16 @@ export class GitCommandManager {
return stdout
}
async exec(
args: string[],
{
encoding = 'utf8',
allowAllExitCodes = false,
suppressGitCmdOutput = false
}: ExecOpts = {}
): Promise<GitOutput> {
async exec(args: string[], allowAllExitCodes = false): Promise<GitOutput> {
const result = new GitOutput()
if (process.env['CPR_SHOW_GIT_CMD_OUTPUT']) {
// debug mode overrides the suppressGitCmdOutput option
suppressGitCmdOutput = false
}
const env = {}
for (const key of Object.keys(process.env)) {
env[key] = process.env[key]
}
const stdout: Buffer[] = []
let stdoutLength = 0
const stderr: Buffer[] = []
let stderrLength = 0
const stdout: string[] = []
const stderr: string[] = []
const options = {
cwd: this.workingDirectory,
@@ -466,21 +360,17 @@ export class GitCommandManager {
ignoreReturnCode: allowAllExitCodes,
listeners: {
stdout: (data: Buffer) => {
stdout.push(data)
stdoutLength += data.length
stdout.push(data.toString())
},
stderr: (data: Buffer) => {
stderr.push(data)
stderrLength += data.length
stderr.push(data.toString())
}
},
outStream: outStreamHandler(process.stdout, suppressGitCmdOutput),
errStream: outStreamHandler(process.stderr, suppressGitCmdOutput)
}
}
result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options)
result.stdout = Buffer.concat(stdout, stdoutLength).toString(encoding)
result.stderr = Buffer.concat(stderr, stderrLength).toString(encoding)
result.stdout = stdout.join('')
result.stderr = stderr.join('')
return result
}
}
@@ -490,24 +380,3 @@ class GitOutput {
stderr = ''
exitCode = 0
}
const outStreamHandler = (
outStream: Writable,
suppressGitCmdOutput: boolean
): Writable => {
return new stream.Writable({
write(chunk, _, next) {
if (suppressGitCmdOutput) {
const lines = chunk.toString().trimEnd().split('\n')
for (const line of lines) {
if (line.startsWith('[command]')) {
outStream.write(`${line}\n`)
}
}
} else {
outStream.write(chunk)
}
next()
}
})
}
+24 -194
View File
@@ -4,7 +4,6 @@ import {GitCommandManager} from './git-command-manager'
import * as path from 'path'
import {URL} from 'url'
import * as utils from './utils'
import {v4 as uuid} from 'uuid'
interface GitRemote {
hostname: string
@@ -14,6 +13,7 @@ interface GitRemote {
export class GitConfigHelper {
private git: GitCommandManager
private gitConfigPath = ''
private workingDirectory: string
private safeDirectoryConfigKey = 'safe.directory'
private safeDirectoryAdded = false
@@ -22,8 +22,6 @@ export class GitConfigHelper {
private extraheaderConfigPlaceholderValue = 'AUTHORIZATION: basic ***'
private extraheaderConfigValueRegex = '^AUTHORIZATION:'
private persistedExtraheaderConfigValue = ''
// Path to the credentials config file in RUNNER_TEMP (new v6-style auth)
private credentialsConfigPath = ''
private constructor(git: GitCommandManager) {
this.git = git
@@ -123,14 +121,11 @@ export class GitConfigHelper {
async savePersistedAuth(): Promise<void> {
const serverUrl = new URL(`https://${this.getGitRemote().hostname}`)
this.extraheaderConfigKey = `http.${serverUrl.origin}/.extraheader`
// Save and unset persisted extraheader credential in git config if it exists (old-style auth)
// Note: checkout@v6 uses credentials files with includeIf, so we don't need to
// manipulate those - they work independently via git's include mechanism
// Save and unset persisted extraheader credential in git config if it exists
this.persistedExtraheaderConfigValue = await this.getAndUnset()
}
async restorePersistedAuth(): Promise<void> {
// Restore old-style extraheader config if it was persisted
if (this.persistedExtraheaderConfigValue) {
try {
await this.setExtraheaderConfig(this.persistedExtraheaderConfigValue)
@@ -142,218 +137,36 @@ export class GitConfigHelper {
}
async configureToken(token: string): Promise<void> {
// Encode the basic credential for HTTPS access
// Encode and configure the basic credential for HTTPS access
const basicCredential = Buffer.from(
`x-access-token:${token}`,
'utf8'
).toString('base64')
core.setSecret(basicCredential)
const extraheaderConfigValue = `AUTHORIZATION: basic ${basicCredential}`
// Get or create the credentials config file path
const credentialsConfigPath = this.getCredentialsConfigPath()
// Write placeholder to the separate credentials config file using git config.
// This approach avoids the credential being captured by process creation audit events,
// which are commonly logged. For more information, refer to
// https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
await this.git.config(
this.extraheaderConfigKey,
this.extraheaderConfigPlaceholderValue,
false, // globalConfig
false, // add
credentialsConfigPath
)
// Replace the placeholder in the credentials config file
let content = (await fs.promises.readFile(credentialsConfigPath)).toString()
const placeholderIndex = content.indexOf(
this.extraheaderConfigPlaceholderValue
)
if (
placeholderIndex < 0 ||
placeholderIndex !=
content.lastIndexOf(this.extraheaderConfigPlaceholderValue)
) {
throw new Error(
`Unable to replace auth placeholder in ${credentialsConfigPath}`
)
}
content = content.replace(
this.extraheaderConfigPlaceholderValue,
extraheaderConfigValue
)
await fs.promises.writeFile(credentialsConfigPath, content)
// Configure includeIf entries to reference the credentials config file
await this.configureIncludeIf(credentialsConfigPath)
await this.setExtraheaderConfig(extraheaderConfigValue)
}
async removeAuth(): Promise<void> {
// Remove old-style extraheader config if it exists
await this.getAndUnset()
// Remove includeIf entries that point to git-credentials-*.config files
// and clean up the credentials config files
await this.removeIncludeIfCredentials()
}
/**
* Gets or creates the path to the credentials config file in RUNNER_TEMP.
* @returns The absolute path to the credentials config file
*/
private getCredentialsConfigPath(): string {
if (this.credentialsConfigPath) {
return this.credentialsConfigPath
}
const runnerTemp = process.env['RUNNER_TEMP'] || ''
if (!runnerTemp) {
throw new Error('RUNNER_TEMP is not defined')
}
// Create a unique filename for this action instance
const configFileName = `git-credentials-${uuid()}.config`
this.credentialsConfigPath = path.join(runnerTemp, configFileName)
core.debug(`Credentials config path: ${this.credentialsConfigPath}`)
return this.credentialsConfigPath
}
/**
* Configures includeIf entries in the local git config to reference the credentials file.
* Sets up entries for both host and container paths to support Docker container actions.
*/
private async configureIncludeIf(
credentialsConfigPath: string
): Promise<void> {
// Host git directory
const gitDir = await this.git.getGitDirectory()
let hostGitDir = path.join(this.workingDirectory, gitDir)
hostGitDir = hostGitDir.replace(/\\/g, '/') // Use forward slashes, even on Windows
// Configure host includeIf
const hostIncludeKey = `includeIf.gitdir:${hostGitDir}.path`
await this.git.config(hostIncludeKey, credentialsConfigPath)
// Configure host includeIf for worktrees
const hostWorktreeIncludeKey = `includeIf.gitdir:${hostGitDir}/worktrees/*.path`
await this.git.config(hostWorktreeIncludeKey, credentialsConfigPath)
// Container paths for Docker container actions
const githubWorkspace = process.env['GITHUB_WORKSPACE']
if (githubWorkspace) {
let relativePath = path.relative(githubWorkspace, this.workingDirectory)
relativePath = relativePath.replace(/\\/g, '/') // Use forward slashes, even on Windows
const containerGitDir = path.posix.join(
'/github/workspace',
relativePath,
'.git'
)
// Container credentials config path
const containerCredentialsPath = path.posix.join(
'/github/runner_temp',
path.basename(credentialsConfigPath)
)
// Configure container includeIf
const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`
await this.git.config(containerIncludeKey, containerCredentialsPath)
// Configure container includeIf for worktrees
const containerWorktreeIncludeKey = `includeIf.gitdir:${containerGitDir}/worktrees/*.path`
await this.git.config(
containerWorktreeIncludeKey,
containerCredentialsPath
)
}
}
/**
* Removes the includeIf entry and credentials config file created by this action instance.
* Only cleans up the specific credentials file tracked in this.credentialsConfigPath,
* leaving credentials created by other actions (e.g., actions/checkout) intact.
*/
private async removeIncludeIfCredentials(): Promise<void> {
// Only clean up if this action instance created a credentials config file
if (!this.credentialsConfigPath) {
return
}
try {
// Get all includeIf.gitdir keys from local config
const keys = await this.git.tryGetConfigKeys('^includeIf\\.gitdir:')
for (const key of keys) {
// Get all values for this key
const values = await this.git.tryGetConfigValues(key)
for (const value of values) {
// Only remove entries pointing to our specific credentials file
if (value === this.credentialsConfigPath) {
await this.git.tryConfigUnsetValue(key, value)
core.debug(`Removed includeIf entry: ${key} = ${value}`)
}
}
}
} catch (e) {
// Ignore errors during cleanup
core.debug(`Error during includeIf cleanup: ${utils.getErrorMessage(e)}`)
}
// Delete only our credentials config file
const runnerTemp = process.env['RUNNER_TEMP']
const resolvedCredentialsPath = path.resolve(this.credentialsConfigPath)
const resolvedRunnerTemp = runnerTemp ? path.resolve(runnerTemp) : ''
if (
resolvedRunnerTemp &&
resolvedCredentialsPath.startsWith(resolvedRunnerTemp + path.sep)
) {
try {
await fs.promises.unlink(this.credentialsConfigPath)
core.info(
`Removed credentials config file: ${this.credentialsConfigPath}`
)
} catch (e) {
core.debug(
`Could not remove credentials file ${this.credentialsConfigPath}: ${utils.getErrorMessage(e)}`
)
}
}
}
/**
* Sets extraheader config directly in .git/config (old-style auth).
* Used only for restoring persisted credentials from checkout@v4/v5.
*/
private async setExtraheaderConfig(
extraheaderConfigValue: string
): Promise<void> {
// Configure a placeholder value. This approach avoids the credential being captured
// by process creation audit events, which are commonly logged. For more information,
// refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
// See https://github.com/actions/checkout/blob/main/src/git-auth-helper.ts#L267-L274
await this.git.config(
this.extraheaderConfigKey,
this.extraheaderConfigPlaceholderValue
)
// Replace the placeholder in the local git config
const gitDir = await this.git.getGitDirectory()
const gitConfigPath = path.join(this.workingDirectory, gitDir, 'config')
let content = (await fs.promises.readFile(gitConfigPath)).toString()
const index = content.indexOf(this.extraheaderConfigPlaceholderValue)
if (
index < 0 ||
index != content.lastIndexOf(this.extraheaderConfigPlaceholderValue)
) {
throw new Error(
`Unable to replace '${this.extraheaderConfigPlaceholderValue}' in ${gitConfigPath}`
)
}
content = content.replace(
// Replace the placeholder
await this.gitConfigStringReplace(
this.extraheaderConfigPlaceholderValue,
extraheaderConfigValue
)
await fs.promises.writeFile(gitConfigPath, content)
}
private async getAndUnset(): Promise<string> {
@@ -384,4 +197,21 @@ export class GitConfigHelper {
}
return configValue
}
private async gitConfigStringReplace(
find: string,
replace: string
): 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()
const index = content.indexOf(find)
if (index < 0 || index != content.lastIndexOf(find)) {
throw new Error(`Unable to replace '${find}' in ${this.gitConfigPath}`)
}
content = content.replace(find, replace)
await fs.promises.writeFile(this.gitConfigPath, content)
}
}
+34 -99
View File
@@ -1,12 +1,7 @@
import * as core from '@actions/core'
import {Inputs} from './create-pull-request'
import {Commit, GitCommandManager} from './git-command-manager'
import {
Octokit,
OctokitOptions,
retryOptions,
throttleOptions
} from './octokit-client'
import {Commit} from './git-command-manager'
import {Octokit, OctokitOptions, throttleOptions} from './octokit-client'
import pLimit from 'p-limit'
import * as utils from './utils'
@@ -40,7 +35,7 @@ type TreeObject = {
path: string
mode: '100644' | '100755' | '040000' | '160000' | '120000'
sha: string | null
type: 'blob' | 'commit'
type: 'blob'
}
export class GitHubHelper {
@@ -57,7 +52,6 @@ export class GitHubHelper {
options.baseUrl = 'https://api.github.com'
}
options.throttle = throttleOptions
options.retry = retryOptions
this.octokit = new Octokit(options)
}
@@ -69,50 +63,6 @@ export class GitHubHelper {
}
}
private async getPullNumber(
baseRepository: string,
headBranch: string,
baseBranch: string
): Promise<number> {
const {data: pulls} = await this.octokit.rest.pulls.list({
...this.parseRepository(baseRepository),
state: 'open',
head: headBranch,
base: baseBranch
})
let pullNumber: number | undefined = undefined
if (pulls?.length === 0 || pulls === null || pulls === undefined) {
// This is a fallback due to a bug that affects the list endpoint when called on forks with the same owner as the repository parent.
core.info(
`Pull request not found via list endpoint; attempting fallback mechanism`
)
for await (const response of this.octokit.paginate.iterator(
this.octokit.rest.pulls.list,
{
...this.parseRepository(baseRepository),
state: 'open',
base: baseBranch
}
)) {
const existingPull = response.data.find(
pull => pull.head.label === headBranch
)
if (existingPull !== undefined) {
pullNumber = existingPull.number
break
}
}
} else {
pullNumber = pulls[0].number
}
if (pullNumber === undefined) {
throw new Error(
`Failed to find pull request number for branch ${headBranch}`
)
}
return pullNumber
}
private async createOrUpdate(
inputs: Inputs,
baseRepository: string,
@@ -163,15 +113,16 @@ export class GitHubHelper {
// Update the pull request that exists for this branch and base
core.info(`Fetching existing pull request`)
const pullNumber = await this.getPullNumber(
baseRepository,
headBranch,
inputs.base
)
const {data: pulls} = await this.octokit.rest.pulls.list({
...this.parseRepository(baseRepository),
state: 'open',
head: headBranch,
base: inputs.base
})
core.info(`Attempting update of pull request`)
const {data: pull} = await this.octokit.rest.pulls.update({
...this.parseRepository(baseRepository),
pull_number: pullNumber,
pull_number: pulls[0].number,
title: inputs.title,
body: inputs.body
})
@@ -269,7 +220,6 @@ export class GitHubHelper {
}
async pushSignedCommits(
git: GitCommandManager,
branchCommits: Commit[],
baseCommit: Commit,
repoPath: string,
@@ -283,7 +233,6 @@ export class GitHubHelper {
}
for (const commit of branchCommits) {
headCommit = await this.createCommit(
git,
commit,
headCommit,
repoPath,
@@ -295,7 +244,6 @@ export class GitHubHelper {
}
private async createCommit(
git: GitCommandManager,
commit: Commit,
parentCommit: CommitResponse,
repoPath: string,
@@ -307,45 +255,32 @@ export class GitHubHelper {
if (commit.changes.length > 0) {
core.info(`Creating tree objects for local commit ${commit.sha}`)
const treeObjects = await Promise.all(
commit.changes.map(async ({path, mode, status, dstSha}) => {
if (mode === '160000') {
// submodule
core.info(`Creating tree object for submodule commit at '${path}'`)
return <TreeObject>{
path,
mode,
sha: dstSha,
type: 'commit'
}
} else {
let sha: string | null = null
if (status === 'A' || status === 'M') {
try {
const {data: blob} = await blobCreationLimit(async () =>
this.octokit.rest.git.createBlob({
...repository,
content: await git.showFileAtRefBase64(commit.sha, path),
encoding: 'base64'
})
)
sha = blob.sha
} catch (error) {
core.error(
`Error creating blob for file '${path}': ${utils.getErrorMessage(error)}`
)
throw error
}
}
core.info(
`Creating tree object for blob at '${path}' with status '${status}'`
)
return <TreeObject>{
path,
mode,
sha,
type: 'blob'
commit.changes.map(async ({path, mode, status}) => {
let sha: string | null = null
if (status === 'A' || status === 'M') {
try {
const {data: blob} = await blobCreationLimit(() =>
this.octokit.rest.git.createBlob({
...repository,
content: utils.readFileBase64([repoPath, path]),
encoding: 'base64'
})
)
sha = blob.sha
} catch (error) {
core.error(
`Error creating blob for file '${path}': ${utils.getErrorMessage(error)}`
)
throw error
}
}
core.info(`Created blob for file '${path}'`)
return <TreeObject>{
path,
mode,
sha,
type: 'blob'
}
})
)
+16 -8
View File
@@ -2,9 +2,9 @@ import * as core from '@actions/core'
import {Octokit as OctokitCore} from '@octokit/core'
import {paginateRest} from '@octokit/plugin-paginate-rest'
import {restEndpointMethods} from '@octokit/plugin-rest-endpoint-methods'
import {retry} from '@octokit/plugin-retry'
import {throttling} from '@octokit/plugin-throttling'
import {fetch} from 'node-fetch-native/proxy'
import {getProxyForUrl} from 'proxy-from-env'
import {ProxyAgent, fetch as undiciFetch} from 'undici'
export {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods'
// eslint-disable-next-line import/no-unresolved
export {OctokitOptions} from '@octokit/core/dist-types/types'
@@ -12,7 +12,6 @@ export {OctokitOptions} from '@octokit/core/dist-types/types'
export const Octokit = OctokitCore.plugin(
paginateRest,
restEndpointMethods,
retry,
throttling,
autoProxyAgent
)
@@ -34,14 +33,23 @@ export const throttleOptions = {
}
}
export const retryOptions = {
// 429 is handled by the throttling plugin, so we exclude it from retry
doNotRetry: [400, 401, 403, 404, 410, 422, 429, 451]
}
const proxyFetch =
(proxyUrl: string): typeof undiciFetch =>
(url, opts) => {
return undiciFetch(url, {
...opts,
dispatcher: new ProxyAgent({
uri: proxyUrl
})
})
}
// Octokit plugin to support the standard environment variables http_proxy, https_proxy and no_proxy
function autoProxyAgent(octokit: OctokitCore) {
octokit.hook.before('request', options => {
options.request.fetch = fetch
const proxy = getProxyForUrl(options.baseUrl)
if (proxy) {
options.request.fetch = proxyFetch(proxy)
}
})
}
+4 -5
View File
@@ -126,6 +126,10 @@ export function readFile(path: string): string {
return fs.readFileSync(path, 'utf-8')
}
export function readFileBase64(pathParts: string[]): string {
return fs.readFileSync(path.resolve(...pathParts)).toString('base64')
}
/* eslint-disable @typescript-eslint/no-explicit-any */
function hasErrorCode(error: any): error is {code: string} {
return typeof (error && error.code) === 'string'
@@ -135,8 +139,3 @@ export function getErrorMessage(error: unknown) {
if (error instanceof Error) return error.message
return String(error)
}
export const isSelfHosted = (): boolean =>
process.env['RUNNER_ENVIRONMENT'] !== 'github-hosted' &&
(process.env['AGENT_ISSELFHOSTED'] === '1' ||
process.env['AGENT_ISSELFHOSTED'] === undefined)