Merge branch 'main' into rm-deprecated-substr
This commit is contained in:
commit
55ad59dfaa
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
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
|
||||||
|
67
.github/workflows/test.yml
vendored
67
.github/workflows/test.yml
vendored
@ -72,6 +72,33 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: __test__/verify-side-by-side.sh
|
run: __test__/verify-side-by-side.sh
|
||||||
|
|
||||||
|
# Sparse checkout
|
||||||
|
- name: Sparse checkout
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
__test__
|
||||||
|
.github
|
||||||
|
dist
|
||||||
|
path: sparse-checkout
|
||||||
|
|
||||||
|
- name: Verify sparse checkout
|
||||||
|
run: __test__/verify-sparse-checkout.sh
|
||||||
|
|
||||||
|
# Sparse checkout (non-cone mode)
|
||||||
|
- name: Sparse checkout (non-cone mode)
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
/__test__/
|
||||||
|
/.github/
|
||||||
|
/dist/
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
path: sparse-checkout-non-cone-mode
|
||||||
|
|
||||||
|
- name: Verify sparse checkout (non-cone mode)
|
||||||
|
run: __test__/verify-sparse-checkout-non-cone-mode.sh
|
||||||
|
|
||||||
# LFS
|
# LFS
|
||||||
- name: Checkout LFS
|
- name: Checkout LFS
|
||||||
uses: ./
|
uses: ./
|
||||||
@ -142,7 +169,7 @@ jobs:
|
|||||||
options: --dns 127.0.0.1
|
options: --dns 127.0.0.1
|
||||||
services:
|
services:
|
||||||
squid-proxy:
|
squid-proxy:
|
||||||
image: datadog/squid:latest
|
image: ubuntu/squid:latest
|
||||||
ports:
|
ports:
|
||||||
- 3128:3128
|
- 3128:3128
|
||||||
env:
|
env:
|
||||||
@ -205,3 +232,41 @@ jobs:
|
|||||||
path: basic
|
path: basic
|
||||||
- name: Verify basic
|
- name: Verify basic
|
||||||
run: __test__/verify-basic.sh --archive
|
run: __test__/verify-basic.sh --archive
|
||||||
|
|
||||||
|
test-git-container:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: bitnami/git:latest
|
||||||
|
steps:
|
||||||
|
# Clone this repo
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: v3
|
||||||
|
|
||||||
|
# Basic checkout using git
|
||||||
|
- name: Checkout basic
|
||||||
|
uses: ./v3
|
||||||
|
with:
|
||||||
|
ref: test-data/v2/basic
|
||||||
|
- name: Verify basic
|
||||||
|
run: |
|
||||||
|
if [ ! -f "./basic-file.txt" ]; then
|
||||||
|
echo "Expected basic file does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify .git folder
|
||||||
|
if [ ! -d "./.git" ]; then
|
||||||
|
echo "Expected ./.git folder to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify auth token
|
||||||
|
git config --global --add safe.directory "*"
|
||||||
|
git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main
|
||||||
|
|
||||||
|
# needed to make checkout post cleanup succeed
|
||||||
|
- name: Fix Checkout v3
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: v3
|
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/
|
BIN
.licenses/npm/@actions/core.dep.yml
generated
BIN
.licenses/npm/@actions/core.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/github.dep.yml
generated
BIN
.licenses/npm/@actions/github.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/http-client.dep.yml
generated
BIN
.licenses/npm/@actions/http-client.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@actions/io.dep.yml
generated
BIN
.licenses/npm/@actions/io.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/auth-token.dep.yml
generated
BIN
.licenses/npm/@octokit/auth-token.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@octokit/endpoint.dep.yml
generated
BIN
.licenses/npm/@octokit/endpoint.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/graphql.dep.yml
generated
BIN
.licenses/npm/@octokit/graphql.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@octokit/plugin-paginate-rest.dep.yml
generated
BIN
.licenses/npm/@octokit/plugin-paginate-rest.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/plugin-request-log.dep.yml
generated
BIN
.licenses/npm/@octokit/plugin-request-log.dep.yml
generated
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/@octokit/request.dep.yml
generated
BIN
.licenses/npm/@octokit/request.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/rest.dep.yml
generated
BIN
.licenses/npm/@octokit/rest.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@octokit/types.dep.yml
generated
BIN
.licenses/npm/@octokit/types.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/@types/node.dep.yml
generated
BIN
.licenses/npm/@types/node.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/atob-lite.dep.yml
generated
BIN
.licenses/npm/atob-lite.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/before-after-hook.dep.yml
generated
BIN
.licenses/npm/before-after-hook.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/btoa-lite.dep.yml
generated
BIN
.licenses/npm/btoa-lite.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/cross-spawn.dep.yml
generated
BIN
.licenses/npm/cross-spawn.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/end-of-stream.dep.yml
generated
BIN
.licenses/npm/end-of-stream.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/get-stream.dep.yml
generated
BIN
.licenses/npm/get-stream.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/is-plain-object.dep.yml
generated
BIN
.licenses/npm/is-plain-object.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/is-stream.dep.yml
generated
BIN
.licenses/npm/is-stream.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/isexe.dep.yml
generated
BIN
.licenses/npm/isexe.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/isobject.dep.yml
generated
BIN
.licenses/npm/isobject.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/lodash.get.dep.yml
generated
BIN
.licenses/npm/lodash.get.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/lodash.set.dep.yml
generated
BIN
.licenses/npm/lodash.set.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/lodash.uniq.dep.yml
generated
BIN
.licenses/npm/lodash.uniq.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/macos-release.dep.yml
generated
BIN
.licenses/npm/macos-release.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/nice-try.dep.yml
generated
BIN
.licenses/npm/nice-try.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/node-fetch.dep.yml
generated
BIN
.licenses/npm/node-fetch.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/npm-run-path.dep.yml
generated
BIN
.licenses/npm/npm-run-path.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/octokit-pagination-methods.dep.yml
generated
BIN
.licenses/npm/octokit-pagination-methods.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/os-name.dep.yml
generated
BIN
.licenses/npm/os-name.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/p-finally.dep.yml
generated
BIN
.licenses/npm/p-finally.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/path-key.dep.yml
generated
BIN
.licenses/npm/path-key.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/pump.dep.yml
generated
BIN
.licenses/npm/pump.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/qs.dep.yml
generated
BIN
.licenses/npm/qs.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/semver-5.7.1.dep.yml
generated
BIN
.licenses/npm/semver-5.7.1.dep.yml
generated
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/shebang-command.dep.yml
generated
BIN
.licenses/npm/shebang-command.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/shebang-regex.dep.yml
generated
BIN
.licenses/npm/shebang-regex.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/signal-exit.dep.yml
generated
BIN
.licenses/npm/signal-exit.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/strip-eof.dep.yml
generated
BIN
.licenses/npm/strip-eof.dep.yml
generated
Binary file not shown.
BIN
.licenses/npm/universal-user-agent-5.0.0.dep.yml
generated
BIN
.licenses/npm/universal-user-agent-5.0.0.dep.yml
generated
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.licenses/npm/which.dep.yml
generated
BIN
.licenses/npm/which.dep.yml
generated
Binary file not shown.
52
CHANGELOG.md
52
CHANGELOG.md
@ -1,10 +1,60 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v3.6.0
|
||||||
|
- [Fix: Mark test scripts with Bash'isms to be run via Bash](https://github.com/actions/checkout/pull/1377)
|
||||||
|
- [Add option to fetch tags even if fetch-depth > 0](https://github.com/actions/checkout/pull/579)
|
||||||
|
|
||||||
|
## v3.5.3
|
||||||
|
- [Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in](https://github.com/actions/checkout/pull/1196)
|
||||||
|
- [Fix typos found by codespell](https://github.com/actions/checkout/pull/1287)
|
||||||
|
- [Add support for sparse checkouts](https://github.com/actions/checkout/pull/1369)
|
||||||
|
|
||||||
|
## v3.5.2
|
||||||
|
- [Fix api endpoint for GHES](https://github.com/actions/checkout/pull/1289)
|
||||||
|
|
||||||
|
## v3.5.1
|
||||||
|
- [Fix slow checkout on Windows](https://github.com/actions/checkout/pull/1246)
|
||||||
|
|
||||||
|
## v3.5.0
|
||||||
|
* [Add new public key for known_hosts](https://github.com/actions/checkout/pull/1237)
|
||||||
|
|
||||||
|
## v3.4.0
|
||||||
|
- [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)
|
||||||
|
|
||||||
|
## v3.3.0
|
||||||
|
- [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)
|
||||||
|
|
||||||
|
## v3.2.0
|
||||||
|
- [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:
|
61
README.md
61
README.md
@ -1,6 +1,4 @@
|
|||||||
<p align="center">
|
[![Build and Test](https://github.com/actions/checkout/actions/workflows/test.yml/badge.svg)](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 V3
|
# Checkout V3
|
||||||
|
|
||||||
@ -76,10 +74,23 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
|
|||||||
# Default: true
|
# Default: true
|
||||||
clean: ''
|
clean: ''
|
||||||
|
|
||||||
|
# Do a sparse checkout on given patterns. Each pattern should be separated with
|
||||||
|
# new lines
|
||||||
|
# Default: null
|
||||||
|
sparse-checkout: ''
|
||||||
|
|
||||||
|
# Specifies whether to use cone-mode when doing a sparse checkout.
|
||||||
|
# Default: true
|
||||||
|
sparse-checkout-cone-mode: ''
|
||||||
|
|
||||||
# Number of commits to fetch. 0 indicates all history for all branches and tags.
|
# Number of commits to fetch. 0 indicates all history for all branches and tags.
|
||||||
# Default: 1
|
# Default: 1
|
||||||
fetch-depth: ''
|
fetch-depth: ''
|
||||||
|
|
||||||
|
# Whether to fetch tags, even if fetch-depth > 0.
|
||||||
|
# Default: false
|
||||||
|
fetch-tags: ''
|
||||||
|
|
||||||
# Whether to download Git-LFS files
|
# Whether to download Git-LFS files
|
||||||
# Default: false
|
# Default: false
|
||||||
lfs: ''
|
lfs: ''
|
||||||
@ -92,11 +103,25 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
|
|||||||
#
|
#
|
||||||
# Default: false
|
# Default: false
|
||||||
submodules: ''
|
submodules: ''
|
||||||
|
|
||||||
|
# Add repository path as safe.directory for Git global config by running `git
|
||||||
|
# config --global --add safe.directory <path>`
|
||||||
|
# Default: true
|
||||||
|
set-safe-directory: ''
|
||||||
|
|
||||||
|
# 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
|
||||||
|
github-server-url: ''
|
||||||
```
|
```
|
||||||
<!-- end usage -->
|
<!-- end usage -->
|
||||||
|
|
||||||
# Scenarios
|
# Scenarios
|
||||||
|
|
||||||
|
- [Fetch only the root files](#Fetch-only-the-root-files)
|
||||||
|
- [Fetch only the root files and `.github` and `src` folder](#Fetch-only-the-root-files-and-github-and-src-folder)
|
||||||
|
- [Fetch only a single file](#Fetch-only-a-single-file)
|
||||||
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
|
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
|
||||||
- [Checkout a different branch](#Checkout-a-different-branch)
|
- [Checkout a different branch](#Checkout-a-different-branch)
|
||||||
- [Checkout HEAD^](#Checkout-HEAD)
|
- [Checkout HEAD^](#Checkout-HEAD)
|
||||||
@ -107,6 +132,34 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
|
|||||||
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
|
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
|
||||||
- [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token)
|
- [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token)
|
||||||
|
|
||||||
|
## Fetch only the root files
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
sparse-checkout: .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fetch only the root files and `.github` and `src` folder
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
.github
|
||||||
|
src
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fetch only a single file
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
README.md
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
```
|
||||||
|
|
||||||
## Fetch all history for all tags and branches
|
## Fetch all history for all tags and branches
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@ -146,6 +199,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
|
|||||||
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)
|
||||||
|
|
||||||
@ -159,6 +213,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
|
|||||||
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)
|
||||||
|
|
||||||
|
@ -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 =
|
||||||
@ -643,10 +675,11 @@ describe('git-auth-helper tests', () => {
|
|||||||
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
const removeGlobalAuth_removesOverride = 'removeGlobalAuth removes override'
|
const removeGlobalConfig_removesOverride =
|
||||||
it(removeGlobalAuth_removesOverride, async () => {
|
'removeGlobalConfig removes override'
|
||||||
|
it(removeGlobalConfig_removesOverride, async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
await setup(removeGlobalAuth_removesOverride)
|
await setup(removeGlobalConfig_removesOverride)
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
await authHelper.configureGlobalAuth()
|
await authHelper.configureGlobalAuth()
|
||||||
@ -655,7 +688,7 @@ describe('git-auth-helper tests', () => {
|
|||||||
await fs.promises.stat(path.join(git.env['HOME'], '.gitconfig'))
|
await fs.promises.stat(path.join(git.env['HOME'], '.gitconfig'))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await authHelper.removeGlobalAuth()
|
await authHelper.removeGlobalConfig()
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(git.env['HOME']).toBeUndefined()
|
expect(git.env['HOME']).toBeUndefined()
|
||||||
@ -694,6 +727,8 @@ async function setup(testName: string): Promise<void> {
|
|||||||
branchDelete: jest.fn(),
|
branchDelete: jest.fn(),
|
||||||
branchExists: jest.fn(),
|
branchExists: jest.fn(),
|
||||||
branchList: jest.fn(),
|
branchList: jest.fn(),
|
||||||
|
sparseCheckout: jest.fn(),
|
||||||
|
sparseCheckoutNonConeMode: jest.fn(),
|
||||||
checkout: jest.fn(),
|
checkout: jest.fn(),
|
||||||
checkoutDetach: jest.fn(),
|
checkoutDetach: jest.fn(),
|
||||||
config: jest.fn(
|
config: jest.fn(
|
||||||
@ -737,6 +772,9 @@ async function setup(testName: string): Promise<void> {
|
|||||||
return ''
|
return ''
|
||||||
}),
|
}),
|
||||||
submoduleSync: jest.fn(),
|
submoduleSync: jest.fn(),
|
||||||
|
submoduleStatus: jest.fn(async () => {
|
||||||
|
return true
|
||||||
|
}),
|
||||||
submoduleUpdate: jest.fn(),
|
submoduleUpdate: jest.fn(),
|
||||||
tagExists: jest.fn(),
|
tagExists: jest.fn(),
|
||||||
tryClean: jest.fn(),
|
tryClean: jest.fn(),
|
||||||
@ -764,7 +802,10 @@ async function setup(testName: string): Promise<void> {
|
|||||||
authToken: 'some auth token',
|
authToken: 'some auth token',
|
||||||
clean: true,
|
clean: true,
|
||||||
commit: '',
|
commit: '',
|
||||||
|
sparseCheckout: [],
|
||||||
|
sparseCheckoutConeMode: true,
|
||||||
fetchDepth: 1,
|
fetchDepth: 1,
|
||||||
|
fetchTags: false,
|
||||||
lfs: false,
|
lfs: false,
|
||||||
submodules: false,
|
submodules: false,
|
||||||
nestedSubmodules: false,
|
nestedSubmodules: false,
|
||||||
@ -776,7 +817,9 @@ async function setup(testName: string): Promise<void> {
|
|||||||
sshKey: sshPath ? 'some ssh private key' : '',
|
sshKey: sshPath ? 'some ssh private key' : '',
|
||||||
sshKnownHosts: '',
|
sshKnownHosts: '',
|
||||||
sshStrict: true,
|
sshStrict: true,
|
||||||
workflowOrganizationId: 123456
|
workflowOrganizationId: 123456,
|
||||||
|
setSafeDirectory: true,
|
||||||
|
githubServerUrl: githubServerUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
266
__test__/git-command-manager.test.ts
Normal file
266
__test__/git-command-manager.test.ts
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
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
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
|
||||||
|
let branches = await git.branchList(false)
|
||||||
|
|
||||||
|
expect(branches).toHaveLength(1)
|
||||||
|
expect(branches.sort()).toEqual(['foo'].sort())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Test fetchDepth and fetchTags options', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
|
||||||
|
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
console.log(args, options.listeners.stdout)
|
||||||
|
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('2.18'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call execGit with the correct arguments when fetchDepth is 0 and fetchTags is true', async () => {
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
|
||||||
|
const refSpec = ['refspec1', 'refspec2']
|
||||||
|
const options = {
|
||||||
|
filter: 'filterValue',
|
||||||
|
fetchDepth: 0,
|
||||||
|
fetchTags: true
|
||||||
|
}
|
||||||
|
|
||||||
|
await git.fetch(refSpec, options)
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
[
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'fetch',
|
||||||
|
'--prune',
|
||||||
|
'--progress',
|
||||||
|
'--no-recurse-submodules',
|
||||||
|
'--filter=filterValue',
|
||||||
|
'origin',
|
||||||
|
'refspec1',
|
||||||
|
'refspec2'
|
||||||
|
],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call execGit with the correct arguments when fetchDepth is 0 and fetchTags is false', async () => {
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
const refSpec = ['refspec1', 'refspec2']
|
||||||
|
const options = {
|
||||||
|
filter: 'filterValue',
|
||||||
|
fetchDepth: 0,
|
||||||
|
fetchTags: false
|
||||||
|
}
|
||||||
|
|
||||||
|
await git.fetch(refSpec, options)
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
[
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'fetch',
|
||||||
|
'--no-tags',
|
||||||
|
'--prune',
|
||||||
|
'--progress',
|
||||||
|
'--no-recurse-submodules',
|
||||||
|
'--filter=filterValue',
|
||||||
|
'origin',
|
||||||
|
'refspec1',
|
||||||
|
'refspec2'
|
||||||
|
],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call execGit with the correct arguments when fetchDepth is 1 and fetchTags is false', async () => {
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
const refSpec = ['refspec1', 'refspec2']
|
||||||
|
const options = {
|
||||||
|
filter: 'filterValue',
|
||||||
|
fetchDepth: 1,
|
||||||
|
fetchTags: false
|
||||||
|
}
|
||||||
|
|
||||||
|
await git.fetch(refSpec, options)
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
[
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'fetch',
|
||||||
|
'--no-tags',
|
||||||
|
'--prune',
|
||||||
|
'--progress',
|
||||||
|
'--no-recurse-submodules',
|
||||||
|
'--filter=filterValue',
|
||||||
|
'--depth=1',
|
||||||
|
'origin',
|
||||||
|
'refspec1',
|
||||||
|
'refspec2'
|
||||||
|
],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call execGit with the correct arguments when fetchDepth is 1 and fetchTags is true', async () => {
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
const workingDirectory = 'test'
|
||||||
|
const lfs = false
|
||||||
|
const doSparseCheckout = false
|
||||||
|
git = await commandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
|
const refSpec = ['refspec1', 'refspec2']
|
||||||
|
const options = {
|
||||||
|
filter: 'filterValue',
|
||||||
|
fetchDepth: 1,
|
||||||
|
fetchTags: true
|
||||||
|
}
|
||||||
|
|
||||||
|
await git.fetch(refSpec, options)
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
[
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'fetch',
|
||||||
|
'--prune',
|
||||||
|
'--progress',
|
||||||
|
'--no-recurse-submodules',
|
||||||
|
'--filter=filterValue',
|
||||||
|
'--depth=1',
|
||||||
|
'origin',
|
||||||
|
'refspec1',
|
||||||
|
'refspec2'
|
||||||
|
],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
@ -281,6 +281,65 @@ describe('git-directory-helper tests', () => {
|
|||||||
expect(git.branchDelete).toHaveBeenCalledWith(false, 'local-branch-2')
|
expect(git.branchDelete).toHaveBeenCalledWith(false, 'local-branch-2')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const cleanWhenSubmoduleStatusIsFalse =
|
||||||
|
'cleans when submodule status is false'
|
||||||
|
|
||||||
|
it(cleanWhenSubmoduleStatusIsFalse, async () => {
|
||||||
|
// Arrange
|
||||||
|
await setup(cleanWhenSubmoduleStatusIsFalse)
|
||||||
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
|
|
||||||
|
//mock bad submodule
|
||||||
|
|
||||||
|
const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
|
||||||
|
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
|
git,
|
||||||
|
repositoryPath,
|
||||||
|
repositoryUrl,
|
||||||
|
clean,
|
||||||
|
ref
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const files = await fs.promises.readdir(repositoryPath)
|
||||||
|
expect(files).toHaveLength(0)
|
||||||
|
expect(git.tryClean).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
const doesNotCleanWhenSubmoduleStatusIsTrue =
|
||||||
|
'does not clean when submodule status is true'
|
||||||
|
|
||||||
|
it(doesNotCleanWhenSubmoduleStatusIsTrue, async () => {
|
||||||
|
// Arrange
|
||||||
|
await setup(doesNotCleanWhenSubmoduleStatusIsTrue)
|
||||||
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
|
|
||||||
|
const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
|
||||||
|
submoduleStatus.mockImplementation(async (remote: boolean) => {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
|
git,
|
||||||
|
repositoryPath,
|
||||||
|
repositoryUrl,
|
||||||
|
clean,
|
||||||
|
ref
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
|
||||||
|
const files = await fs.promises.readdir(repositoryPath)
|
||||||
|
expect(files.sort()).toEqual(['.git', 'my-file'])
|
||||||
|
expect(git.tryClean).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
const removesLockFiles = 'removes lock files'
|
const removesLockFiles = 'removes lock files'
|
||||||
it(removesLockFiles, async () => {
|
it(removesLockFiles, async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
@ -403,6 +462,8 @@ async function setup(testName: string): Promise<void> {
|
|||||||
branchList: jest.fn(async () => {
|
branchList: jest.fn(async () => {
|
||||||
return []
|
return []
|
||||||
}),
|
}),
|
||||||
|
sparseCheckout: jest.fn(),
|
||||||
|
sparseCheckoutNonConeMode: jest.fn(),
|
||||||
checkout: jest.fn(),
|
checkout: jest.fn(),
|
||||||
checkoutDetach: jest.fn(),
|
checkoutDetach: jest.fn(),
|
||||||
config: jest.fn(),
|
config: jest.fn(),
|
||||||
@ -423,6 +484,9 @@ async function setup(testName: string): Promise<void> {
|
|||||||
submoduleForeach: jest.fn(),
|
submoduleForeach: jest.fn(),
|
||||||
submoduleSync: jest.fn(),
|
submoduleSync: jest.fn(),
|
||||||
submoduleUpdate: jest.fn(),
|
submoduleUpdate: jest.fn(),
|
||||||
|
submoduleStatus: jest.fn(async () => {
|
||||||
|
return true
|
||||||
|
}),
|
||||||
tagExists: jest.fn(),
|
tagExists: jest.fn(),
|
||||||
tryClean: jest.fn(async () => {
|
tryClean: jest.fn(async () => {
|
||||||
return true
|
return true
|
||||||
|
@ -79,12 +79,16 @@ describe('input-helper tests', () => {
|
|||||||
expect(settings.clean).toBe(true)
|
expect(settings.clean).toBe(true)
|
||||||
expect(settings.commit).toBeTruthy()
|
expect(settings.commit).toBeTruthy()
|
||||||
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
||||||
|
expect(settings.sparseCheckout).toBe(undefined)
|
||||||
|
expect(settings.sparseCheckoutConeMode).toBe(true)
|
||||||
expect(settings.fetchDepth).toBe(1)
|
expect(settings.fetchDepth).toBe(1)
|
||||||
|
expect(settings.fetchTags).toBe(false)
|
||||||
expect(settings.lfs).toBe(false)
|
expect(settings.lfs).toBe(false)
|
||||||
expect(settings.ref).toBe('refs/heads/some-ref')
|
expect(settings.ref).toBe('refs/heads/some-ref')
|
||||||
expect(settings.repositoryName).toBe('some-repo')
|
expect(settings.repositoryName).toBe('some-repo')
|
||||||
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)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('qualifies ref', async () => {
|
it('qualifies ref', async () => {
|
||||||
|
51
__test__/verify-sparse-checkout-non-cone-mode.sh
Executable file
51
__test__/verify-sparse-checkout-non-cone-mode.sh
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Verify .git folder
|
||||||
|
if [ ! -d "./sparse-checkout-non-cone-mode/.git" ]; then
|
||||||
|
echo "Expected ./sparse-checkout-non-cone-mode/.git folder to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify sparse-checkout (non-cone-mode)
|
||||||
|
cd sparse-checkout-non-cone-mode
|
||||||
|
|
||||||
|
ENABLED=$(git config --local --get-all core.sparseCheckout)
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
echo "Failed to verify that sparse-checkout is enabled"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that sparse-checkout is enabled
|
||||||
|
if [ "$ENABLED" != "true" ]; then
|
||||||
|
echo "Expected sparse-checkout to be enabled (is: $ENABLED)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SPARSE_CHECKOUT_FILE=$(git rev-parse --git-path info/sparse-checkout)
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
echo "Failed to validate sparse-checkout"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that sparse-checkout list is not empty
|
||||||
|
if [ ! -f "$SPARSE_CHECKOUT_FILE" ]; then
|
||||||
|
echo "Expected sparse-checkout file to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that all folders from sparse-checkout exists
|
||||||
|
for pattern in $(cat "$SPARSE_CHECKOUT_FILE")
|
||||||
|
do
|
||||||
|
if [ ! -d "${pattern#/}" ]; then
|
||||||
|
echo "Expected directory '${pattern#/}' to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Verify that the root directory is not checked out
|
||||||
|
if [ -f README.md ]; then
|
||||||
|
echo "Expected top-level files not to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
63
__test__/verify-sparse-checkout.sh
Executable file
63
__test__/verify-sparse-checkout.sh
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Verify .git folder
|
||||||
|
if [ ! -d "./sparse-checkout/.git" ]; then
|
||||||
|
echo "Expected ./sparse-checkout/.git folder to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify sparse-checkout
|
||||||
|
cd sparse-checkout
|
||||||
|
|
||||||
|
SPARSE=$(git sparse-checkout list)
|
||||||
|
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
echo "Failed to validate sparse-checkout"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that sparse-checkout list is not empty
|
||||||
|
if [ -z "$SPARSE" ]; then
|
||||||
|
echo "Expected sparse-checkout list to not be empty"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that all folders of the sparse checkout exist
|
||||||
|
for pattern in $SPARSE
|
||||||
|
do
|
||||||
|
if [ ! -d "$pattern" ]; then
|
||||||
|
echo "Expected directory '$pattern' to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
checkSparse () {
|
||||||
|
if [ ! -d "./$1" ]; then
|
||||||
|
echo "Expected directory '$1' to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for file in $(git ls-tree -r --name-only HEAD $1)
|
||||||
|
do
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo "Expected file '$file' to exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check that all folders and their children have been checked out
|
||||||
|
checkSparse __test__
|
||||||
|
checkSparse .github
|
||||||
|
checkSparse dist
|
||||||
|
|
||||||
|
# Check that only sparse-checkout folders have been checked out
|
||||||
|
for pattern in $(git ls-tree --name-only HEAD)
|
||||||
|
do
|
||||||
|
if [ -d "$pattern" ]; then
|
||||||
|
if [[ "$pattern" != "__test__" && "$pattern" != ".github" && "$pattern" != "dist" ]]; then
|
||||||
|
echo "Expected directory '$pattern' to not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
18
action.yml
18
action.yml
@ -53,9 +53,21 @@ inputs:
|
|||||||
clean:
|
clean:
|
||||||
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
|
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
|
||||||
default: true
|
default: true
|
||||||
|
sparse-checkout:
|
||||||
|
description: >
|
||||||
|
Do a sparse checkout on given patterns.
|
||||||
|
Each pattern should be separated with new lines
|
||||||
|
default: null
|
||||||
|
sparse-checkout-cone-mode:
|
||||||
|
description: >
|
||||||
|
Specifies whether to use cone-mode when doing a sparse checkout.
|
||||||
|
default: true
|
||||||
fetch-depth:
|
fetch-depth:
|
||||||
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
|
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
|
||||||
default: 1
|
default: 1
|
||||||
|
fetch-tags:
|
||||||
|
description: 'Whether to fetch tags, even if fetch-depth > 0.'
|
||||||
|
default: false
|
||||||
lfs:
|
lfs:
|
||||||
description: 'Whether to download Git-LFS files'
|
description: 'Whether to download Git-LFS files'
|
||||||
default: false
|
default: false
|
||||||
@ -68,6 +80,12 @@ inputs:
|
|||||||
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
|
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
|
||||||
converted to HTTPS.
|
converted to HTTPS.
|
||||||
default: false
|
default: false
|
||||||
|
set-safe-directory:
|
||||||
|
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
|
||||||
|
default: true
|
||||||
|
github-server-url:
|
||||||
|
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: false
|
||||||
runs:
|
runs:
|
||||||
using: node16
|
using: node16
|
||||||
main: dist/index.js
|
main: dist/index.js
|
||||||
|
@ -181,7 +181,7 @@ GITHUB_WORKSPACE=/home/runner/work/foo/foo
|
|||||||
RUNNER_WORKSPACE=/home/runner/work/foo
|
RUNNER_WORKSPACE=/home/runner/work/foo
|
||||||
```
|
```
|
||||||
|
|
||||||
V2 introduces a new contraint on the checkout path. The location must now be under `github.workspace`. Whereas the checkout@v1 constraint was one level up, under `runner.workspace`.
|
V2 introduces a new constraint on the checkout path. The location must now be under `github.workspace`. Whereas the checkout@v1 constraint was one level up, under `runner.workspace`.
|
||||||
|
|
||||||
V2 no longer changes `github.workspace` to follow wherever the self repo is checked-out.
|
V2 no longer changes `github.workspace` to follow wherever the self repo is checked-out.
|
||||||
|
|
||||||
|
45336
dist/index.js
vendored
45336
dist/index.js
vendored
File diff suppressed because one or more lines are too long
22759
package-lock.json
generated
22759
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "checkout",
|
"name": "checkout",
|
||||||
"version": "2.0.2",
|
"version": "3.6.0",
|
||||||
"description": "checkout action",
|
"description": "checkout action",
|
||||||
"main": "lib/main.js",
|
"main": "lib/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -28,10 +28,10 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/actions/checkout#readme",
|
"homepage": "https://github.com/actions/checkout#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.2.6",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/exec": "^1.0.1",
|
"@actions/exec": "^1.0.1",
|
||||||
"@actions/github": "^2.2.0",
|
"@actions/github": "^5.0.0",
|
||||||
"@actions/io": "^1.0.1",
|
"@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"
|
||||||
},
|
},
|
||||||
@ -39,11 +39,12 @@
|
|||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
"@types/node": "^12.7.12",
|
"@types/node": "^12.7.12",
|
||||||
"@types/uuid": "^3.4.6",
|
"@types/uuid": "^3.4.6",
|
||||||
"@typescript-eslint/parser": "^5.1.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@zeit/ncc": "^0.20.5",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
|
"@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.2.2",
|
"eslint-plugin-jest": "^25.7.0",
|
||||||
"jest": "^27.3.0",
|
"jest": "^27.3.0",
|
||||||
"jest-circus": "^27.3.0",
|
"jest-circus": "^27.3.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
|
@ -19,8 +19,9 @@ export interface IGitAuthHelper {
|
|||||||
configureAuth(): Promise<void>
|
configureAuth(): Promise<void>
|
||||||
configureGlobalAuth(): Promise<void>
|
configureGlobalAuth(): Promise<void>
|
||||||
configureSubmoduleAuth(): Promise<void>
|
configureSubmoduleAuth(): Promise<void>
|
||||||
|
configureTempGlobalConfig(): Promise<string>
|
||||||
removeAuth(): Promise<void>
|
removeAuth(): Promise<void>
|
||||||
removeGlobalAuth(): Promise<void>
|
removeGlobalConfig(): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAuthHelper(
|
export function createAuthHelper(
|
||||||
@ -51,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}`,
|
||||||
@ -80,7 +81,11 @@ class GitAuthHelper {
|
|||||||
await this.configureToken()
|
await this.configureToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
async configureGlobalAuth(): Promise<void> {
|
async configureTempGlobalConfig(): Promise<string> {
|
||||||
|
// Already setup global config
|
||||||
|
if (this.temporaryHomePath?.length > 0) {
|
||||||
|
return path.join(this.temporaryHomePath, '.gitconfig')
|
||||||
|
}
|
||||||
// Create a temp home directory
|
// Create a temp home directory
|
||||||
const runnerTemp = process.env['RUNNER_TEMP'] || ''
|
const runnerTemp = process.env['RUNNER_TEMP'] || ''
|
||||||
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
|
||||||
@ -110,13 +115,19 @@ class GitAuthHelper {
|
|||||||
await fs.promises.writeFile(newGitConfigPath, '')
|
await fs.promises.writeFile(newGitConfigPath, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Override HOME
|
||||||
// Override HOME
|
core.info(
|
||||||
core.info(
|
`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`
|
||||||
`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`
|
)
|
||||||
)
|
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
|
||||||
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
|
|
||||||
|
|
||||||
|
return newGitConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
async configureGlobalAuth(): Promise<void> {
|
||||||
|
// 'configureTempGlobalConfig' noops if already set, just returns the path
|
||||||
|
const newGitConfigPath = await this.configureTempGlobalConfig()
|
||||||
|
try {
|
||||||
// Configure the token
|
// Configure the token
|
||||||
await this.configureToken(newGitConfigPath, true)
|
await this.configureToken(newGitConfigPath, true)
|
||||||
|
|
||||||
@ -146,7 +157,8 @@ class GitAuthHelper {
|
|||||||
// by process creation audit events, which are commonly logged. For more information,
|
// 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
|
// refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
|
||||||
const output = await this.git.submoduleForeach(
|
const output = await this.git.submoduleForeach(
|
||||||
`git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url`,
|
// wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
|
||||||
|
`sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`,
|
||||||
this.settings.nestedSubmodules
|
this.settings.nestedSubmodules
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -181,10 +193,12 @@ class GitAuthHelper {
|
|||||||
await this.removeToken()
|
await this.removeToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeGlobalAuth(): Promise<void> {
|
async removeGlobalConfig(): Promise<void> {
|
||||||
core.debug(`Unsetting HOME override`)
|
if (this.temporaryHomePath?.length > 0) {
|
||||||
this.git.removeEnvironmentVariable('HOME')
|
core.debug(`Unsetting HOME override`)
|
||||||
await io.rmRF(this.temporaryHomePath)
|
this.git.removeEnvironmentVariable('HOME')
|
||||||
|
await io.rmRF(this.temporaryHomePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async configureSsh(): Promise<void> {
|
private async configureSsh(): Promise<void> {
|
||||||
@ -233,7 +247,7 @@ class GitAuthHelper {
|
|||||||
if (this.settings.sshKnownHosts) {
|
if (this.settings.sshKnownHosts) {
|
||||||
knownHosts += `# Begin from input known hosts\n${this.settings.sshKnownHosts}\n# end from input known hosts\n`
|
knownHosts += `# Begin from input known hosts\n${this.settings.sshKnownHosts}\n# end from input known hosts\n`
|
||||||
}
|
}
|
||||||
knownHosts += `# Begin implicitly added github.com\ngithub.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n# End implicitly added github.com\n`
|
knownHosts += `# Begin implicitly added github.com\ngithub.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=\n# End implicitly added github.com\n`
|
||||||
this.sshKnownHostsPath = path.join(runnerTemp, `${uniqueId}_known_hosts`)
|
this.sshKnownHostsPath = path.join(runnerTemp, `${uniqueId}_known_hosts`)
|
||||||
stateHelper.setSshKnownHostsPath(this.sshKnownHostsPath)
|
stateHelper.setSshKnownHostsPath(this.sshKnownHostsPath)
|
||||||
await fs.promises.writeFile(this.sshKnownHostsPath, knownHosts)
|
await fs.promises.writeFile(this.sshKnownHostsPath, knownHosts)
|
||||||
@ -352,7 +366,8 @@ class GitAuthHelper {
|
|||||||
|
|
||||||
const pattern = regexpHelper.escape(configKey)
|
const pattern = regexpHelper.escape(configKey)
|
||||||
await this.git.submoduleForeach(
|
await this.git.submoduleForeach(
|
||||||
`git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :`,
|
// wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
|
||||||
|
`sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
|
import * as fs from 'fs'
|
||||||
import * as fshelper from './fs-helper'
|
import * as fshelper from './fs-helper'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
@ -16,6 +17,8 @@ export interface IGitCommandManager {
|
|||||||
branchDelete(remote: boolean, branch: string): Promise<void>
|
branchDelete(remote: boolean, branch: string): Promise<void>
|
||||||
branchExists(remote: boolean, pattern: string): Promise<boolean>
|
branchExists(remote: boolean, pattern: string): Promise<boolean>
|
||||||
branchList(remote: boolean): Promise<string[]>
|
branchList(remote: boolean): Promise<string[]>
|
||||||
|
sparseCheckout(sparseCheckout: string[]): Promise<void>
|
||||||
|
sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void>
|
||||||
checkout(ref: string, startPoint: string): Promise<void>
|
checkout(ref: string, startPoint: string): Promise<void>
|
||||||
checkoutDetach(): Promise<void>
|
checkoutDetach(): Promise<void>
|
||||||
config(
|
config(
|
||||||
@ -25,7 +28,14 @@ export interface IGitCommandManager {
|
|||||||
add?: boolean
|
add?: boolean
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
|
configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
|
||||||
fetch(refSpec: string[], fetchDepth?: number): Promise<void>
|
fetch(
|
||||||
|
refSpec: string[],
|
||||||
|
options: {
|
||||||
|
filter?: string
|
||||||
|
fetchDepth?: number
|
||||||
|
fetchTags?: boolean
|
||||||
|
}
|
||||||
|
): Promise<void>
|
||||||
getDefaultBranch(repositoryUrl: string): Promise<string>
|
getDefaultBranch(repositoryUrl: string): Promise<string>
|
||||||
getWorkingDirectory(): string
|
getWorkingDirectory(): string
|
||||||
init(): Promise<void>
|
init(): Promise<void>
|
||||||
@ -41,6 +51,7 @@ export interface IGitCommandManager {
|
|||||||
submoduleForeach(command: string, recursive: boolean): Promise<string>
|
submoduleForeach(command: string, recursive: boolean): Promise<string>
|
||||||
submoduleSync(recursive: boolean): Promise<void>
|
submoduleSync(recursive: boolean): Promise<void>
|
||||||
submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
|
submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
|
||||||
|
submoduleStatus(): Promise<boolean>
|
||||||
tagExists(pattern: string): Promise<boolean>
|
tagExists(pattern: string): Promise<boolean>
|
||||||
tryClean(): Promise<boolean>
|
tryClean(): Promise<boolean>
|
||||||
tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
|
tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
|
||||||
@ -51,9 +62,14 @@ export interface IGitCommandManager {
|
|||||||
|
|
||||||
export async function createCommandManager(
|
export async function createCommandManager(
|
||||||
workingDirectory: string,
|
workingDirectory: string,
|
||||||
lfs: boolean
|
lfs: boolean,
|
||||||
|
doSparseCheckout: boolean
|
||||||
): Promise<IGitCommandManager> {
|
): Promise<IGitCommandManager> {
|
||||||
return await GitCommandManager.createCommandManager(workingDirectory, lfs)
|
return await GitCommandManager.createCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class GitCommandManager {
|
class GitCommandManager {
|
||||||
@ -63,6 +79,7 @@ class GitCommandManager {
|
|||||||
}
|
}
|
||||||
private gitPath = ''
|
private gitPath = ''
|
||||||
private lfs = false
|
private lfs = false
|
||||||
|
private doSparseCheckout = false
|
||||||
private workingDirectory = ''
|
private workingDirectory = ''
|
||||||
|
|
||||||
// Private constructor; use createCommandManager()
|
// Private constructor; use createCommandManager()
|
||||||
@ -94,8 +111,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,24 +124,73 @@ 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.slice('refs/heads/'.length)
|
errline: (data: Buffer) => {
|
||||||
} else if (branch.startsWith('refs/remotes/')) {
|
errline.push(data.toString())
|
||||||
branch = branch.slice('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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sparseCheckout(sparseCheckout: string[]): Promise<void> {
|
||||||
|
await this.execGit(['sparse-checkout', 'set', ...sparseCheckout])
|
||||||
|
}
|
||||||
|
|
||||||
|
async sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void> {
|
||||||
|
await this.execGit(['config', 'core.sparseCheckout', 'true'])
|
||||||
|
const output = await this.execGit([
|
||||||
|
'rev-parse',
|
||||||
|
'--git-path',
|
||||||
|
'info/sparse-checkout'
|
||||||
|
])
|
||||||
|
const sparseCheckoutPath = path.join(
|
||||||
|
this.workingDirectory,
|
||||||
|
output.stdout.trimRight()
|
||||||
|
)
|
||||||
|
await fs.promises.appendFile(
|
||||||
|
sparseCheckoutPath,
|
||||||
|
`\n${sparseCheckout.join('\n')}\n`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async checkout(ref: string, startPoint: string): Promise<void> {
|
async checkout(ref: string, startPoint: string): Promise<void> {
|
||||||
const args = ['checkout', '--progress', '--force']
|
const args = ['checkout', '--progress', '--force']
|
||||||
if (startPoint) {
|
if (startPoint) {
|
||||||
@ -170,15 +239,23 @@ class GitCommandManager {
|
|||||||
return output.exitCode === 0
|
return output.exitCode === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetch(refSpec: string[], fetchDepth?: number): Promise<void> {
|
async fetch(
|
||||||
|
refSpec: string[],
|
||||||
|
options: {filter?: string; fetchDepth?: number; fetchTags?: boolean}
|
||||||
|
): Promise<void> {
|
||||||
const args = ['-c', 'protocol.version=2', 'fetch']
|
const args = ['-c', 'protocol.version=2', 'fetch']
|
||||||
if (!refSpec.some(x => x === refHelper.tagsRefSpec)) {
|
if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) {
|
||||||
args.push('--no-tags')
|
args.push('--no-tags')
|
||||||
}
|
}
|
||||||
|
|
||||||
args.push('--prune', '--progress', '--no-recurse-submodules')
|
args.push('--prune', '--progress', '--no-recurse-submodules')
|
||||||
if (fetchDepth && fetchDepth > 0) {
|
|
||||||
args.push(`--depth=${fetchDepth}`)
|
if (options.filter) {
|
||||||
|
args.push(`--filter=${options.filter}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.fetchDepth && options.fetchDepth > 0) {
|
||||||
|
args.push(`--depth=${options.fetchDepth}`)
|
||||||
} else if (
|
} else if (
|
||||||
fshelper.fileExistsSync(
|
fshelper.fileExistsSync(
|
||||||
path.join(this.workingDirectory, '.git', 'shallow')
|
path.join(this.workingDirectory, '.git', 'shallow')
|
||||||
@ -257,8 +334,8 @@ class GitCommandManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async log1(format?: string): Promise<string> {
|
async log1(format?: string): Promise<string> {
|
||||||
var args = format ? ['log', '-1', format] : ['log', '-1']
|
const args = format ? ['log', '-1', format] : ['log', '-1']
|
||||||
var silent = format ? false : true
|
const silent = format ? false : true
|
||||||
const output = await this.execGit(args, false, silent)
|
const output = await this.execGit(args, false, silent)
|
||||||
return output.stdout
|
return output.stdout
|
||||||
}
|
}
|
||||||
@ -326,6 +403,12 @@ class GitCommandManager {
|
|||||||
await this.execGit(args)
|
await this.execGit(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async submoduleStatus(): Promise<boolean> {
|
||||||
|
const output = await this.execGit(['submodule', 'status'], true)
|
||||||
|
core.debug(output.stdout)
|
||||||
|
return output.exitCode === 0
|
||||||
|
}
|
||||||
|
|
||||||
async tagExists(pattern: string): Promise<boolean> {
|
async tagExists(pattern: string): Promise<boolean> {
|
||||||
const output = await this.execGit(['tag', '--list', pattern])
|
const output = await this.execGit(['tag', '--list', pattern])
|
||||||
return !!output.stdout.trim()
|
return !!output.stdout.trim()
|
||||||
@ -385,17 +468,23 @@ class GitCommandManager {
|
|||||||
|
|
||||||
static async createCommandManager(
|
static async createCommandManager(
|
||||||
workingDirectory: string,
|
workingDirectory: string,
|
||||||
lfs: boolean
|
lfs: boolean,
|
||||||
|
doSparseCheckout: boolean
|
||||||
): Promise<GitCommandManager> {
|
): Promise<GitCommandManager> {
|
||||||
const result = new GitCommandManager()
|
const result = new GitCommandManager()
|
||||||
await result.initializeCommandManager(workingDirectory, lfs)
|
await result.initializeCommandManager(
|
||||||
|
workingDirectory,
|
||||||
|
lfs,
|
||||||
|
doSparseCheckout
|
||||||
|
)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
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,28 +498,36 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initializeCommandManager(
|
private async initializeCommandManager(
|
||||||
workingDirectory: string,
|
workingDirectory: string,
|
||||||
lfs: boolean
|
lfs: boolean,
|
||||||
|
doSparseCheckout: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.workingDirectory = workingDirectory
|
this.workingDirectory = workingDirectory
|
||||||
|
|
||||||
@ -493,6 +590,16 @@ class GitCommandManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.doSparseCheckout = doSparseCheckout
|
||||||
|
if (this.doSparseCheckout) {
|
||||||
|
// The `git sparse-checkout` command was introduced in Git v2.25.0
|
||||||
|
const minimumGitSparseCheckoutVersion = new GitVersion('2.25')
|
||||||
|
if (!gitVersion.checkMinimum(minimumGitSparseCheckoutVersion)) {
|
||||||
|
throw new Error(
|
||||||
|
`Minimum Git version required for sparse checkout is ${minimumGitSparseCheckoutVersion}. Your git ('${this.gitPath}') is ${gitVersion}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Set the user agent
|
// Set the user agent
|
||||||
const gitHttpUserAgent = `git/${gitVersion} (github-actions-checkout)`
|
const gitHttpUserAgent = `git/${gitVersion} (github-actions-checkout)`
|
||||||
core.debug(`Set git useragent to: ${gitHttpUserAgent}`)
|
core.debug(`Set git useragent to: ${gitHttpUserAgent}`)
|
||||||
|
@ -81,12 +81,18 @@ export async function prepareExistingDirectory(
|
|||||||
}
|
}
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
|
// Check for submodules and delete any existing files if submodules are present
|
||||||
|
if (!(await git.submoduleStatus())) {
|
||||||
|
remove = true
|
||||||
|
core.info('Bad Submodules found, removing existing files')
|
||||||
|
}
|
||||||
|
|
||||||
// Clean
|
// Clean
|
||||||
if (clean) {
|
if (clean) {
|
||||||
core.startGroup('Cleaning the repository')
|
core.startGroup('Cleaning the repository')
|
||||||
if (!(await git.tryClean())) {
|
if (!(await git.tryClean())) {
|
||||||
core.debug(
|
core.debug(
|
||||||
`The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`
|
`The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For further investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`
|
||||||
)
|
)
|
||||||
remove = true
|
remove = true
|
||||||
} else if (!(await git.tryReset())) {
|
} else if (!(await git.tryReset())) {
|
||||||
|
@ -36,68 +36,95 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
const git = await getGitCommandManager(settings)
|
const git = await getGitCommandManager(settings)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
// Prepare existing directory, otherwise recreate
|
let authHelper: gitAuthHelper.IGitAuthHelper | null = null
|
||||||
if (isExisting) {
|
try {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
if (git) {
|
||||||
git,
|
authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
settings.repositoryPath,
|
if (settings.setSafeDirectory) {
|
||||||
repositoryUrl,
|
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
|
||||||
settings.clean,
|
// Otherwise all git commands we run in a container fail
|
||||||
settings.ref
|
await authHelper.configureTempGlobalConfig()
|
||||||
)
|
core.info(
|
||||||
}
|
`Adding repository directory to the temporary git global config as a safe directory`
|
||||||
|
)
|
||||||
|
|
||||||
if (!git) {
|
await git
|
||||||
// Downloading using REST API
|
.config('safe.directory', settings.repositoryPath, true, true)
|
||||||
core.info(`The repository will be downloaded using the GitHub REST API`)
|
.catch(error => {
|
||||||
core.info(
|
core.info(
|
||||||
`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
|
`Failed to initialize safe directory with error: ${error}`
|
||||||
)
|
)
|
||||||
if (settings.submodules) {
|
})
|
||||||
throw new Error(
|
|
||||||
`Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
|
stateHelper.setSafeDirectory()
|
||||||
)
|
}
|
||||||
} else if (settings.sshKey) {
|
}
|
||||||
throw new Error(
|
|
||||||
`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
|
// Prepare existing directory, otherwise recreate
|
||||||
|
if (isExisting) {
|
||||||
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
|
git,
|
||||||
|
settings.repositoryPath,
|
||||||
|
repositoryUrl,
|
||||||
|
settings.clean,
|
||||||
|
settings.ref
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await githubApiHelper.downloadRepository(
|
if (!git) {
|
||||||
settings.authToken,
|
// Downloading using REST API
|
||||||
settings.repositoryOwner,
|
core.info(`The repository will be downloaded using the GitHub REST API`)
|
||||||
settings.repositoryName,
|
core.info(
|
||||||
settings.ref,
|
`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
|
||||||
settings.commit,
|
)
|
||||||
settings.repositoryPath
|
if (settings.submodules) {
|
||||||
)
|
throw new Error(
|
||||||
return
|
`Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
|
||||||
}
|
)
|
||||||
|
} else if (settings.sshKey) {
|
||||||
|
throw new Error(
|
||||||
|
`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Save state for POST action
|
await githubApiHelper.downloadRepository(
|
||||||
stateHelper.setRepositoryPath(settings.repositoryPath)
|
settings.authToken,
|
||||||
|
settings.repositoryOwner,
|
||||||
|
settings.repositoryName,
|
||||||
|
settings.ref,
|
||||||
|
settings.commit,
|
||||||
|
settings.repositoryPath,
|
||||||
|
settings.githubServerUrl
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the repository
|
// Save state for POST action
|
||||||
if (
|
stateHelper.setRepositoryPath(settings.repositoryPath)
|
||||||
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
|
||||||
) {
|
// Initialize the repository
|
||||||
core.startGroup('Initializing the repository')
|
if (
|
||||||
await git.init()
|
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
||||||
await git.remoteAdd('origin', repositoryUrl)
|
) {
|
||||||
|
core.startGroup('Initializing the repository')
|
||||||
|
await git.init()
|
||||||
|
await git.remoteAdd('origin', repositoryUrl)
|
||||||
|
core.endGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable automatic garbage collection
|
||||||
|
core.startGroup('Disabling automatic garbage collection')
|
||||||
|
if (!(await git.tryDisableAutomaticGarbageCollection())) {
|
||||||
|
core.warning(
|
||||||
|
`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`
|
||||||
|
)
|
||||||
|
}
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
|
||||||
|
|
||||||
// Disable automatic garbage collection
|
// If we didn't initialize it above, do it now
|
||||||
core.startGroup('Disabling automatic garbage collection')
|
if (!authHelper) {
|
||||||
if (!(await git.tryDisableAutomaticGarbageCollection())) {
|
authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
core.warning(
|
}
|
||||||
`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
|
||||||
try {
|
|
||||||
// Configure auth
|
// Configure auth
|
||||||
core.startGroup('Setting up auth')
|
core.startGroup('Setting up auth')
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
@ -112,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()
|
||||||
@ -125,23 +153,31 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
|
|
||||||
// Fetch
|
// Fetch
|
||||||
core.startGroup('Fetching the repository')
|
core.startGroup('Fetching the repository')
|
||||||
|
const fetchOptions: {
|
||||||
|
filter?: string
|
||||||
|
fetchDepth?: number
|
||||||
|
fetchTags?: boolean
|
||||||
|
} = {}
|
||||||
|
if (settings.sparseCheckout) fetchOptions.filter = 'blob:none'
|
||||||
if (settings.fetchDepth <= 0) {
|
if (settings.fetchDepth <= 0) {
|
||||||
// Fetch all branches and tags
|
// Fetch all branches and tags
|
||||||
let refSpec = refHelper.getRefSpecForAllHistory(
|
let refSpec = refHelper.getRefSpecForAllHistory(
|
||||||
settings.ref,
|
settings.ref,
|
||||||
settings.commit
|
settings.commit
|
||||||
)
|
)
|
||||||
await git.fetch(refSpec)
|
await git.fetch(refSpec, fetchOptions)
|
||||||
|
|
||||||
// When all history is fetched, the ref we're interested in may have moved to a different
|
// When all history is fetched, the ref we're interested in may have moved to a different
|
||||||
// commit (push or force push). If so, fetch again with a targeted refspec.
|
// commit (push or force push). If so, fetch again with a targeted refspec.
|
||||||
if (!(await refHelper.testRef(git, settings.ref, settings.commit))) {
|
if (!(await refHelper.testRef(git, settings.ref, settings.commit))) {
|
||||||
refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
|
refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
|
||||||
await git.fetch(refSpec)
|
await git.fetch(refSpec, fetchOptions)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
fetchOptions.fetchDepth = settings.fetchDepth
|
||||||
|
fetchOptions.fetchTags = settings.fetchTags
|
||||||
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
|
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
|
||||||
await git.fetch(refSpec, settings.fetchDepth)
|
await git.fetch(refSpec, fetchOptions)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
@ -157,12 +193,24 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
// LFS fetch
|
// LFS fetch
|
||||||
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
|
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
|
||||||
// Explicit lfs fetch will fetch lfs objects in parallel.
|
// Explicit lfs fetch will fetch lfs objects in parallel.
|
||||||
if (settings.lfs) {
|
// For sparse checkouts, let `checkout` fetch the needed objects lazily.
|
||||||
|
if (settings.lfs && !settings.sparseCheckout) {
|
||||||
core.startGroup('Fetching LFS objects')
|
core.startGroup('Fetching LFS objects')
|
||||||
await git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref)
|
await git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sparse checkout
|
||||||
|
if (settings.sparseCheckout) {
|
||||||
|
core.startGroup('Setting up sparse checkout')
|
||||||
|
if (settings.sparseCheckoutConeMode) {
|
||||||
|
await git.sparseCheckout(settings.sparseCheckout)
|
||||||
|
} else {
|
||||||
|
await git.sparseCheckoutNonConeMode(settings.sparseCheckout)
|
||||||
|
}
|
||||||
|
core.endGroup()
|
||||||
|
}
|
||||||
|
|
||||||
// Checkout
|
// Checkout
|
||||||
core.startGroup('Checking out the ref')
|
core.startGroup('Checking out the ref')
|
||||||
await git.checkout(checkoutInfo.ref, checkoutInfo.startPoint)
|
await git.checkout(checkoutInfo.ref, checkoutInfo.startPoint)
|
||||||
@ -170,34 +218,26 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
|
|
||||||
// Submodules
|
// Submodules
|
||||||
if (settings.submodules) {
|
if (settings.submodules) {
|
||||||
try {
|
// Temporarily override global config
|
||||||
// Temporarily override global config
|
core.startGroup('Setting up auth for fetching submodules')
|
||||||
core.startGroup('Setting up auth for fetching submodules')
|
await authHelper.configureGlobalAuth()
|
||||||
await authHelper.configureGlobalAuth()
|
core.endGroup()
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
// Checkout submodules
|
// Checkout submodules
|
||||||
core.startGroup('Fetching submodules')
|
core.startGroup('Fetching submodules')
|
||||||
await git.submoduleSync(settings.nestedSubmodules)
|
await git.submoduleSync(settings.nestedSubmodules)
|
||||||
await git.submoduleUpdate(
|
await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
|
||||||
settings.fetchDepth,
|
await git.submoduleForeach(
|
||||||
settings.nestedSubmodules
|
'git config --local gc.auto 0',
|
||||||
)
|
settings.nestedSubmodules
|
||||||
await git.submoduleForeach(
|
)
|
||||||
'git config --local gc.auto 0',
|
core.endGroup()
|
||||||
settings.nestedSubmodules
|
|
||||||
)
|
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
// Persist credentials
|
// Persist credentials
|
||||||
if (settings.persistCredentials) {
|
if (settings.persistCredentials) {
|
||||||
core.startGroup('Persisting credentials for submodules')
|
core.startGroup('Persisting credentials for submodules')
|
||||||
await authHelper.configureSubmoduleAuth()
|
await authHelper.configureSubmoduleAuth()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// Remove temporary global config override
|
|
||||||
await authHelper.removeGlobalAuth()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,14 +254,18 @@ 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
|
||||||
if (!settings.persistCredentials) {
|
if (authHelper) {
|
||||||
core.startGroup('Removing auth')
|
if (!settings.persistCredentials) {
|
||||||
await authHelper.removeAuth()
|
core.startGroup('Removing auth')
|
||||||
core.endGroup()
|
await authHelper.removeAuth()
|
||||||
|
core.endGroup()
|
||||||
|
}
|
||||||
|
authHelper.removeGlobalConfig()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,14 +281,37 @@ export async function cleanup(repositoryPath: string): Promise<void> {
|
|||||||
|
|
||||||
let git: IGitCommandManager
|
let git: IGitCommandManager
|
||||||
try {
|
try {
|
||||||
git = await gitCommandManager.createCommandManager(repositoryPath, false)
|
git = await gitCommandManager.createCommandManager(
|
||||||
|
repositoryPath,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
} catch {
|
} catch {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove auth
|
// Remove auth
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git)
|
const authHelper = gitAuthHelper.createAuthHelper(git)
|
||||||
await authHelper.removeAuth()
|
try {
|
||||||
|
if (stateHelper.PostSetSafeDirectory) {
|
||||||
|
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
|
||||||
|
// Otherwise all git commands we run in a container fail
|
||||||
|
await authHelper.configureTempGlobalConfig()
|
||||||
|
core.info(
|
||||||
|
`Adding repository directory to the temporary git global config as a safe directory`
|
||||||
|
)
|
||||||
|
|
||||||
|
await git
|
||||||
|
.config('safe.directory', repositoryPath, true, true)
|
||||||
|
.catch(error => {
|
||||||
|
core.info(`Failed to initialize safe directory with error: ${error}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await authHelper.removeAuth()
|
||||||
|
} finally {
|
||||||
|
await authHelper.removeGlobalConfig()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getGitCommandManager(
|
async function getGitCommandManager(
|
||||||
@ -254,7 +321,8 @@ async function getGitCommandManager(
|
|||||||
try {
|
try {
|
||||||
return await gitCommandManager.createCommandManager(
|
return await gitCommandManager.createCommandManager(
|
||||||
settings.repositoryPath,
|
settings.repositoryPath,
|
||||||
settings.lfs
|
settings.lfs,
|
||||||
|
settings.sparseCheckout != null
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Git is required for LFS
|
// Git is required for LFS
|
||||||
|
@ -29,11 +29,26 @@ export interface IGitSourceSettings {
|
|||||||
*/
|
*/
|
||||||
clean: boolean
|
clean: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array of folders to make the sparse checkout
|
||||||
|
*/
|
||||||
|
sparseCheckout: string[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether to use cone mode in the sparse checkout (if any)
|
||||||
|
*/
|
||||||
|
sparseCheckoutConeMode: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The depth when fetching
|
* The depth when fetching
|
||||||
*/
|
*/
|
||||||
fetchDepth: number
|
fetchDepth: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch tags, even if fetchDepth > 0 (default: false)
|
||||||
|
*/
|
||||||
|
fetchTags: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether to fetch LFS objects
|
* Indicates whether to fetch LFS objects
|
||||||
*/
|
*/
|
||||||
@ -78,4 +93,14 @@ export interface IGitSourceSettings {
|
|||||||
* Organization ID for the currently running workflow (used for auth settings)
|
* Organization ID for the currently running workflow (used for auth settings)
|
||||||
*/
|
*/
|
||||||
workflowOrganizationId: number | undefined
|
workflowOrganizationId: number | undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether to add repositoryPath as safe.directory in git global config
|
||||||
|
*/
|
||||||
|
setSafeDirectory: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User override on the GitHub Server/Host URL that hosts the repository to be cloned
|
||||||
|
*/
|
||||||
|
githubServerUrl: string | undefined
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ 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 {getServerApiUrl} from './url-helper'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
|
||||||
@ -17,18 +17,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,15 +80,18 @@ 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 = github.getOctokit(authToken, {
|
||||||
|
baseUrl: getServerApiUrl(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
|
||||||
const response = await octokit.repos.get({owner, repo})
|
const response = await octokit.rest.repos.get({owner, repo})
|
||||||
result = response.data.default_branch
|
result = response.data.default_branch
|
||||||
assert.ok(result, 'default_branch cannot be empty')
|
assert.ok(result, 'default_branch cannot be empty')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -121,21 +125,19 @@ 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 = github.getOctokit(authToken, {
|
||||||
const params: Octokit.ReposGetArchiveLinkParams = {
|
baseUrl: getServerApiUrl(baseUrl)
|
||||||
|
})
|
||||||
|
const download = IS_WINDOWS
|
||||||
|
? octokit.rest.repos.downloadZipballArchive
|
||||||
|
: octokit.rest.repos.downloadTarballArchive
|
||||||
|
const response = await download({
|
||||||
owner: owner,
|
owner: owner,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
|
|
||||||
ref: commit || ref
|
ref: commit || ref
|
||||||
}
|
})
|
||||||
const response = await octokit.repos.getArchiveLink(params)
|
return Buffer.from(response.data as ArrayBuffer) // response.data is ArrayBuffer
|
||||||
if (response.status != 200) {
|
|
||||||
throw new Error(
|
|
||||||
`Unexpected response from GitHub API. Status: ${response.status}, Data: ${response.data}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.from(response.data) // response.data is ArrayBuffer
|
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,17 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||||||
result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE'
|
result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE'
|
||||||
core.debug(`clean = ${result.clean}`)
|
core.debug(`clean = ${result.clean}`)
|
||||||
|
|
||||||
|
// Sparse checkout
|
||||||
|
const sparseCheckout = core.getMultilineInput('sparse-checkout')
|
||||||
|
if (sparseCheckout.length) {
|
||||||
|
result.sparseCheckout = sparseCheckout
|
||||||
|
core.debug(`sparse checkout = ${result.sparseCheckout}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.sparseCheckoutConeMode =
|
||||||
|
(core.getInput('sparse-checkout-cone-mode') || 'true').toUpperCase() ===
|
||||||
|
'TRUE'
|
||||||
|
|
||||||
// Fetch depth
|
// Fetch depth
|
||||||
result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1'))
|
result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1'))
|
||||||
if (isNaN(result.fetchDepth) || result.fetchDepth < 0) {
|
if (isNaN(result.fetchDepth) || result.fetchDepth < 0) {
|
||||||
@ -89,6 +100,11 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||||||
}
|
}
|
||||||
core.debug(`fetch depth = ${result.fetchDepth}`)
|
core.debug(`fetch depth = ${result.fetchDepth}`)
|
||||||
|
|
||||||
|
// Fetch tags
|
||||||
|
result.fetchTags =
|
||||||
|
(core.getInput('fetch-tags') || 'false').toUpperCase() === 'TRUE'
|
||||||
|
core.debug(`fetch tags = ${result.fetchTags}`)
|
||||||
|
|
||||||
// LFS
|
// LFS
|
||||||
result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE'
|
result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE'
|
||||||
core.debug(`lfs = ${result.lfs}`)
|
core.debug(`lfs = ${result.lfs}`)
|
||||||
@ -122,5 +138,13 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||||||
// Workflow organization ID
|
// Workflow organization ID
|
||||||
result.workflowOrganizationId = await workflowContextHelper.getOrganizationId()
|
result.workflowOrganizationId = await workflowContextHelper.getOrganizationId()
|
||||||
|
|
||||||
|
// Set safe.directory in git global config.
|
||||||
|
result.setSafeDirectory =
|
||||||
|
(core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE'
|
||||||
|
|
||||||
|
// Determine the GitHub URL that the repository is being hosted from
|
||||||
|
result.githubServerUrl = core.getInput('github-server-url')
|
||||||
|
core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,4 @@ set -e
|
|||||||
src/misc/licensed-download.sh
|
src/misc/licensed-download.sh
|
||||||
|
|
||||||
echo 'Running: licensed cached'
|
echo 'Running: licensed cached'
|
||||||
_temp/licensed-3.3.1/licensed status
|
_temp/licensed-3.6.0/licensed status
|
@ -2,23 +2,23 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ ! -f _temp/licensed-3.3.1.done ]; then
|
if [ ! -f _temp/licensed-3.6.0.done ]; then
|
||||||
echo 'Clearing temp'
|
echo 'Clearing temp'
|
||||||
rm -rf _temp/licensed-3.3.1 || true
|
rm -rf _temp/licensed-3.6.0 || true
|
||||||
|
|
||||||
echo 'Downloading licensed'
|
echo 'Downloading licensed'
|
||||||
mkdir -p _temp/licensed-3.3.1
|
mkdir -p _temp/licensed-3.6.0
|
||||||
pushd _temp/licensed-3.3.1
|
pushd _temp/licensed-3.6.0
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.3.1/licensed-3.3.1-darwin-x64.tar.gz
|
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.6.0/licensed-3.6.0-darwin-x64.tar.gz
|
||||||
else
|
else
|
||||||
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.3.1/licensed-3.3.1-linux-x64.tar.gz
|
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.6.0/licensed-3.6.0-linux-x64.tar.gz
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo 'Extracting licenesed'
|
echo 'Extracting licenesed'
|
||||||
tar -xzf licensed.tar.gz
|
tar -xzf licensed.tar.gz
|
||||||
popd
|
popd
|
||||||
touch _temp/licensed-3.3.1.done
|
touch _temp/licensed-3.6.0.done
|
||||||
else
|
else
|
||||||
echo 'Licensed already downloaded'
|
echo 'Licensed already downloaded'
|
||||||
fi
|
fi
|
||||||
|
@ -5,4 +5,4 @@ set -e
|
|||||||
src/misc/licensed-download.sh
|
src/misc/licensed-download.sh
|
||||||
|
|
||||||
echo 'Running: licensed cached'
|
echo 'Running: licensed cached'
|
||||||
_temp/licensed-3.3.1/licensed cache
|
_temp/licensed-3.6.0/licensed cache
|
@ -1,7 +1,7 @@
|
|||||||
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 {getServerApiUrl, isGhes} from './url-helper'
|
||||||
|
|
||||||
export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
|
export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
|
||||||
|
|
||||||
@ -183,11 +183,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,14 +244,18 @@ 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 = github.getOctokit(token, {
|
||||||
|
baseUrl: getServerApiUrl(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=${
|
||||||
process.env['GITHUB_RUN_ID']
|
process.env['GITHUB_RUN_ID']
|
||||||
};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
|
};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
|
||||||
})
|
})
|
||||||
await octokit.repos.get({owner: repositoryOwner, repo: repositoryName})
|
await octokit.rest.repos.get({
|
||||||
|
owner: repositoryOwner,
|
||||||
|
repo: repositoryName
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
core.debug(
|
core.debug(
|
||||||
@ -276,10 +281,3 @@ function select(obj: any, path: string): any {
|
|||||||
const key = path.slice(0, i)
|
const key = path.slice(0, i)
|
||||||
return select(obj[key], path.slice(i + 1))
|
return select(obj[key], path.slice(i + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGhes(): boolean {
|
|
||||||
const ghUrl = new URL(
|
|
||||||
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
|
||||||
)
|
|
||||||
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
|
|
||||||
}
|
|
||||||
|
@ -1,58 +1,60 @@
|
|||||||
import * as coreCommand from '@actions/core/lib/command'
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the POST action is running
|
* Indicates whether the POST action is running
|
||||||
*/
|
*/
|
||||||
export const IsPost = !!process.env['STATE_isPost']
|
export const IsPost = !!core.getState('isPost')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The repository path for the POST action. The value is empty during the MAIN action.
|
* The repository path for the POST action. The value is empty during the MAIN action.
|
||||||
*/
|
*/
|
||||||
export const RepositoryPath =
|
export const RepositoryPath = core.getState('repositoryPath')
|
||||||
(process.env['STATE_repositoryPath'] as string) || ''
|
|
||||||
|
/**
|
||||||
|
* The set-safe-directory for the POST action. The value is set if input: 'safe-directory' is set during the MAIN action.
|
||||||
|
*/
|
||||||
|
export const PostSetSafeDirectory = core.getState('setSafeDirectory') === 'true'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SSH key path for the POST action. The value is empty during the MAIN action.
|
* The SSH key path for the POST action. The value is empty during the MAIN action.
|
||||||
*/
|
*/
|
||||||
export const SshKeyPath = (process.env['STATE_sshKeyPath'] as string) || ''
|
export const SshKeyPath = core.getState('sshKeyPath')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SSH known hosts path for the POST action. The value is empty during the MAIN action.
|
* The SSH known hosts path for the POST action. The value is empty during the MAIN action.
|
||||||
*/
|
*/
|
||||||
export const SshKnownHostsPath =
|
export const SshKnownHostsPath = core.getState('sshKnownHostsPath')
|
||||||
(process.env['STATE_sshKnownHostsPath'] as string) || ''
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the repository path so the POST action can retrieve the value.
|
* Save the repository path so the POST action can retrieve the value.
|
||||||
*/
|
*/
|
||||||
export function setRepositoryPath(repositoryPath: string) {
|
export function setRepositoryPath(repositoryPath: string) {
|
||||||
coreCommand.issueCommand(
|
core.saveState('repositoryPath', repositoryPath)
|
||||||
'save-state',
|
|
||||||
{name: 'repositoryPath'},
|
|
||||||
repositoryPath
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the SSH key path so the POST action can retrieve the value.
|
* Save the SSH key path so the POST action can retrieve the value.
|
||||||
*/
|
*/
|
||||||
export function setSshKeyPath(sshKeyPath: string) {
|
export function setSshKeyPath(sshKeyPath: string) {
|
||||||
coreCommand.issueCommand('save-state', {name: 'sshKeyPath'}, sshKeyPath)
|
core.saveState('sshKeyPath', sshKeyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the SSH known hosts path so the POST action can retrieve the value.
|
* Save the SSH known hosts path so the POST action can retrieve the value.
|
||||||
*/
|
*/
|
||||||
export function setSshKnownHostsPath(sshKnownHostsPath: string) {
|
export function setSshKnownHostsPath(sshKnownHostsPath: string) {
|
||||||
coreCommand.issueCommand(
|
core.saveState('sshKnownHostsPath', sshKnownHostsPath)
|
||||||
'save-state',
|
}
|
||||||
{name: 'sshKnownHostsPath'},
|
|
||||||
sshKnownHostsPath
|
/**
|
||||||
)
|
* Save the set-safe-directory input so the POST action can retrieve the value.
|
||||||
|
*/
|
||||||
|
export function setSafeDirectory() {
|
||||||
|
core.saveState('setSafeDirectory', 'true')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic.
|
// Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic.
|
||||||
// This is necessary since we don't have a separate entry point.
|
// This is necessary since we don't have a separate entry point.
|
||||||
if (!IsPost) {
|
if (!IsPost) {
|
||||||
coreCommand.issueCommand('save-state', {name: 'isPost'}, 'true')
|
core.saveState('isPost', 'true')
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user