mirror of
https://github.com/actions/checkout.git
synced 2026-07-07 05:04:10 +00:00
Compare commits
26 Commits
aiqiaoy/ba
...
v3.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f4b7f8486 | ||
|
|
cd6a9fd493 | ||
|
|
24cb908017 | ||
|
|
27135e314d | ||
|
|
7b187184d1 | ||
|
|
ac59398561 | ||
|
|
3ba5ee6fac | ||
|
|
8856415920 | ||
|
|
755da8c3cf | ||
|
|
26d48e8ea1 | ||
|
|
bf085276ce | ||
|
|
5c3ccc22eb | ||
|
|
1f9a0c22da | ||
|
|
8230315d06 | ||
|
|
93ea575cb5 | ||
|
|
6a84743051 | ||
|
|
e6d535c99c | ||
|
|
2541b1294d | ||
|
|
0ffe6f9c55 | ||
|
|
dcd71f6466 | ||
|
|
add3486cc3 | ||
|
|
5126516654 | ||
|
|
d50f8ea767 | ||
|
|
2d1c1198e7 | ||
|
|
a12a3943b4 | ||
|
|
8f9e05e482 |
8
.github/workflows/check-dist.yml
vendored
8
.github/workflows/check-dist.yml
vendored
@@ -22,12 +22,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set Node.js 12.x
|
- name: Set Node.js 16.x
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# If dist/ was different than expected, upload the expected version as an artifact
|
# If dist/ was different than expected, upload the expected version as an artifact
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v2
|
||||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
|
|||||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -39,10 +39,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -55,4 +55,4 @@ jobs:
|
|||||||
- run: rm -rf dist # We want code scanning to analyze lib instead (individual .js files)
|
- run: rm -rf dist # We want code scanning to analyze lib instead (individual .js files)
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
2
.github/workflows/licensed.yml
vendored
2
.github/workflows/licensed.yml
vendored
@@ -9,6 +9,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Check licenses
|
name: Check licenses
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run licensed-check
|
- run: npm run licensed-check
|
||||||
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
@@ -13,8 +13,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run format-check
|
- run: npm run format-check
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Basic checkout
|
# Basic checkout
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
@@ -150,7 +150,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Basic checkout using git
|
# Basic checkout using git
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
@@ -182,7 +182,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Basic checkout using git
|
# Basic checkout using git
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
|
|||||||
31
.github/workflows/update-main-version.yml
vendored
Normal file
31
.github/workflows/update-main-version.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Update Main Version
|
||||||
|
run-name: Move ${{ github.event.inputs.major_version }} to ${{ github.event.inputs.target }}
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
target:
|
||||||
|
description: The tag or reference to use
|
||||||
|
required: true
|
||||||
|
major_version:
|
||||||
|
type: choice
|
||||||
|
description: The major version to update
|
||||||
|
options:
|
||||||
|
- v3
|
||||||
|
- v2
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Git config
|
||||||
|
run: |
|
||||||
|
git config user.name github-actions
|
||||||
|
git config user.email github-actions@github.com
|
||||||
|
- name: Tag new target
|
||||||
|
run: git tag -f ${{ github.event.inputs.major_version }} ${{ github.event.inputs.target }}
|
||||||
|
- name: Push new tag
|
||||||
|
run: git push origin ${{ github.event.inputs.major_version }} --force
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ __test__/_temp
|
|||||||
_temp/
|
_temp/
|
||||||
lib/
|
lib/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.vscode/
|
||||||
2
.licenses/npm/@actions/io.dep.yml
generated
2
.licenses/npm/@actions/io.dep.yml
generated
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: "@actions/io"
|
name: "@actions/io"
|
||||||
version: 1.1.2
|
version: 1.1.3
|
||||||
type: npm
|
type: npm
|
||||||
summary: Actions io lib
|
summary: Actions io lib
|
||||||
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
homepage: https://github.com/actions/toolkit/tree/main/packages/io
|
||||||
|
|||||||
2
.licenses/npm/uuid-3.3.3.dep.yml
generated
2
.licenses/npm/uuid-3.3.3.dep.yml
generated
@@ -3,7 +3,7 @@ name: uuid
|
|||||||
version: 3.3.3
|
version: 3.3.3
|
||||||
type: npm
|
type: npm
|
||||||
summary: RFC4122 (v1, v4, and v5) UUIDs
|
summary: RFC4122 (v1, v4, and v5) UUIDs
|
||||||
homepage:
|
homepage: https://github.com/kelektiv/node-uuid#readme
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE.md
|
- sources: LICENSE.md
|
||||||
|
|||||||
2
.licenses/npm/uuid-8.3.2.dep.yml
generated
2
.licenses/npm/uuid-8.3.2.dep.yml
generated
@@ -3,7 +3,7 @@ name: uuid
|
|||||||
version: 8.3.2
|
version: 8.3.2
|
||||||
type: npm
|
type: npm
|
||||||
summary: RFC4122 (v1, v4, and v5) UUIDs
|
summary: RFC4122 (v1, v4, and v5) UUIDs
|
||||||
homepage:
|
homepage: https://github.com/uuidjs/uuid#readme
|
||||||
license: mit
|
license: mit
|
||||||
licenses:
|
licenses:
|
||||||
- sources: LICENSE.md
|
- sources: LICENSE.md
|
||||||
|
|||||||
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,19 +1,42 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v2.5.0
|
## v3.4.0
|
||||||
- [Bump @actions/core to v1.10.0](https://github.com/actions/checkout/pull/962)
|
- [Upgrade codeql actions to v2](https://github.com/actions/checkout/pull/1209)
|
||||||
|
- [Upgrade dependencies](https://github.com/actions/checkout/pull/1210)
|
||||||
|
- [Upgrade @actions/io](https://github.com/actions/checkout/pull/1225)
|
||||||
|
|
||||||
## v2.4.2
|
## v3.3.0
|
||||||
- [Add input `set-safe-directory`](https://github.com/actions/checkout/pull/776)
|
- [Implement branch list using callbacks from exec function](https://github.com/actions/checkout/pull/1045)
|
||||||
|
- [Add in explicit reference to private checkout options](https://github.com/actions/checkout/pull/1050)
|
||||||
|
- [Fix comment typos (that got added in #770)](https://github.com/actions/checkout/pull/1057)
|
||||||
|
|
||||||
## v2.4.1
|
## v3.2.0
|
||||||
- [Set the safe directory option on git to prevent git commands failing when running in containers](https://github.com/actions/checkout/pull/762)
|
- [Add GitHub Action to perform release](https://github.com/actions/checkout/pull/942)
|
||||||
|
- [Fix status badge](https://github.com/actions/checkout/pull/967)
|
||||||
|
- [Replace datadog/squid with ubuntu/squid Docker image](https://github.com/actions/checkout/pull/1002)
|
||||||
|
- [Wrap pipeline commands for submoduleForeach in quotes](https://github.com/actions/checkout/pull/964)
|
||||||
|
- [Update @actions/io to 1.1.2](https://github.com/actions/checkout/pull/1029)
|
||||||
|
- [Upgrading version to 3.2.0](https://github.com/actions/checkout/pull/1039)
|
||||||
|
|
||||||
|
## v3.1.0
|
||||||
|
- [Use @actions/core `saveState` and `getState`](https://github.com/actions/checkout/pull/939)
|
||||||
|
- [Add `github-server-url` input](https://github.com/actions/checkout/pull/922)
|
||||||
|
|
||||||
|
## v3.0.2
|
||||||
|
- [Add input `set-safe-directory`](https://github.com/actions/checkout/pull/770)
|
||||||
|
|
||||||
|
## v3.0.1
|
||||||
|
- [Fixed an issue where checkout failed to run in container jobs due to the new git setting `safe.directory`](https://github.com/actions/checkout/pull/762)
|
||||||
|
- [Bumped various npm package versions](https://github.com/actions/checkout/pull/744)
|
||||||
|
|
||||||
|
## v3.0.0
|
||||||
|
|
||||||
|
- [Update to node 16](https://github.com/actions/checkout/pull/689)
|
||||||
|
|
||||||
## v2.3.1
|
## v2.3.1
|
||||||
|
|
||||||
- [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284)
|
- [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284)
|
||||||
|
|
||||||
|
|
||||||
## v2.3.0
|
## v2.3.0
|
||||||
|
|
||||||
- [Fallback to the default branch](https://github.com/actions/checkout/pull/278)
|
- [Fallback to the default branch](https://github.com/actions/checkout/pull/278)
|
||||||
|
|||||||
28
CONTRIBUTING.md
Normal file
28
CONTRIBUTING.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Submitting a pull request
|
||||||
|
|
||||||
|
1. Fork and clone the repository
|
||||||
|
1. Configure and install the dependencies: `npm install`
|
||||||
|
1. Create a new branch: `git checkout -b my-branch-name`
|
||||||
|
1. Make your change, add tests, and make sure the tests still pass: `npm run test`
|
||||||
|
1. Make sure your code is correctly formatted: `npm run format`
|
||||||
|
1. Update `dist/index.js` using `npm run build`. This creates a single javascript file that is used as an entrypoint for the action
|
||||||
|
1. Push to your fork and submit a pull request
|
||||||
|
1. Pat yourself on the back and wait for your pull request to be reviewed and merged
|
||||||
|
|
||||||
|
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
|
||||||
|
|
||||||
|
- Write tests.
|
||||||
|
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||||
|
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||||
|
- [GitHub Help](https://help.github.com)
|
||||||
|
- [Writing good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||||
|
|
||||||
|
Thanks! :heart: :heart: :heart:
|
||||||
|
|
||||||
|
GitHub Actions Team :octocat:
|
||||||
64
README.md
64
README.md
@@ -1,8 +1,6 @@
|
|||||||
<p align="center">
|
[](https://github.com/actions/checkout/actions/workflows/test.yml)
|
||||||
<a href="https://github.com/actions/checkout"><img alt="GitHub Actions status" src="https://github.com/actions/checkout/workflows/test-local/badge.svg"></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# Checkout V2
|
# Checkout V3
|
||||||
|
|
||||||
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
|
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
|
||||||
|
|
||||||
@@ -14,27 +12,14 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
|
|||||||
|
|
||||||
# What's new
|
# What's new
|
||||||
|
|
||||||
- Improved performance
|
- Updated to the node16 runtime by default
|
||||||
- Fetches only a single commit by default
|
- This requires a minimum [Actions Runner](https://github.com/actions/runner/releases/tag/v2.285.0) version of v2.285.0 to run, which is by default available in GHES 3.4 or later.
|
||||||
- Script authenticated git commands
|
|
||||||
- Auth token persisted in the local git config
|
|
||||||
- Supports SSH
|
|
||||||
- Creates a local branch
|
|
||||||
- No longer detached HEAD when checking out a branch
|
|
||||||
- Improved layout
|
|
||||||
- The input `path` is always relative to $GITHUB_WORKSPACE
|
|
||||||
- Aligns better with container actions, where $GITHUB_WORKSPACE gets mapped in
|
|
||||||
- Fallback to REST API download
|
|
||||||
- When Git 2.18 or higher is not in the PATH, the REST API will be used to download the files
|
|
||||||
- When using a job container, the container's PATH is used
|
|
||||||
|
|
||||||
Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions.
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
<!-- start usage -->
|
<!-- start usage -->
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
# Repository name with owner. For example, actions/checkout
|
# Repository name with owner. For example, actions/checkout
|
||||||
# Default: ${{ github.repository }}
|
# Default: ${{ github.repository }}
|
||||||
@@ -111,14 +96,11 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
|
|||||||
# Default: true
|
# Default: true
|
||||||
set-safe-directory: ''
|
set-safe-directory: ''
|
||||||
|
|
||||||
# Required to check out fork pull request code from a workflow triggered by
|
# The base URL for the GitHub instance that you are trying to clone from, will use
|
||||||
# `pull_request_target` or `workflow_run`. These workflows run with the base
|
# environment defaults to fetch from the same instance that the workflow is
|
||||||
# repository's GITHUB_TOKEN, secrets, default-branch cache scope, and runner
|
# running from unless specified. Example URLs are https://github.com or
|
||||||
# access; fetching and executing a fork's code in that trusted context commonly
|
# https://my-ghes-server.example.com
|
||||||
# leads to "pwn request" vulnerabilities. Set to `true` only after reviewing the
|
github-server-url: ''
|
||||||
# risks at https://gh.io/securely-using-pull_request_target.
|
|
||||||
# Default: false
|
|
||||||
allow-unsafe-pr-checkout: ''
|
|
||||||
```
|
```
|
||||||
<!-- end usage -->
|
<!-- end usage -->
|
||||||
|
|
||||||
@@ -137,7 +119,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
|
|||||||
## Fetch all history for all tags and branches
|
## Fetch all history for all tags and branches
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
```
|
```
|
||||||
@@ -145,7 +127,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
|
|||||||
## Checkout a different branch
|
## Checkout a different branch
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: my-branch
|
ref: my-branch
|
||||||
```
|
```
|
||||||
@@ -153,7 +135,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
|
|||||||
## Checkout HEAD^
|
## Checkout HEAD^
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- run: git checkout HEAD^
|
- run: git checkout HEAD^
|
||||||
@@ -163,40 +145,42 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
```
|
```
|
||||||
|
> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
|
||||||
|
|
||||||
## Checkout multiple repos (nested)
|
## Checkout multiple repos (nested)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
```
|
```
|
||||||
|
> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
|
||||||
|
|
||||||
## Checkout multiple repos (private)
|
## Checkout multiple repos (private)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout private tools
|
- name: Checkout private tools
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-private-tools
|
repository: my-org/my-private-tools
|
||||||
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
|
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
|
||||||
@@ -209,7 +193,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
|
|||||||
## Checkout pull request HEAD commit instead of merge commit
|
## Checkout pull request HEAD commit instead of merge commit
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
```
|
```
|
||||||
@@ -225,7 +209,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
```
|
```
|
||||||
|
|
||||||
## Push a commit using the built-in token
|
## Push a commit using the built-in token
|
||||||
@@ -236,7 +220,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- run: |
|
- run: |
|
||||||
date > generated.txt
|
date > generated.txt
|
||||||
git config user.name github-actions
|
git config user.name github-actions
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ let tempHomedir: string
|
|||||||
let git: IGitCommandManager & {env: {[key: string]: string}}
|
let git: IGitCommandManager & {env: {[key: string]: string}}
|
||||||
let settings: IGitSourceSettings
|
let settings: IGitSourceSettings
|
||||||
let sshPath: string
|
let sshPath: string
|
||||||
|
let githubServerUrl: string
|
||||||
|
|
||||||
describe('git-auth-helper tests', () => {
|
describe('git-auth-helper tests', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -67,11 +68,18 @@ describe('git-auth-helper tests', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const configureAuth_configuresAuthHeader =
|
async function testAuthHeader(
|
||||||
'configureAuth configures auth header'
|
testName: string,
|
||||||
it(configureAuth_configuresAuthHeader, async () => {
|
serverUrl: string | undefined = undefined
|
||||||
|
) {
|
||||||
// Arrange
|
// Arrange
|
||||||
await setup(configureAuth_configuresAuthHeader)
|
let expectedServerUrl = 'https://github.com'
|
||||||
|
if (serverUrl) {
|
||||||
|
githubServerUrl = serverUrl
|
||||||
|
expectedServerUrl = githubServerUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
await setup(testName)
|
||||||
expect(settings.authToken).toBeTruthy() // sanity check
|
expect(settings.authToken).toBeTruthy() // sanity check
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
|
|
||||||
@@ -88,9 +96,33 @@ describe('git-auth-helper tests', () => {
|
|||||||
).toString('base64')
|
).toString('base64')
|
||||||
expect(
|
expect(
|
||||||
configContent.indexOf(
|
configContent.indexOf(
|
||||||
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
`http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
||||||
)
|
)
|
||||||
).toBeGreaterThanOrEqual(0)
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const configureAuth_configuresAuthHeader =
|
||||||
|
'configureAuth configures auth header'
|
||||||
|
it(configureAuth_configuresAuthHeader, async () => {
|
||||||
|
await testAuthHeader(configureAuth_configuresAuthHeader)
|
||||||
|
})
|
||||||
|
|
||||||
|
const configureAuth_AcceptsGitHubServerUrl =
|
||||||
|
'inject https://my-ghes-server.com as github server url'
|
||||||
|
it(configureAuth_AcceptsGitHubServerUrl, async () => {
|
||||||
|
await testAuthHeader(
|
||||||
|
configureAuth_AcceptsGitHubServerUrl,
|
||||||
|
'https://my-ghes-server.com'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const configureAuth_AcceptsGitHubServerUrlSetToGHEC =
|
||||||
|
'inject https://github.com as github server url'
|
||||||
|
it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => {
|
||||||
|
await testAuthHeader(
|
||||||
|
configureAuth_AcceptsGitHubServerUrl,
|
||||||
|
'https://github.com'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse =
|
const configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse =
|
||||||
@@ -779,7 +811,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
sshStrict: true,
|
sshStrict: true,
|
||||||
workflowOrganizationId: 123456,
|
workflowOrganizationId: 123456,
|
||||||
setSafeDirectory: true,
|
setSafeDirectory: true,
|
||||||
allowUnsafePrCheckout: false
|
githubServerUrl: githubServerUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
80
__test__/git-command-manager.test.ts
Normal file
80
__test__/git-command-manager.test.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import * as exec from '@actions/exec'
|
||||||
|
import * as fshelper from '../lib/fs-helper'
|
||||||
|
import * as commandManager from '../lib/git-command-manager'
|
||||||
|
|
||||||
|
let git: commandManager.IGitCommandManager
|
||||||
|
let mockExec = jest.fn()
|
||||||
|
|
||||||
|
describe('git-auth-helper tests', () => {
|
||||||
|
beforeAll(async () => {})
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
|
||||||
|
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {})
|
||||||
|
|
||||||
|
it('branch list matches', async () => {
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
console.log(args, options.listeners.stdout)
|
||||||
|
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.includes('rev-parse')) {
|
||||||
|
options.listeners.stdline(Buffer.from('refs/heads/foo'))
|
||||||
|
options.listeners.stdline(Buffer.from('refs/heads/bar'))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
})
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
git = await commandManager.createCommandManager(workingDirectory, lfs)
|
||||||
|
|
||||||
|
let branches = await git.branchList(false)
|
||||||
|
|
||||||
|
expect(branches).toHaveLength(2)
|
||||||
|
expect(branches.sort()).toEqual(['foo', 'bar'].sort())
|
||||||
|
})
|
||||||
|
|
||||||
|
it('ambiguous ref name output is captured', async () => {
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
console.log(args, options.listeners.stdout)
|
||||||
|
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.includes('rev-parse')) {
|
||||||
|
options.listeners.stdline(Buffer.from('refs/heads/foo'))
|
||||||
|
// If refs/tags/v1 and refs/heads/tags/v1 existed on this repository
|
||||||
|
options.listeners.errline(
|
||||||
|
Buffer.from("error: refname 'tags/v1' is ambiguous")
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
})
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
git = await commandManager.createCommandManager(workingDirectory, lfs)
|
||||||
|
|
||||||
|
let branches = await git.branchList(false)
|
||||||
|
|
||||||
|
expect(branches).toHaveLength(1)
|
||||||
|
expect(branches.sort()).toEqual(['foo'].sort())
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -86,7 +86,6 @@ describe('input-helper tests', () => {
|
|||||||
expect(settings.repositoryOwner).toBe('some-owner')
|
expect(settings.repositoryOwner).toBe('some-owner')
|
||||||
expect(settings.repositoryPath).toBe(gitHubWorkspace)
|
expect(settings.repositoryPath).toBe(gitHubWorkspace)
|
||||||
expect(settings.setSafeDirectory).toBe(true)
|
expect(settings.setSafeDirectory).toBe(true)
|
||||||
expect(settings.allowUnsafePrCheckout).toBe(false)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('qualifies ref', async () => {
|
it('qualifies ref', async () => {
|
||||||
|
|||||||
@@ -1,267 +0,0 @@
|
|||||||
import * as github from '@actions/github'
|
|
||||||
import {assertSafePrCheckout} from '../lib/unsafe-pr-checkout-helper'
|
|
||||||
|
|
||||||
// Shallow clone original @actions/github context
|
|
||||||
const originalContext = {...github.context}
|
|
||||||
const originalEventName = github.context.eventName
|
|
||||||
const originalPayload = github.context.payload
|
|
||||||
|
|
||||||
const BASE_REPO_ID = 100
|
|
||||||
const FORK_REPO_ID = 200
|
|
||||||
const PR_HEAD_SHA = '1111111111111111111111111111111111111111'
|
|
||||||
const PR_MERGE_SHA = '2222222222222222222222222222222222222222'
|
|
||||||
const SAFE_BASE_SHA = '3333333333333333333333333333333333333333'
|
|
||||||
const WORKFLOW_RUN_HEAD_COMMIT_SHA = '4444444444444444444444444444444444444444'
|
|
||||||
const BASE_QUALIFIED_REPO = 'some-owner/some-repo'
|
|
||||||
const FORK_QUALIFIED_REPO = 'another-repo/fork'
|
|
||||||
|
|
||||||
function setContext(eventName: string, payload: object): void {
|
|
||||||
;(github.context as {eventName: string}).eventName = eventName
|
|
||||||
;(github.context as {payload: object}).payload = payload
|
|
||||||
}
|
|
||||||
|
|
||||||
function forkPullRequestTargetPayload(): object {
|
|
||||||
return {
|
|
||||||
repository: {id: BASE_REPO_ID},
|
|
||||||
pull_request: {
|
|
||||||
head: {
|
|
||||||
sha: PR_HEAD_SHA,
|
|
||||||
repo: {id: FORK_REPO_ID, full_name: FORK_QUALIFIED_REPO}
|
|
||||||
},
|
|
||||||
merge_commit_sha: PR_MERGE_SHA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sameRepoPullRequestTargetPayload(): object {
|
|
||||||
return {
|
|
||||||
repository: {id: BASE_REPO_ID},
|
|
||||||
pull_request: {
|
|
||||||
head: {
|
|
||||||
sha: PR_HEAD_SHA,
|
|
||||||
repo: {id: BASE_REPO_ID, full_name: BASE_QUALIFIED_REPO}
|
|
||||||
},
|
|
||||||
merge_commit_sha: PR_MERGE_SHA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function forkWorkflowRunPayload(): object {
|
|
||||||
return {
|
|
||||||
repository: {id: BASE_REPO_ID},
|
|
||||||
workflow_run: {
|
|
||||||
event: 'pull_request',
|
|
||||||
head_commit: {id: WORKFLOW_RUN_HEAD_COMMIT_SHA},
|
|
||||||
head_repository: {id: FORK_REPO_ID, full_name: FORK_QUALIFIED_REPO}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('unsafe-pr-checkout-helper', () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
jest.spyOn(github.context, 'repo', 'get').mockReturnValue({
|
|
||||||
owner: 'some-owner',
|
|
||||||
repo: 'some-repo'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
;(github.context as {eventName: string}).eventName = originalEventName
|
|
||||||
;(github.context as {payload: object}).payload = originalPayload
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
;(github.context as {eventName: string}).eventName =
|
|
||||||
originalContext.eventName
|
|
||||||
;(github.context as {payload: object}).payload = originalContext.payload
|
|
||||||
jest.restoreAllMocks()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('allows pull_request events untouched', () => {
|
|
||||||
setContext('pull_request', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: 'attacker/fork',
|
|
||||||
ref: 'refs/pull/1/merge',
|
|
||||||
commit: '',
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).not.toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('allows pull_request_target default checkout (base branch)', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: 'refs/heads/main',
|
|
||||||
commit: SAFE_BASE_SHA,
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).not.toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('allows same-repo pull_request_target checkout of PR head', () => {
|
|
||||||
setContext('pull_request_target', sameRepoPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: '',
|
|
||||||
commit: PR_HEAD_SHA,
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).not.toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses pull_request_target fork PR head SHA checkout', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: '',
|
|
||||||
commit: PR_HEAD_SHA,
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow(/Refusing to check out fork pull request code/)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses pull_request_target fork PR merge_commit_sha checkout', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: '',
|
|
||||||
commit: PR_MERGE_SHA,
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow(/allow-unsafe-pr-checkout/)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses pull_request_target fork PR ref pattern (head)', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: 'refs/pull/42/head',
|
|
||||||
commit: '',
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses pull_request_target fork PR ref pattern (merge)', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: 'refs/pull/42/merge',
|
|
||||||
commit: '',
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses pull_request_target when repository points at the fork', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: FORK_QUALIFIED_REPO,
|
|
||||||
ref: 'refs/heads/main',
|
|
||||||
commit: '',
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('allows pull_request_target checkout of an unrelated third-party repo', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: 'some-other/unrelated',
|
|
||||||
ref: 'refs/heads/main',
|
|
||||||
commit: '',
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).not.toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses pull_request_target ignoring repository case differences', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: FORK_QUALIFIED_REPO.toUpperCase(),
|
|
||||||
ref: '',
|
|
||||||
commit: '',
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses pull_request_target ignoring commit SHA case differences', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: '',
|
|
||||||
commit: PR_HEAD_SHA.toUpperCase(),
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('allows pull_request_target fork PR checkout when opted in', () => {
|
|
||||||
setContext('pull_request_target', forkPullRequestTargetPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: 'refs/pull/42/merge',
|
|
||||||
commit: '',
|
|
||||||
allowUnsafePrCheckout: true
|
|
||||||
})
|
|
||||||
).not.toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses workflow_run fork PR head_commit.id checkout', () => {
|
|
||||||
setContext('workflow_run', forkWorkflowRunPayload())
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: '',
|
|
||||||
commit: WORKFLOW_RUN_HEAD_COMMIT_SHA,
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('refuses workflow_run with pull_request_target underlying event', () => {
|
|
||||||
const payload = forkWorkflowRunPayload() as {
|
|
||||||
workflow_run: {event: string}
|
|
||||||
}
|
|
||||||
payload.workflow_run.event = 'pull_request_target'
|
|
||||||
setContext('workflow_run', payload)
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: '',
|
|
||||||
commit: WORKFLOW_RUN_HEAD_COMMIT_SHA,
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('allows workflow_run same-repo PR (head_repository.id matches base)', () => {
|
|
||||||
const payload = forkWorkflowRunPayload() as {
|
|
||||||
workflow_run: {head_repository: {id: number}}
|
|
||||||
}
|
|
||||||
payload.workflow_run.head_repository.id = BASE_REPO_ID
|
|
||||||
setContext('workflow_run', payload)
|
|
||||||
expect(() =>
|
|
||||||
assertSafePrCheckout({
|
|
||||||
qualifiedRepository: BASE_QUALIFIED_REPO,
|
|
||||||
ref: '',
|
|
||||||
commit: WORKFLOW_RUN_HEAD_COMMIT_SHA,
|
|
||||||
allowUnsafePrCheckout: false
|
|
||||||
})
|
|
||||||
).not.toThrow()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
14
action.yml
14
action.yml
@@ -71,16 +71,10 @@ inputs:
|
|||||||
set-safe-directory:
|
set-safe-directory:
|
||||||
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
|
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
|
||||||
default: true
|
default: true
|
||||||
allow-unsafe-pr-checkout:
|
github-server-url:
|
||||||
description: >
|
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
|
||||||
Required to check out fork pull request code from a workflow triggered by
|
required: false
|
||||||
`pull_request_target` or `workflow_run`. These workflows run with the
|
|
||||||
base repository's GITHUB_TOKEN, secrets, default-branch cache scope, and
|
|
||||||
runner access; fetching and executing a fork's code in that trusted
|
|
||||||
context commonly leads to "pwn request" vulnerabilities. Set to `true`
|
|
||||||
only after reviewing the risks at https://gh.io/securely-using-pull_request_target.
|
|
||||||
default: false
|
|
||||||
runs:
|
runs:
|
||||||
using: node12
|
using: node16
|
||||||
main: dist/index.js
|
main: dist/index.js
|
||||||
post: dist/index.js
|
post: dist/index.js
|
||||||
|
|||||||
36971
dist/index.js
vendored
36971
dist/index.js
vendored
File diff suppressed because one or more lines are too long
18642
package-lock.json
generated
18642
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "checkout",
|
"name": "checkout",
|
||||||
"version": "2.6.0",
|
"version": "3.2.0",
|
||||||
"description": "checkout action",
|
"description": "checkout action",
|
||||||
"main": "lib/main.js",
|
"main": "lib/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/exec": "^1.0.1",
|
"@actions/exec": "^1.0.1",
|
||||||
"@actions/github": "^2.2.0",
|
"@actions/github": "^2.2.0",
|
||||||
"@actions/io": "^1.1.2",
|
"@actions/io": "^1.1.3",
|
||||||
"@actions/tool-cache": "^1.1.2",
|
"@actions/tool-cache": "^1.1.2",
|
||||||
"uuid": "^3.3.3"
|
"uuid": "^3.3.3"
|
||||||
},
|
},
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"@types/uuid": "^3.4.6",
|
"@types/uuid": "^3.4.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
"@zeit/ncc": "^0.20.5",
|
"@vercel/ncc": "^0.36.1",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-plugin-github": "^4.3.2",
|
"eslint-plugin-github": "^4.3.2",
|
||||||
"eslint-plugin-jest": "^25.7.0",
|
"eslint-plugin-jest": "^25.7.0",
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class GitAuthHelper {
|
|||||||
this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings)
|
this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings)
|
||||||
|
|
||||||
// Token auth header
|
// Token auth header
|
||||||
const serverUrl = urlHelper.getServerUrl()
|
const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl)
|
||||||
this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT]
|
this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT]
|
||||||
const basicCredential = Buffer.from(
|
const basicCredential = Buffer.from(
|
||||||
`x-access-token:${this.settings.authToken}`,
|
`x-access-token:${this.settings.authToken}`,
|
||||||
|
|||||||
@@ -94,8 +94,11 @@ class GitCommandManager {
|
|||||||
|
|
||||||
// Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
|
// Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
|
||||||
// "branch --list" is more difficult when in a detached HEAD state.
|
// "branch --list" is more difficult when in a detached HEAD state.
|
||||||
// Note, this implementation uses "rev-parse --symbolic-full-name" because there is a bug
|
|
||||||
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.
|
// TODO(https://github.com/actions/checkout/issues/786): this implementation uses
|
||||||
|
// "rev-parse --symbolic-full-name" because there is a bug
|
||||||
|
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names. When
|
||||||
|
// 2.18 is no longer supported, we can switch back to --symbolic.
|
||||||
|
|
||||||
const args = ['rev-parse', '--symbolic-full-name']
|
const args = ['rev-parse', '--symbolic-full-name']
|
||||||
if (remote) {
|
if (remote) {
|
||||||
@@ -104,21 +107,49 @@ class GitCommandManager {
|
|||||||
args.push('--branches')
|
args.push('--branches')
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = await this.execGit(args)
|
const stderr: string[] = []
|
||||||
|
const errline: string[] = []
|
||||||
|
const stdout: string[] = []
|
||||||
|
const stdline: string[] = []
|
||||||
|
|
||||||
for (let branch of output.stdout.trim().split('\n')) {
|
const listeners = {
|
||||||
branch = branch.trim()
|
stderr: (data: Buffer) => {
|
||||||
if (branch) {
|
stderr.push(data.toString())
|
||||||
if (branch.startsWith('refs/heads/')) {
|
},
|
||||||
branch = branch.substr('refs/heads/'.length)
|
errline: (data: Buffer) => {
|
||||||
} else if (branch.startsWith('refs/remotes/')) {
|
errline.push(data.toString())
|
||||||
branch = branch.substr('refs/remotes/'.length)
|
},
|
||||||
}
|
stdout: (data: Buffer) => {
|
||||||
|
stdout.push(data.toString())
|
||||||
result.push(branch)
|
},
|
||||||
|
stdline: (data: Buffer) => {
|
||||||
|
stdline.push(data.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Suppress the output in order to avoid flooding annotations with innocuous errors.
|
||||||
|
await this.execGit(args, false, true, listeners)
|
||||||
|
|
||||||
|
core.debug(`stderr callback is: ${stderr}`)
|
||||||
|
core.debug(`errline callback is: ${errline}`)
|
||||||
|
core.debug(`stdout callback is: ${stdout}`)
|
||||||
|
core.debug(`stdline callback is: ${stdline}`)
|
||||||
|
|
||||||
|
for (let branch of stdline) {
|
||||||
|
branch = branch.trim()
|
||||||
|
if (!branch) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (branch.startsWith('refs/heads/')) {
|
||||||
|
branch = branch.substring('refs/heads/'.length)
|
||||||
|
} else if (branch.startsWith('refs/remotes/')) {
|
||||||
|
branch = branch.substring('refs/remotes/'.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(branch)
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +426,8 @@ class GitCommandManager {
|
|||||||
private async execGit(
|
private async execGit(
|
||||||
args: string[],
|
args: string[],
|
||||||
allowAllExitCodes = false,
|
allowAllExitCodes = false,
|
||||||
silent = false
|
silent = false,
|
||||||
|
customListeners = {}
|
||||||
): Promise<GitOutput> {
|
): Promise<GitOutput> {
|
||||||
fshelper.directoryExistsSync(this.workingDirectory, true)
|
fshelper.directoryExistsSync(this.workingDirectory, true)
|
||||||
|
|
||||||
@@ -409,22 +441,29 @@ class GitCommandManager {
|
|||||||
env[key] = this.gitEnv[key]
|
env[key] = this.gitEnv[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
const stdout: string[] = []
|
const defaultListener = {
|
||||||
|
stdout: (data: Buffer) => {
|
||||||
|
stdout.push(data.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergedListeners = {...defaultListener, ...customListeners}
|
||||||
|
|
||||||
|
const stdout: string[] = []
|
||||||
const options = {
|
const options = {
|
||||||
cwd: this.workingDirectory,
|
cwd: this.workingDirectory,
|
||||||
env,
|
env,
|
||||||
silent,
|
silent,
|
||||||
ignoreReturnCode: allowAllExitCodes,
|
ignoreReturnCode: allowAllExitCodes,
|
||||||
listeners: {
|
listeners: mergedListeners
|
||||||
stdout: (data: Buffer) => {
|
|
||||||
stdout.push(data.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options)
|
result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options)
|
||||||
result.stdout = stdout.join('')
|
result.stdout = stdout.join('')
|
||||||
|
|
||||||
|
core.debug(result.exitCode.toString())
|
||||||
|
core.debug(result.stdout)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
settings.repositoryName,
|
settings.repositoryName,
|
||||||
settings.ref,
|
settings.ref,
|
||||||
settings.commit,
|
settings.commit,
|
||||||
settings.repositoryPath
|
settings.repositoryPath,
|
||||||
|
settings.githubServerUrl
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -138,7 +139,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
settings.ref = await githubApiHelper.getDefaultBranch(
|
settings.ref = await githubApiHelper.getDefaultBranch(
|
||||||
settings.authToken,
|
settings.authToken,
|
||||||
settings.repositoryOwner,
|
settings.repositoryOwner,
|
||||||
settings.repositoryName
|
settings.repositoryName,
|
||||||
|
settings.githubServerUrl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
@@ -232,7 +234,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
settings.repositoryOwner,
|
settings.repositoryOwner,
|
||||||
settings.repositoryName,
|
settings.repositoryName,
|
||||||
settings.ref,
|
settings.ref,
|
||||||
settings.commit
|
settings.commit,
|
||||||
|
settings.githubServerUrl
|
||||||
)
|
)
|
||||||
} finally {
|
} finally {
|
||||||
// Remove auth
|
// Remove auth
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ export interface IGitSourceSettings {
|
|||||||
setSafeDirectory: boolean
|
setSafeDirectory: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opt-in to allow checking out fork pull request code from a workflow
|
* User override on the GitHub Server/Host URL that hosts the repository to be cloned
|
||||||
* triggered by pull_request_target or workflow_run.
|
|
||||||
*/
|
*/
|
||||||
allowUnsafePrCheckout: boolean
|
githubServerUrl: string | undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import * as assert from 'assert'
|
import * as assert from 'assert'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as github from '@actions/github'
|
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as retryHelper from './retry-helper'
|
import * as retryHelper from './retry-helper'
|
||||||
import * as toolCache from '@actions/tool-cache'
|
import * as toolCache from '@actions/tool-cache'
|
||||||
import {default as uuid} from 'uuid/v4'
|
import {default as uuid} from 'uuid/v4'
|
||||||
import {Octokit} from '@octokit/rest'
|
import {getOctokit, Octokit} from './octokit-provider'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
|
||||||
@@ -17,18 +16,19 @@ export async function downloadRepository(
|
|||||||
repo: string,
|
repo: string,
|
||||||
ref: string,
|
ref: string,
|
||||||
commit: string,
|
commit: string,
|
||||||
repositoryPath: string
|
repositoryPath: string,
|
||||||
|
baseUrl?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Determine the default branch
|
// Determine the default branch
|
||||||
if (!ref && !commit) {
|
if (!ref && !commit) {
|
||||||
core.info('Determining the default branch')
|
core.info('Determining the default branch')
|
||||||
ref = await getDefaultBranch(authToken, owner, repo)
|
ref = await getDefaultBranch(authToken, owner, repo, baseUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download the archive
|
// Download the archive
|
||||||
let archiveData = await retryHelper.execute(async () => {
|
let archiveData = await retryHelper.execute(async () => {
|
||||||
core.info('Downloading the archive')
|
core.info('Downloading the archive')
|
||||||
return await downloadArchive(authToken, owner, repo, ref, commit)
|
return await downloadArchive(authToken, owner, repo, ref, commit, baseUrl)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Write archive to disk
|
// Write archive to disk
|
||||||
@@ -79,11 +79,12 @@ export async function downloadRepository(
|
|||||||
export async function getDefaultBranch(
|
export async function getDefaultBranch(
|
||||||
authToken: string,
|
authToken: string,
|
||||||
owner: string,
|
owner: string,
|
||||||
repo: string
|
repo: string,
|
||||||
|
baseUrl?: string
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return await retryHelper.execute(async () => {
|
return await retryHelper.execute(async () => {
|
||||||
core.info('Retrieving the default branch name')
|
core.info('Retrieving the default branch name')
|
||||||
const octokit = new github.GitHub(authToken)
|
const octokit = getOctokit(authToken, {baseUrl: baseUrl})
|
||||||
let result: string
|
let result: string
|
||||||
try {
|
try {
|
||||||
// Get the default branch from the repo info
|
// Get the default branch from the repo info
|
||||||
@@ -121,9 +122,10 @@ async function downloadArchive(
|
|||||||
owner: string,
|
owner: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
ref: string,
|
ref: string,
|
||||||
commit: string
|
commit: string,
|
||||||
|
baseUrl?: string
|
||||||
): Promise<Buffer> {
|
): Promise<Buffer> {
|
||||||
const octokit = new github.GitHub(authToken)
|
const octokit = getOctokit(authToken, {baseUrl: baseUrl})
|
||||||
const params: Octokit.ReposGetArchiveLinkParams = {
|
const params: Octokit.ReposGetArchiveLinkParams = {
|
||||||
owner: owner,
|
owner: owner,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import * as core from '@actions/core'
|
|||||||
import * as fsHelper from './fs-helper'
|
import * as fsHelper from './fs-helper'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as unsafePrCheckoutHelper from './unsafe-pr-checkout-helper'
|
|
||||||
import * as workflowContextHelper from './workflow-context-helper'
|
import * as workflowContextHelper from './workflow-context-helper'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import {IGitSourceSettings} from './git-source-settings'
|
||||||
|
|
||||||
@@ -127,18 +126,9 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||||||
result.setSafeDirectory =
|
result.setSafeDirectory =
|
||||||
(core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE'
|
(core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE'
|
||||||
|
|
||||||
// Allow unsafe PR checkout (opt-in for pull_request_target / workflow_run fork PRs)
|
// Determine the GitHub URL that the repository is being hosted from
|
||||||
result.allowUnsafePrCheckout =
|
result.githubServerUrl = core.getInput('github-server-url')
|
||||||
(core.getInput('allow-unsafe-pr-checkout') || 'false').toUpperCase() ===
|
core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
|
||||||
'TRUE'
|
|
||||||
core.debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`)
|
|
||||||
|
|
||||||
unsafePrCheckoutHelper.assertSafePrCheckout({
|
|
||||||
qualifiedRepository,
|
|
||||||
ref: result.ref,
|
|
||||||
commit: result.commit,
|
|
||||||
allowUnsafePrCheckout: result.allowUnsafePrCheckout
|
|
||||||
})
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ function updateUsage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateUsage(
|
updateUsage(
|
||||||
'actions/checkout@v2',
|
'actions/checkout@v3',
|
||||||
path.join(__dirname, '..', '..', 'action.yml'),
|
path.join(__dirname, '..', '..', 'action.yml'),
|
||||||
path.join(__dirname, '..', '..', 'README.md')
|
path.join(__dirname, '..', '..', 'README.md')
|
||||||
)
|
)
|
||||||
|
|||||||
23
src/octokit-provider.ts
Normal file
23
src/octokit-provider.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import * as github from '@actions/github'
|
||||||
|
import {Octokit} from '@octokit/rest'
|
||||||
|
import {getServerApiUrl} from './url-helper'
|
||||||
|
|
||||||
|
// Centralize all Octokit references by re-exporting
|
||||||
|
export {Octokit} from '@octokit/rest'
|
||||||
|
|
||||||
|
export type OctokitOptions = {
|
||||||
|
baseUrl?: string
|
||||||
|
userAgent?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOctokit(authToken: string, opts: OctokitOptions) {
|
||||||
|
const options: Octokit.Options = {
|
||||||
|
baseUrl: getServerApiUrl(opts.baseUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.userAgent) {
|
||||||
|
options.userAgent = opts.userAgent
|
||||||
|
}
|
||||||
|
|
||||||
|
return new github.GitHub(authToken, options)
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import {URL} from 'url'
|
|
||||||
import {IGitCommandManager} from './git-command-manager'
|
import {IGitCommandManager} from './git-command-manager'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
|
import {getOctokit} from './octokit-provider'
|
||||||
|
import {isGhes} from './url-helper'
|
||||||
|
|
||||||
export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
|
export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
|
||||||
|
|
||||||
@@ -183,11 +184,12 @@ export async function checkCommitInfo(
|
|||||||
repositoryOwner: string,
|
repositoryOwner: string,
|
||||||
repositoryName: string,
|
repositoryName: string,
|
||||||
ref: string,
|
ref: string,
|
||||||
commit: string
|
commit: string,
|
||||||
|
baseUrl?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// GHES?
|
// GHES?
|
||||||
if (isGhes()) {
|
if (isGhes(baseUrl)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +245,8 @@ export async function checkCommitInfo(
|
|||||||
core.debug(
|
core.debug(
|
||||||
`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`
|
`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`
|
||||||
)
|
)
|
||||||
const octokit = new github.GitHub(token, {
|
const octokit = getOctokit(token, {
|
||||||
|
baseUrl: baseUrl,
|
||||||
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload(
|
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload(
|
||||||
'number'
|
'number'
|
||||||
)};run_id=${
|
)};run_id=${
|
||||||
@@ -259,7 +262,7 @@ export async function checkCommitInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fromPayload(path: string): any {
|
function fromPayload(path: string): any {
|
||||||
return select(github.context.payload, path)
|
return select(github.context.payload, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,10 +279,3 @@ function select(obj: any, path: string): any {
|
|||||||
const key = path.substr(0, i)
|
const key = path.substr(0, i)
|
||||||
return select(obj[key], path.substr(i + 1))
|
return select(obj[key], path.substr(i + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGhes(): boolean {
|
|
||||||
const ghUrl = new URL(
|
|
||||||
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
|
||||||
)
|
|
||||||
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function setSshKnownHostsPath(sshKnownHostsPath: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the sef-safe-directory input so the POST action can retrieve the value.
|
* Save the set-safe-directory input so the POST action can retrieve the value.
|
||||||
*/
|
*/
|
||||||
export function setSafeDirectory() {
|
export function setSafeDirectory() {
|
||||||
core.saveState('setSafeDirectory', 'true')
|
core.saveState('setSafeDirectory', 'true')
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
import * as github from '@actions/github'
|
|
||||||
import {fromPayload} from './ref-helper'
|
|
||||||
|
|
||||||
const PR_REF_PATTERN = /^refs\/pull\/[0-9]+\/(?:head|merge)$/
|
|
||||||
|
|
||||||
export interface IUnsafePrCheckoutInput {
|
|
||||||
qualifiedRepository: string
|
|
||||||
ref: string
|
|
||||||
commit: string | undefined
|
|
||||||
allowUnsafePrCheckout: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export function assertSafePrCheckout(input: IUnsafePrCheckoutInput): void {
|
|
||||||
if (input.allowUnsafePrCheckout) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventName = github.context.eventName
|
|
||||||
if (eventName !== 'pull_request_target' && eventName !== 'workflow_run') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseRepoId = fromPayload('repository.id')
|
|
||||||
if (typeof baseRepoId !== 'number') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let prHeadRepoId: unknown
|
|
||||||
let prHeadRepoFullName: unknown
|
|
||||||
const prShas: string[] = []
|
|
||||||
|
|
||||||
if (eventName === 'pull_request_target') {
|
|
||||||
prHeadRepoId = fromPayload('pull_request.head.repo.id')
|
|
||||||
prHeadRepoFullName = fromPayload('pull_request.head.repo.full_name')
|
|
||||||
pushIfSha(prShas, fromPayload('pull_request.head.sha'))
|
|
||||||
pushIfSha(prShas, fromPayload('pull_request.merge_commit_sha'))
|
|
||||||
} else {
|
|
||||||
const wrEvent = fromPayload('workflow_run.event')
|
|
||||||
if (typeof wrEvent !== 'string' || !wrEvent.startsWith('pull_request')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
prHeadRepoId = fromPayload('workflow_run.head_repository.id')
|
|
||||||
prHeadRepoFullName = fromPayload('workflow_run.head_repository.full_name')
|
|
||||||
pushIfSha(prShas, fromPayload('workflow_run.head_commit.id'))
|
|
||||||
// For `pull_request_target`-triggered workflow_run, `head_sha` is the base
|
|
||||||
// default branch SHA (not the PR head)
|
|
||||||
if (wrEvent !== 'pull_request_target') {
|
|
||||||
pushIfSha(prShas, fromPayload('workflow_run.head_sha'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// (A) Fork PR?
|
|
||||||
if (typeof prHeadRepoId !== 'number' || prHeadRepoId === baseRepoId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// (B) We cannot check for all fork PR refs so check to see
|
|
||||||
// if the resolved input points to the fork PR sha we have in the payload
|
|
||||||
const repositoryMatchesPrHead =
|
|
||||||
typeof prHeadRepoFullName === 'string' &&
|
|
||||||
input.qualifiedRepository.toLowerCase() === prHeadRepoFullName.toLowerCase()
|
|
||||||
const refMatchesPullPattern = PR_REF_PATTERN.test(input.ref)
|
|
||||||
const commitMatchesPrHeadSha =
|
|
||||||
!!input.commit && prShas.includes(input.commit.toLowerCase())
|
|
||||||
|
|
||||||
if (
|
|
||||||
!repositoryMatchesPrHead &&
|
|
||||||
!refMatchesPullPattern &&
|
|
||||||
!commitMatchesPrHeadSha
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Refusing to check out fork pull request code from a '${eventName}' workflow. ` +
|
|
||||||
`This workflow runs with the base repository's GITHUB_TOKEN, secrets, default-branch ` +
|
|
||||||
`cache scope, and runner access. Fetching and executing a fork's code in that trusted ` +
|
|
||||||
`context commonly leads to "pwn request" vulnerabilities. To opt in, review the risks ` +
|
|
||||||
`at https://gh.io/securely-using-pull_request_target and set 'allow-unsafe-pr-checkout: true' ` +
|
|
||||||
`on the actions/checkout step.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushIfSha(target: string[], value: unknown): void {
|
|
||||||
if (typeof value === 'string' && value.length > 0) {
|
|
||||||
target.push(value.toLowerCase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as assert from 'assert'
|
import * as assert from 'assert'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
|
||||||
import {URL} from 'url'
|
import {URL} from 'url'
|
||||||
|
import {IGitSourceSettings} from './git-source-settings'
|
||||||
|
|
||||||
export function getFetchUrl(settings: IGitSourceSettings): string {
|
export function getFetchUrl(settings: IGitSourceSettings): string {
|
||||||
assert.ok(
|
assert.ok(
|
||||||
@@ -8,7 +8,7 @@ export function getFetchUrl(settings: IGitSourceSettings): string {
|
|||||||
'settings.repositoryOwner must be defined'
|
'settings.repositoryOwner must be defined'
|
||||||
)
|
)
|
||||||
assert.ok(settings.repositoryName, 'settings.repositoryName must be defined')
|
assert.ok(settings.repositoryName, 'settings.repositoryName must be defined')
|
||||||
const serviceUrl = getServerUrl()
|
const serviceUrl = getServerUrl(settings.githubServerUrl)
|
||||||
const encodedOwner = encodeURIComponent(settings.repositoryOwner)
|
const encodedOwner = encodeURIComponent(settings.repositoryOwner)
|
||||||
const encodedName = encodeURIComponent(settings.repositoryName)
|
const encodedName = encodeURIComponent(settings.repositoryName)
|
||||||
if (settings.sshKey) {
|
if (settings.sshKey) {
|
||||||
@@ -19,11 +19,27 @@ export function getFetchUrl(settings: IGitSourceSettings): string {
|
|||||||
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`
|
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getServerUrl(): URL {
|
export function getServerUrl(url?: string): URL {
|
||||||
// todo: remove GITHUB_URL after support for GHES Alpha is no longer needed
|
let urlValue =
|
||||||
return new URL(
|
url && url.trim().length > 0
|
||||||
process.env['GITHUB_SERVER_URL'] ||
|
? url
|
||||||
process.env['GITHUB_URL'] ||
|
: process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
||||||
'https://github.com'
|
return new URL(urlValue)
|
||||||
)
|
}
|
||||||
|
|
||||||
|
export function getServerApiUrl(url?: string): string {
|
||||||
|
let apiUrl = 'https://api.github.com'
|
||||||
|
|
||||||
|
if (isGhes(url)) {
|
||||||
|
const serverUrl = getServerUrl(url)
|
||||||
|
apiUrl = new URL(`${serverUrl.origin}/api/v3`).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isGhes(url?: string): boolean {
|
||||||
|
const ghUrl = getServerUrl(url)
|
||||||
|
|
||||||
|
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user