Compare commits
5 Commits
main
...
users/eric
Author | SHA1 | Date |
---|---|---|
eric sciple | c455122937 | |
eric sciple | 605b7eb961 | |
eric sciple | 036772999d | |
eric sciple | 46054cf00b | |
eric sciple | 85a425b582 |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"plugins": ["jest", "@typescript-eslint"],
|
"plugins": ["jest", "@typescript-eslint"],
|
||||||
"extends": ["plugin:github/recommended"],
|
"extends": ["plugin:github/es6"],
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 9,
|
"ecmaVersion": 9,
|
||||||
|
@ -16,19 +16,23 @@
|
||||||
"@typescript-eslint/no-require-imports": "error",
|
"@typescript-eslint/no-require-imports": "error",
|
||||||
"@typescript-eslint/array-type": "error",
|
"@typescript-eslint/array-type": "error",
|
||||||
"@typescript-eslint/await-thenable": "error",
|
"@typescript-eslint/await-thenable": "error",
|
||||||
|
"@typescript-eslint/ban-ts-ignore": "error",
|
||||||
"camelcase": "off",
|
"camelcase": "off",
|
||||||
|
"@typescript-eslint/camelcase": "error",
|
||||||
|
"@typescript-eslint/class-name-casing": "error",
|
||||||
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
|
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
|
||||||
"@typescript-eslint/func-call-spacing": ["error", "never"],
|
"@typescript-eslint/func-call-spacing": ["error", "never"],
|
||||||
|
"@typescript-eslint/generic-type-naming": ["error", "^[A-Z][A-Za-z]*$"],
|
||||||
"@typescript-eslint/no-array-constructor": "error",
|
"@typescript-eslint/no-array-constructor": "error",
|
||||||
"@typescript-eslint/no-empty-interface": "error",
|
"@typescript-eslint/no-empty-interface": "error",
|
||||||
"@typescript-eslint/no-explicit-any": "error",
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
"@typescript-eslint/no-extraneous-class": "error",
|
"@typescript-eslint/no-extraneous-class": "error",
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
|
||||||
"@typescript-eslint/no-for-in-array": "error",
|
"@typescript-eslint/no-for-in-array": "error",
|
||||||
"@typescript-eslint/no-inferrable-types": "error",
|
"@typescript-eslint/no-inferrable-types": "error",
|
||||||
"@typescript-eslint/no-misused-new": "error",
|
"@typescript-eslint/no-misused-new": "error",
|
||||||
"@typescript-eslint/no-namespace": "error",
|
"@typescript-eslint/no-namespace": "error",
|
||||||
"@typescript-eslint/no-non-null-assertion": "warn",
|
"@typescript-eslint/no-non-null-assertion": "warn",
|
||||||
|
"@typescript-eslint/no-object-literal-type-assertion": "error",
|
||||||
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
||||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||||
"@typescript-eslint/no-useless-constructor": "error",
|
"@typescript-eslint/no-useless-constructor": "error",
|
||||||
|
@ -36,6 +40,7 @@
|
||||||
"@typescript-eslint/prefer-for-of": "warn",
|
"@typescript-eslint/prefer-for-of": "warn",
|
||||||
"@typescript-eslint/prefer-function-type": "warn",
|
"@typescript-eslint/prefer-function-type": "warn",
|
||||||
"@typescript-eslint/prefer-includes": "error",
|
"@typescript-eslint/prefer-includes": "error",
|
||||||
|
"@typescript-eslint/prefer-interface": "error",
|
||||||
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
||||||
"@typescript-eslint/promise-function-async": "error",
|
"@typescript-eslint/promise-function-async": "error",
|
||||||
"@typescript-eslint/require-array-sort-compare": "error",
|
"@typescript-eslint/require-array-sort-compare": "error",
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
.licenses/** -diff linguist-generated=true
|
|
|
@ -1,51 +0,0 @@
|
||||||
# `dist/index.js` is a special file in Actions.
|
|
||||||
# When you reference an action with `uses:` in a workflow,
|
|
||||||
# `index.js` is the code that will run.
|
|
||||||
# For our project, we generate this file through a build process
|
|
||||||
# from other source files.
|
|
||||||
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
|
||||||
name: Check dist
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
pull_request:
|
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-dist:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set Node.js 20.x
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 20.x
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Rebuild the index.js file
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Compare the expected and actual dist/ directories
|
|
||||||
run: |
|
|
||||||
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
|
||||||
echo "Detected uncommitted changes after build. See status below:"
|
|
||||||
git diff
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If dist/ was different than expected, upload the expected version as an artifact
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
|
||||||
with:
|
|
||||||
name: dist
|
|
||||||
path: dist/
|
|
|
@ -1,58 +0,0 @@
|
||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [ main ]
|
|
||||||
schedule:
|
|
||||||
- cron: '28 9 * * 0'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'javascript' ]
|
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
|
||||||
# Learn more:
|
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v2
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
||||||
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run build
|
|
||||||
- run: rm -rf dist # We want code scanning to analyze lib instead (individual .js files)
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
|
@ -1,14 +0,0 @@
|
||||||
name: Licensed
|
|
||||||
|
|
||||||
on:
|
|
||||||
push: {branches: main}
|
|
||||||
pull_request: {branches: main}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Check licenses
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run licensed-check
|
|
|
@ -4,7 +4,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- master
|
||||||
- releases/*
|
- releases/*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
@ -13,8 +13,8 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 12.x
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
- run: npm run format-check
|
- run: npm run format-check
|
||||||
|
@ -32,7 +32,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
# Basic checkout
|
# Basic checkout
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
|
@ -72,43 +72,6 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: __test__/verify-side-by-side.sh
|
run: __test__/verify-side-by-side.sh
|
||||||
|
|
||||||
# Filter
|
|
||||||
- name: Fetch filter
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
filter: 'blob:none'
|
|
||||||
path: fetch-filter
|
|
||||||
|
|
||||||
- name: Verify fetch filter
|
|
||||||
run: __test__/verify-fetch-filter.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: ./
|
||||||
|
@ -179,7 +142,7 @@ jobs:
|
||||||
options: --dns 127.0.0.1
|
options: --dns 127.0.0.1
|
||||||
services:
|
services:
|
||||||
squid-proxy:
|
squid-proxy:
|
||||||
image: ubuntu/squid:latest
|
image: datadog/squid:latest
|
||||||
ports:
|
ports:
|
||||||
- 3128:3128
|
- 3128:3128
|
||||||
env:
|
env:
|
||||||
|
@ -187,7 +150,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
# Basic checkout using git
|
# Basic checkout using git
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
|
@ -219,7 +182,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
# Clone this repo
|
# Clone this repo
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
# Basic checkout using git
|
# Basic checkout using git
|
||||||
- name: Checkout basic
|
- name: Checkout basic
|
||||||
|
@ -242,41 +205,3 @@ 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
|
|
|
@ -1,32 +0,0 @@
|
||||||
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:
|
|
||||||
- v4
|
|
||||||
- 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,5 +1,3 @@
|
||||||
__test__/_temp
|
__test__/_temp
|
||||||
_temp/
|
|
||||||
lib/
|
lib/
|
||||||
node_modules/
|
node_modules/
|
||||||
.vscode/
|
|
|
@ -1,14 +0,0 @@
|
||||||
sources:
|
|
||||||
npm: true
|
|
||||||
|
|
||||||
allowed:
|
|
||||||
- apache-2.0
|
|
||||||
- bsd-2-clause
|
|
||||||
- bsd-3-clause
|
|
||||||
- isc
|
|
||||||
- mit
|
|
||||||
- cc0-1.0
|
|
||||||
- unlicense
|
|
||||||
|
|
||||||
reviewed:
|
|
||||||
npm:
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
92
CHANGELOG.md
92
CHANGELOG.md
|
@ -1,97 +1,5 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## v4.1.0
|
|
||||||
- [Add support for partial checkout filters](https://github.com/actions/checkout/pull/1396)
|
|
||||||
|
|
||||||
## v4.0.0
|
|
||||||
- [Support fetching without the --progress option](https://github.com/actions/checkout/pull/1067)
|
|
||||||
- [Update to node20](https://github.com/actions/checkout/pull/1436)
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
- [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284)
|
|
||||||
|
|
||||||
## v2.3.0
|
|
||||||
|
|
||||||
- [Fallback to the default branch](https://github.com/actions/checkout/pull/278)
|
|
||||||
|
|
||||||
## v2.2.0
|
|
||||||
|
|
||||||
- [Fetch all history for all tags and branches when fetch-depth=0](https://github.com/actions/checkout/pull/258)
|
|
||||||
|
|
||||||
## v2.1.1
|
|
||||||
|
|
||||||
- Changes to support GHES ([here](https://github.com/actions/checkout/pull/236) and [here](https://github.com/actions/checkout/pull/248))
|
|
||||||
|
|
||||||
## v2.1.0
|
|
||||||
|
|
||||||
- [Group output](https://github.com/actions/checkout/pull/191)
|
|
||||||
- [Changes to support GHES alpha release](https://github.com/actions/checkout/pull/199)
|
|
||||||
- [Persist core.sshCommand for submodules](https://github.com/actions/checkout/pull/184)
|
|
||||||
- [Add support ssh](https://github.com/actions/checkout/pull/163)
|
|
||||||
- [Convert submodule SSH URL to HTTPS, when not using SSH](https://github.com/actions/checkout/pull/179)
|
|
||||||
- [Add submodule support](https://github.com/actions/checkout/pull/157)
|
|
||||||
- [Follow proxy settings](https://github.com/actions/checkout/pull/144)
|
|
||||||
- [Fix ref for pr closed event when a pr is merged](https://github.com/actions/checkout/pull/141)
|
|
||||||
- [Fix issue checking detached when git less than 2.22](https://github.com/actions/checkout/pull/128)
|
|
||||||
|
|
||||||
## v2.0.0
|
|
||||||
|
|
||||||
- [Do not pass cred on command line](https://github.com/actions/checkout/pull/108)
|
|
||||||
- [Add input persist-credentials](https://github.com/actions/checkout/pull/107)
|
|
||||||
- [Fallback to REST API to download repo](https://github.com/actions/checkout/pull/104)
|
|
||||||
|
|
||||||
## v2 (beta)
|
## v2 (beta)
|
||||||
|
|
||||||
- Improved fetch performance
|
- Improved fetch performance
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
* @actions/actions-launch
|
|
|
@ -1,28 +0,0 @@
|
||||||
# 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:
|
|
180
README.md
180
README.md
|
@ -1,10 +1,12 @@
|
||||||
[![Build and Test](https://github.com/actions/checkout/actions/workflows/test.yml/badge.svg)](https://github.com/actions/checkout/actions/workflows/test.yml)
|
<p align="center">
|
||||||
|
<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 V4
|
# Checkout V2
|
||||||
|
|
||||||
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
|
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
|
||||||
|
|
||||||
Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth: 0` to fetch all history for all branches and tags. Refer [here](https://docs.github.com/actions/using-workflows/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
|
Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth` to fetch more history. Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
|
||||||
|
|
||||||
The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out.
|
The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out.
|
||||||
|
|
||||||
|
@ -12,13 +14,27 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
|
||||||
|
|
||||||
# What's new
|
# What's new
|
||||||
|
|
||||||
Please refer to the [release page](https://github.com/actions/checkout/releases/latest) for the latest release notes.
|
- Improved performance
|
||||||
|
- Fetches only a single commit by default
|
||||||
|
- Script authenticated git commands
|
||||||
|
- Auth token persisted in the local git config
|
||||||
|
- Creates a local branch
|
||||||
|
- No longer detached HEAD when checking out a branch
|
||||||
|
- Improved layout
|
||||||
|
- The input `path` is always relative to $GITHUB_WORKSPACE
|
||||||
|
- Aligns better with container actions, where $GITHUB_WORKSPACE gets mapped in
|
||||||
|
- Fallback to REST API download
|
||||||
|
- When Git 2.18 or higher is not in the PATH, the REST API will be used to download the files
|
||||||
|
- When using a job container, the container's PATH is used
|
||||||
|
- Removed input `submodules`
|
||||||
|
|
||||||
|
Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
<!-- start usage -->
|
<!-- start usage -->
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
# Repository name with owner. For example, actions/checkout
|
# Repository name with owner. For example, actions/checkout
|
||||||
# Default: ${{ github.repository }}
|
# Default: ${{ github.repository }}
|
||||||
|
@ -26,7 +42,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
|
|
||||||
# The branch, tag or SHA to checkout. When checking out the repository that
|
# The branch, tag or SHA to checkout. When checking out the repository that
|
||||||
# triggered a workflow, this defaults to the reference or SHA for that event.
|
# triggered a workflow, this defaults to the reference or SHA for that event.
|
||||||
# Otherwise, uses the default branch.
|
# Otherwise, defaults to `master`.
|
||||||
ref: ''
|
ref: ''
|
||||||
|
|
||||||
# Personal access token (PAT) used to fetch the repository. The PAT is configured
|
# Personal access token (PAT) used to fetch the repository. The PAT is configured
|
||||||
|
@ -73,31 +89,10 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
# Default: true
|
# Default: true
|
||||||
clean: ''
|
clean: ''
|
||||||
|
|
||||||
# Partially clone against a given filter. Overrides sparse-checkout if set.
|
# Number of commits to fetch. 0 indicates all history.
|
||||||
# Default: null
|
|
||||||
filter: ''
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
# Default: 1
|
# Default: 1
|
||||||
fetch-depth: ''
|
fetch-depth: ''
|
||||||
|
|
||||||
# Whether to fetch tags, even if fetch-depth > 0.
|
|
||||||
# Default: false
|
|
||||||
fetch-tags: ''
|
|
||||||
|
|
||||||
# Whether to show progress status output when fetching.
|
|
||||||
# Default: true
|
|
||||||
show-progress: ''
|
|
||||||
|
|
||||||
# Whether to download Git-LFS files
|
# Whether to download Git-LFS files
|
||||||
# Default: false
|
# Default: false
|
||||||
lfs: ''
|
lfs: ''
|
||||||
|
@ -110,26 +105,11 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
#
|
#
|
||||||
# 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)
|
|
||||||
- [Checkout a different branch](#Checkout-a-different-branch)
|
- [Checkout a different branch](#Checkout-a-different-branch)
|
||||||
- [Checkout HEAD^](#Checkout-HEAD)
|
- [Checkout HEAD^](#Checkout-HEAD)
|
||||||
- [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
|
- [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
|
||||||
|
@ -137,48 +117,15 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
- [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
|
- [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
|
||||||
- [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
|
- [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
|
||||||
- [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)
|
- [Checkout submodules](#Checkout-submodules)
|
||||||
|
- [Fetch all tags](#Fetch-all-tags)
|
||||||
## Fetch only the root files
|
- [Fetch all branches](#Fetch-all-branches)
|
||||||
|
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
|
||||||
```yaml
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
sparse-checkout: .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fetch only the root files and `.github` and `src` folder
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
sparse-checkout: |
|
|
||||||
.github
|
|
||||||
src
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fetch only a single file
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
sparse-checkout: |
|
|
||||||
README.md
|
|
||||||
sparse-checkout-cone-mode: false
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fetch all history for all tags and branches
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Checkout a different branch
|
## Checkout a different branch
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: my-branch
|
ref: my-branch
|
||||||
```
|
```
|
||||||
|
@ -186,7 +133,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
## Checkout HEAD^
|
## Checkout HEAD^
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- run: git checkout HEAD^
|
- run: git checkout HEAD^
|
||||||
|
@ -196,45 +143,43 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
```
|
```
|
||||||
> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
|
|
||||||
|
|
||||||
## Checkout multiple repos (nested)
|
## Checkout multiple repos (nested)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Checkout tools repo
|
- name: Checkout tools repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-tools
|
repository: my-org/my-tools
|
||||||
path: my-tools
|
path: my-tools
|
||||||
```
|
```
|
||||||
> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
|
|
||||||
|
|
||||||
## Checkout multiple repos (private)
|
## Checkout multiple repos (private)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
|
|
||||||
- name: Checkout private tools
|
- name: Checkout private tools
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: my-org/my-private-tools
|
repository: my-org/my-private-tools
|
||||||
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
|
token: ${{ secrets.GitHub_PAT }} # `GitHub_PAT` is a secret that contains your PAT
|
||||||
path: my-tools
|
path: my-tools
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -244,7 +189,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
## Checkout pull request HEAD commit instead of merge commit
|
## Checkout pull request HEAD commit instead of merge commit
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
```
|
```
|
||||||
|
@ -254,31 +199,50 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
||||||
```yaml
|
```yaml
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [master]
|
||||||
types: [opened, synchronize, closed]
|
types: [opened, synchronize, closed]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
```
|
```
|
||||||
|
|
||||||
## Push a commit using the built-in token
|
## Checkout submodules
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
on: push
|
- uses: actions/checkout@v2
|
||||||
jobs:
|
- name: Checkout submodules
|
||||||
build:
|
shell: bash
|
||||||
runs-on: ubuntu-latest
|
run: |
|
||||||
steps:
|
# If your submodules are configured to use SSH instead of HTTPS please uncomment the following line
|
||||||
- uses: actions/checkout@v4
|
# git config --global url."https://github.com/".insteadOf "git@github.com:"
|
||||||
- run: |
|
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||||
date > generated.txt
|
git submodule sync --recursive
|
||||||
git config user.name github-actions
|
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
||||||
git config user.email github-actions@github.com
|
```
|
||||||
git add .
|
|
||||||
git commit -m "generated"
|
## Fetch all tags
|
||||||
git push
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fetch all branches
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: |
|
||||||
|
git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fetch all history for all tags and branches
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: |
|
||||||
|
git fetch --prune --unshallow
|
||||||
```
|
```
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
|
@ -20,7 +20,6 @@ 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 () => {
|
||||||
|
@ -68,18 +67,11 @@ describe('git-auth-helper tests', () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function testAuthHeader(
|
const configureAuth_configuresAuthHeader =
|
||||||
testName: string,
|
'configureAuth configures auth header'
|
||||||
serverUrl: string | undefined = undefined
|
it(configureAuth_configuresAuthHeader, async () => {
|
||||||
) {
|
|
||||||
// Arrange
|
// Arrange
|
||||||
let expectedServerUrl = 'https://github.com'
|
await setup(configureAuth_configuresAuthHeader)
|
||||||
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)
|
||||||
|
|
||||||
|
@ -96,33 +88,9 @@ describe('git-auth-helper tests', () => {
|
||||||
).toString('base64')
|
).toString('base64')
|
||||||
expect(
|
expect(
|
||||||
configContent.indexOf(
|
configContent.indexOf(
|
||||||
`http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}`
|
`http.https://github.com/.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 =
|
||||||
|
@ -449,7 +417,7 @@ describe('git-auth-helper tests', () => {
|
||||||
`Did not expect file to exist: '${globalGitConfigPath}'`
|
`Did not expect file to exist: '${globalGitConfigPath}'`
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ((err as any)?.code !== 'ENOENT') {
|
if (err.code !== 'ENOENT') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -550,17 +518,12 @@ describe('git-auth-helper tests', () => {
|
||||||
await authHelper.configureSubmoduleAuth()
|
await authHelper.configureSubmoduleAuth()
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4)
|
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
|
||||||
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
|
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
|
||||||
/unset-all.*insteadOf/
|
/unset-all.*insteadOf/
|
||||||
)
|
)
|
||||||
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
|
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
|
||||||
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(
|
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/url.*insteadOf/)
|
||||||
/url.*insteadOf.*git@github.com:/
|
|
||||||
)
|
|
||||||
expect(mockSubmoduleForeach.mock.calls[3][0]).toMatch(
|
|
||||||
/url.*insteadOf.*org-123456@github.com:/
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -638,7 +601,7 @@ describe('git-auth-helper tests', () => {
|
||||||
await fs.promises.stat(actualKeyPath)
|
await fs.promises.stat(actualKeyPath)
|
||||||
throw new Error('SSH key should have been deleted')
|
throw new Error('SSH key should have been deleted')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ((err as any)?.code !== 'ENOENT') {
|
if (err.code !== 'ENOENT') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,7 +611,7 @@ describe('git-auth-helper tests', () => {
|
||||||
await fs.promises.stat(actualKnownHostsPath)
|
await fs.promises.stat(actualKnownHostsPath)
|
||||||
throw new Error('SSH known hosts should have been deleted')
|
throw new Error('SSH known hosts should have been deleted')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ((err as any)?.code !== 'ENOENT') {
|
if (err.code !== 'ENOENT') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -675,11 +638,10 @@ describe('git-auth-helper tests', () => {
|
||||||
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
const removeGlobalConfig_removesOverride =
|
const removeGlobalAuth_removesOverride = 'removeGlobalAuth removes override'
|
||||||
'removeGlobalConfig removes override'
|
it(removeGlobalAuth_removesOverride, async () => {
|
||||||
it(removeGlobalConfig_removesOverride, async () => {
|
|
||||||
// Arrange
|
// Arrange
|
||||||
await setup(removeGlobalConfig_removesOverride)
|
await setup(removeGlobalAuth_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()
|
||||||
|
@ -688,7 +650,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.removeGlobalConfig()
|
await authHelper.removeGlobalAuth()
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(git.env['HOME']).toBeUndefined()
|
expect(git.env['HOME']).toBeUndefined()
|
||||||
|
@ -696,7 +658,7 @@ describe('git-auth-helper tests', () => {
|
||||||
await fs.promises.stat(homeOverride)
|
await fs.promises.stat(homeOverride)
|
||||||
throw new Error(`Should have been deleted '${homeOverride}'`)
|
throw new Error(`Should have been deleted '${homeOverride}'`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ((err as any)?.code !== 'ENOENT') {
|
if (err.code !== 'ENOENT') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -727,8 +689,6 @@ 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(
|
||||||
|
@ -754,7 +714,6 @@ async function setup(testName: string): Promise<void> {
|
||||||
),
|
),
|
||||||
env: {},
|
env: {},
|
||||||
fetch: jest.fn(),
|
fetch: jest.fn(),
|
||||||
getDefaultBranch: jest.fn(),
|
|
||||||
getWorkingDirectory: jest.fn(() => workspace),
|
getWorkingDirectory: jest.fn(() => workspace),
|
||||||
init: jest.fn(),
|
init: jest.fn(),
|
||||||
isDetached: jest.fn(),
|
isDetached: jest.fn(),
|
||||||
|
@ -763,18 +722,14 @@ async function setup(testName: string): Promise<void> {
|
||||||
log1: jest.fn(),
|
log1: jest.fn(),
|
||||||
remoteAdd: jest.fn(),
|
remoteAdd: jest.fn(),
|
||||||
removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]),
|
removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]),
|
||||||
revParse: jest.fn(),
|
|
||||||
setEnvironmentVariable: jest.fn((name: string, value: string) => {
|
setEnvironmentVariable: jest.fn((name: string, value: string) => {
|
||||||
git.env[name] = value
|
git.env[name] = value
|
||||||
}),
|
}),
|
||||||
shaExists: jest.fn(),
|
setRemoteUrl: jest.fn(),
|
||||||
submoduleForeach: jest.fn(async () => {
|
submoduleForeach: jest.fn(async () => {
|
||||||
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(),
|
||||||
|
@ -794,7 +749,7 @@ async function setup(testName: string): Promise<void> {
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
tryDisableAutomaticGarbageCollection: jest.fn(),
|
tryDisableAutomaticGarbageCollection: jest.fn(),
|
||||||
tryGetFetchUrl: jest.fn(),
|
tryGetRemoteUrl: jest.fn(),
|
||||||
tryReset: jest.fn()
|
tryReset: jest.fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -802,26 +757,19 @@ async function setup(testName: string): Promise<void> {
|
||||||
authToken: 'some auth token',
|
authToken: 'some auth token',
|
||||||
clean: true,
|
clean: true,
|
||||||
commit: '',
|
commit: '',
|
||||||
filter: undefined,
|
|
||||||
sparseCheckout: [],
|
|
||||||
sparseCheckoutConeMode: true,
|
|
||||||
fetchDepth: 1,
|
fetchDepth: 1,
|
||||||
fetchTags: false,
|
isWorkflowRepository: true,
|
||||||
showProgress: true,
|
|
||||||
lfs: false,
|
lfs: false,
|
||||||
submodules: false,
|
submodules: false,
|
||||||
nestedSubmodules: false,
|
nestedSubmodules: false,
|
||||||
persistCredentials: true,
|
persistCredentials: true,
|
||||||
ref: 'refs/heads/main',
|
ref: 'refs/heads/master',
|
||||||
repositoryName: 'my-repo',
|
repositoryName: 'my-repo',
|
||||||
repositoryOwner: 'my-org',
|
repositoryOwner: 'my-org',
|
||||||
repositoryPath: '',
|
repositoryPath: '',
|
||||||
sshKey: sshPath ? 'some ssh private key' : '',
|
sshKey: sshPath ? 'some ssh private key' : '',
|
||||||
sshKnownHosts: '',
|
sshKnownHosts: '',
|
||||||
sshStrict: true,
|
sshStrict: true
|
||||||
workflowOrganizationId: 123456,
|
|
||||||
setSafeDirectory: true,
|
|
||||||
githubServerUrl: githubServerUrl
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,378 +0,0 @@
|
||||||
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',
|
|
||||||
'--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',
|
|
||||||
'--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',
|
|
||||||
'--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',
|
|
||||||
'--no-recurse-submodules',
|
|
||||||
'--filter=filterValue',
|
|
||||||
'--depth=1',
|
|
||||||
'origin',
|
|
||||||
'refspec1',
|
|
||||||
'refspec2'
|
|
||||||
],
|
|
||||||
expect.any(Object)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when showProgress 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',
|
|
||||||
showProgress: true
|
|
||||||
}
|
|
||||||
|
|
||||||
await git.fetch(refSpec, options)
|
|
||||||
|
|
||||||
expect(mockExec).toHaveBeenCalledWith(
|
|
||||||
expect.any(String),
|
|
||||||
[
|
|
||||||
'-c',
|
|
||||||
'protocol.version=2',
|
|
||||||
'fetch',
|
|
||||||
'--no-tags',
|
|
||||||
'--prune',
|
|
||||||
'--no-recurse-submodules',
|
|
||||||
'--progress',
|
|
||||||
'--filter=filterValue',
|
|
||||||
'origin',
|
|
||||||
'refspec1',
|
|
||||||
'refspec2'
|
|
||||||
],
|
|
||||||
expect.any(Object)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchDepth is 42 and showProgress 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: 42,
|
|
||||||
showProgress: true
|
|
||||||
}
|
|
||||||
|
|
||||||
await git.fetch(refSpec, options)
|
|
||||||
|
|
||||||
expect(mockExec).toHaveBeenCalledWith(
|
|
||||||
expect.any(String),
|
|
||||||
[
|
|
||||||
'-c',
|
|
||||||
'protocol.version=2',
|
|
||||||
'fetch',
|
|
||||||
'--no-tags',
|
|
||||||
'--prune',
|
|
||||||
'--no-recurse-submodules',
|
|
||||||
'--progress',
|
|
||||||
'--filter=filterValue',
|
|
||||||
'--depth=42',
|
|
||||||
'origin',
|
|
||||||
'refspec1',
|
|
||||||
'refspec2'
|
|
||||||
],
|
|
||||||
expect.any(Object)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should call execGit with the correct arguments when fetchTags is true and showProgress 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',
|
|
||||||
fetchTags: true,
|
|
||||||
showProgress: true
|
|
||||||
}
|
|
||||||
|
|
||||||
await git.fetch(refSpec, options)
|
|
||||||
|
|
||||||
expect(mockExec).toHaveBeenCalledWith(
|
|
||||||
expect.any(String),
|
|
||||||
[
|
|
||||||
'-c',
|
|
||||||
'protocol.version=2',
|
|
||||||
'fetch',
|
|
||||||
'--prune',
|
|
||||||
'--no-recurse-submodules',
|
|
||||||
'--progress',
|
|
||||||
'--filter=filterValue',
|
|
||||||
'origin',
|
|
||||||
'refspec1',
|
|
||||||
'refspec2'
|
|
||||||
],
|
|
||||||
expect.any(Object)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -7,9 +7,9 @@ import {IGitCommandManager} from '../lib/git-command-manager'
|
||||||
|
|
||||||
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
|
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
|
||||||
let repositoryPath: string
|
let repositoryPath: string
|
||||||
let repositoryUrl: string
|
let httpsUrl: string
|
||||||
|
let sshUrl: string
|
||||||
let clean: boolean
|
let clean: boolean
|
||||||
let ref: string
|
|
||||||
let git: IGitCommandManager
|
let git: IGitCommandManager
|
||||||
|
|
||||||
describe('git-directory-helper tests', () => {
|
describe('git-directory-helper tests', () => {
|
||||||
|
@ -41,9 +41,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -64,9 +64,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -90,9 +90,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -112,9 +112,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -141,9 +141,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -161,16 +161,16 @@ describe('git-directory-helper tests', () => {
|
||||||
await setup(removesContentsWhenDifferentRepositoryUrl)
|
await setup(removesContentsWhenDifferentRepositoryUrl)
|
||||||
clean = false
|
clean = false
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
const differentRepositoryUrl =
|
const differentRemoteUrl =
|
||||||
'https://github.com/my-different-org/my-different-repo'
|
'https://github.com/my-different-org/my-different-repo'
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
differentRepositoryUrl,
|
differentRemoteUrl,
|
||||||
clean,
|
[differentRemoteUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -193,9 +193,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -219,9 +219,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -244,9 +244,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
undefined,
|
undefined,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -269,9 +269,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -281,65 +281,6 @@ 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
|
||||||
|
@ -359,9 +300,9 @@ describe('git-directory-helper tests', () => {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -376,66 +317,54 @@ describe('git-directory-helper tests', () => {
|
||||||
expect(git.tryReset).not.toHaveBeenCalled()
|
expect(git.tryReset).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
const removesAncestorRemoteBranch = 'removes ancestor remote branch'
|
const removesRemoteBranches = 'removes local branches'
|
||||||
it(removesAncestorRemoteBranch, async () => {
|
it(removesRemoteBranches, async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
await setup(removesAncestorRemoteBranch)
|
await setup(removesRemoteBranches)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
const mockBranchList = git.branchList as jest.Mock<any, any>
|
const mockBranchList = git.branchList as jest.Mock<any, any>
|
||||||
mockBranchList.mockImplementation(async (remote: boolean) => {
|
mockBranchList.mockImplementation(async (remote: boolean) => {
|
||||||
return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
|
return remote ? ['remote-branch-1', 'remote-branch-2'] : []
|
||||||
})
|
})
|
||||||
ref = 'remote-branch-1/conflict'
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
httpsUrl,
|
||||||
clean,
|
[httpsUrl, sshUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
const files = await fs.promises.readdir(repositoryPath)
|
const files = await fs.promises.readdir(repositoryPath)
|
||||||
expect(files.sort()).toEqual(['.git', 'my-file'])
|
expect(files.sort()).toEqual(['.git', 'my-file'])
|
||||||
expect(git.branchDelete).toHaveBeenCalledTimes(1)
|
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-1')
|
||||||
expect(git.branchDelete).toHaveBeenCalledWith(
|
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-2')
|
||||||
true,
|
|
||||||
'origin/remote-branch-1'
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const removesDescendantRemoteBranches = 'removes descendant remote branch'
|
const updatesRemoteUrl = 'updates remote URL'
|
||||||
it(removesDescendantRemoteBranches, async () => {
|
it(updatesRemoteUrl, async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
await setup(removesDescendantRemoteBranches)
|
await setup(updatesRemoteUrl)
|
||||||
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
|
||||||
const mockBranchList = git.branchList as jest.Mock<any, any>
|
|
||||||
mockBranchList.mockImplementation(async (remote: boolean) => {
|
|
||||||
return remote
|
|
||||||
? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2']
|
|
||||||
: []
|
|
||||||
})
|
|
||||||
ref = 'remote-branch-1'
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
git,
|
git,
|
||||||
repositoryPath,
|
repositoryPath,
|
||||||
repositoryUrl,
|
sshUrl,
|
||||||
clean,
|
[sshUrl, httpsUrl],
|
||||||
ref
|
clean
|
||||||
)
|
)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
const files = await fs.promises.readdir(repositoryPath)
|
const files = await fs.promises.readdir(repositoryPath)
|
||||||
expect(files.sort()).toEqual(['.git', 'my-file'])
|
expect(files.sort()).toEqual(['.git', 'my-file'])
|
||||||
expect(git.branchDelete).toHaveBeenCalledTimes(1)
|
expect(git.isDetached).toHaveBeenCalled()
|
||||||
expect(git.branchDelete).toHaveBeenCalledWith(
|
expect(git.branchList).toHaveBeenCalled()
|
||||||
true,
|
expect(core.warning).not.toHaveBeenCalled()
|
||||||
'origin/remote-branch-1/conflict'
|
expect(git.setRemoteUrl).toHaveBeenCalledWith(sshUrl)
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -446,15 +375,13 @@ async function setup(testName: string): Promise<void> {
|
||||||
repositoryPath = path.join(testWorkspace, testName)
|
repositoryPath = path.join(testWorkspace, testName)
|
||||||
await fs.promises.mkdir(path.join(repositoryPath, '.git'), {recursive: true})
|
await fs.promises.mkdir(path.join(repositoryPath, '.git'), {recursive: true})
|
||||||
|
|
||||||
// Repository URL
|
// Remote URLs
|
||||||
repositoryUrl = 'https://github.com/my-org/my-repo'
|
httpsUrl = 'https://github.com/my-org/my-repo'
|
||||||
|
sshUrl = 'git@github.com:my-org/my-repo'
|
||||||
|
|
||||||
// Clean
|
// Clean
|
||||||
clean = true
|
clean = true
|
||||||
|
|
||||||
// Ref
|
|
||||||
ref = ''
|
|
||||||
|
|
||||||
// Git command manager
|
// Git command manager
|
||||||
git = {
|
git = {
|
||||||
branchDelete: jest.fn(),
|
branchDelete: jest.fn(),
|
||||||
|
@ -462,14 +389,11 @@ 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(),
|
||||||
configExists: jest.fn(),
|
configExists: jest.fn(),
|
||||||
fetch: jest.fn(),
|
fetch: jest.fn(),
|
||||||
getDefaultBranch: jest.fn(),
|
|
||||||
getWorkingDirectory: jest.fn(() => repositoryPath),
|
getWorkingDirectory: jest.fn(() => repositoryPath),
|
||||||
init: jest.fn(),
|
init: jest.fn(),
|
||||||
isDetached: jest.fn(),
|
isDetached: jest.fn(),
|
||||||
|
@ -478,25 +402,21 @@ async function setup(testName: string): Promise<void> {
|
||||||
log1: jest.fn(),
|
log1: jest.fn(),
|
||||||
remoteAdd: jest.fn(),
|
remoteAdd: jest.fn(),
|
||||||
removeEnvironmentVariable: jest.fn(),
|
removeEnvironmentVariable: jest.fn(),
|
||||||
revParse: jest.fn(),
|
|
||||||
setEnvironmentVariable: jest.fn(),
|
setEnvironmentVariable: jest.fn(),
|
||||||
shaExists: jest.fn(),
|
setRemoteUrl: jest.fn(),
|
||||||
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
|
||||||
}),
|
}),
|
||||||
tryConfigUnset: jest.fn(),
|
tryConfigUnset: jest.fn(),
|
||||||
tryDisableAutomaticGarbageCollection: jest.fn(),
|
tryDisableAutomaticGarbageCollection: jest.fn(),
|
||||||
tryGetFetchUrl: jest.fn(async () => {
|
tryGetRemoteUrl: jest.fn(async () => {
|
||||||
// Sanity check - this function shouldn't be called when the .git directory doesn't exist
|
// Sanity check - this function shouldn't be called when the .git directory doesn't exist
|
||||||
await fs.promises.stat(path.join(repositoryPath, '.git'))
|
await fs.promises.stat(path.join(repositoryPath, '.git'))
|
||||||
return repositoryUrl
|
return httpsUrl
|
||||||
}),
|
}),
|
||||||
tryReset: jest.fn(async () => {
|
tryReset: jest.fn(async () => {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import * as assert from 'assert'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fsHelper from '../lib/fs-helper'
|
import * as fsHelper from '../lib/fs-helper'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as inputHelper from '../lib/input-helper'
|
import * as inputHelper from '../lib/input-helper'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as workflowContextHelper from '../lib/workflow-context-helper'
|
|
||||||
import {IGitSourceSettings} from '../lib/git-source-settings'
|
import {IGitSourceSettings} from '../lib/git-source-settings'
|
||||||
|
|
||||||
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
|
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
|
||||||
|
@ -43,11 +43,6 @@ describe('input-helper tests', () => {
|
||||||
.spyOn(fsHelper, 'directoryExistsSync')
|
.spyOn(fsHelper, 'directoryExistsSync')
|
||||||
.mockImplementation((path: string) => path == gitHubWorkspace)
|
.mockImplementation((path: string) => path == gitHubWorkspace)
|
||||||
|
|
||||||
// Mock ./workflowContextHelper getOrganizationId()
|
|
||||||
jest
|
|
||||||
.spyOn(workflowContextHelper, 'getOrganizationId')
|
|
||||||
.mockImplementation(() => Promise.resolve(123456))
|
|
||||||
|
|
||||||
// GitHub workspace
|
// GitHub workspace
|
||||||
process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
|
process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
|
||||||
})
|
})
|
||||||
|
@ -72,32 +67,26 @@ describe('input-helper tests', () => {
|
||||||
jest.restoreAllMocks()
|
jest.restoreAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets defaults', async () => {
|
it('sets defaults', () => {
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = inputHelper.getInputs()
|
||||||
expect(settings).toBeTruthy()
|
expect(settings).toBeTruthy()
|
||||||
expect(settings.authToken).toBeFalsy()
|
expect(settings.authToken).toBeFalsy()
|
||||||
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.filter).toBe(undefined)
|
|
||||||
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.showProgress).toBe(true)
|
|
||||||
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', () => {
|
||||||
let originalRef = github.context.ref
|
let originalRef = github.context.ref
|
||||||
try {
|
try {
|
||||||
github.context.ref = 'some-unqualified-ref'
|
github.context.ref = 'some-unqualified-ref'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = inputHelper.getInputs()
|
||||||
expect(settings).toBeTruthy()
|
expect(settings).toBeTruthy()
|
||||||
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
expect(settings.commit).toBe('1234567890123456789012345678901234567890')
|
||||||
expect(settings.ref).toBe('refs/heads/some-unqualified-ref')
|
expect(settings.ref).toBe('refs/heads/some-unqualified-ref')
|
||||||
|
@ -106,42 +95,39 @@ describe('input-helper tests', () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('requires qualified repo', async () => {
|
it('requires qualified repo', () => {
|
||||||
inputs.repository = 'some-unqualified-repo'
|
inputs.repository = 'some-unqualified-repo'
|
||||||
try {
|
assert.throws(() => {
|
||||||
await inputHelper.getInputs()
|
inputHelper.getInputs()
|
||||||
throw 'should not reach here'
|
}, /Invalid repository 'some-unqualified-repo'/)
|
||||||
} catch (err) {
|
|
||||||
expect(`(${(err as any).message}`).toMatch(
|
|
||||||
"Invalid repository 'some-unqualified-repo'"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('roots path', async () => {
|
it('roots path', () => {
|
||||||
inputs.path = 'some-directory/some-subdirectory'
|
inputs.path = 'some-directory/some-subdirectory'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = inputHelper.getInputs()
|
||||||
expect(settings.repositoryPath).toBe(
|
expect(settings.repositoryPath).toBe(
|
||||||
path.join(gitHubWorkspace, 'some-directory', 'some-subdirectory')
|
path.join(gitHubWorkspace, 'some-directory', 'some-subdirectory')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets ref to empty when explicit sha', async () => {
|
it('sets correct default ref/sha for other repo', () => {
|
||||||
|
inputs.repository = 'some-owner/some-other-repo'
|
||||||
|
const settings: IGitSourceSettings = inputHelper.getInputs()
|
||||||
|
expect(settings.ref).toBe('refs/heads/master')
|
||||||
|
expect(settings.commit).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets ref to empty when explicit sha', () => {
|
||||||
inputs.ref = '1111111111222222222233333333334444444444'
|
inputs.ref = '1111111111222222222233333333334444444444'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = inputHelper.getInputs()
|
||||||
expect(settings.ref).toBeFalsy()
|
expect(settings.ref).toBeFalsy()
|
||||||
expect(settings.commit).toBe('1111111111222222222233333333334444444444')
|
expect(settings.commit).toBe('1111111111222222222233333333334444444444')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets sha to empty when explicit ref', async () => {
|
it('sets sha to empty when explicit ref', () => {
|
||||||
inputs.ref = 'refs/heads/some-other-ref'
|
inputs.ref = 'refs/heads/some-other-ref'
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
const settings: IGitSourceSettings = inputHelper.getInputs()
|
||||||
expect(settings.ref).toBe('refs/heads/some-other-ref')
|
expect(settings.ref).toBe('refs/heads/some-other-ref')
|
||||||
expect(settings.commit).toBeFalsy()
|
expect(settings.commit).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets workflow organization ID', async () => {
|
|
||||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
|
||||||
expect(settings.workflowOrganizationId).toBe(123456)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
mkdir override-git-version
|
mkdir override-git-version
|
||||||
cd override-git-version
|
cd override-git-version
|
||||||
echo @echo override git version 1.2.3 > git.cmd
|
echo @echo override git version 1.2.3 > git.cmd
|
||||||
echo "%CD%" >> $GITHUB_PATH
|
echo ::add-path::%CD%
|
||||||
cd ..
|
cd ..
|
||||||
|
|
|
@ -5,5 +5,5 @@ cd override-git-version
|
||||||
echo "#!/bin/sh" > git
|
echo "#!/bin/sh" > git
|
||||||
echo "echo override git version 1.2.3" >> git
|
echo "echo override git version 1.2.3" >> git
|
||||||
chmod +x git
|
chmod +x git
|
||||||
echo "$(pwd)" >> $GITHUB_PATH
|
echo "::add-path::$(pwd)"
|
||||||
cd ..
|
cd ..
|
||||||
|
|
|
@ -16,7 +16,7 @@ describe('ref-helper tests', () => {
|
||||||
await refHelper.getCheckoutInfo(git, 'refs/heads/my/branch', commit)
|
await refHelper.getCheckoutInfo(git, 'refs/heads/my/branch', commit)
|
||||||
throw new Error('Should not reach here')
|
throw new Error('Should not reach here')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect((err as any)?.message).toBe('Arg git cannot be empty')
|
expect(err.message).toBe('Arg git cannot be empty')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -25,9 +25,7 @@ describe('ref-helper tests', () => {
|
||||||
await refHelper.getCheckoutInfo(git, '', '')
|
await refHelper.getCheckoutInfo(git, '', '')
|
||||||
throw new Error('Should not reach here')
|
throw new Error('Should not reach here')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect((err as any)?.message).toBe(
|
expect(err.message).toBe('Args ref and commit cannot both be empty')
|
||||||
'Args ref and commit cannot both be empty'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -104,7 +102,7 @@ describe('ref-helper tests', () => {
|
||||||
await refHelper.getCheckoutInfo(git, 'my-ref', '')
|
await refHelper.getCheckoutInfo(git, 'my-ref', '')
|
||||||
throw new Error('Should not reach here')
|
throw new Error('Should not reach here')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect((err as any)?.message).toBe(
|
expect(err.message).toBe(
|
||||||
"A branch or tag with the name 'my-ref' could not be found"
|
"A branch or tag with the name 'my-ref' could not be found"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ describe('retry-helper tests', () => {
|
||||||
throw new Error(`some error ${++attempts}`)
|
throw new Error(`some error ${++attempts}`)
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error = err as Error
|
error = err
|
||||||
}
|
}
|
||||||
expect(error.message).toBe('some error 3')
|
expect(error.message).toBe('some error 3')
|
||||||
expect(attempts).toBe(3)
|
expect(attempts).toBe(3)
|
||||||
|
|
|
@ -20,5 +20,5 @@ else
|
||||||
|
|
||||||
# Verify auth token
|
# Verify auth token
|
||||||
cd basic
|
cd basic
|
||||||
git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main
|
git fetch --no-tags --depth=1 origin +refs/heads/master:refs/remotes/origin/master
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Verify .git folder
|
|
||||||
if [ ! -d "./fetch-filter/.git" ]; then
|
|
||||||
echo "Expected ./fetch-filter/.git folder to exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify .git/config contains partialclonefilter
|
|
||||||
|
|
||||||
CLONE_FILTER=$(git -C fetch-filter config --local --get remote.origin.partialclonefilter)
|
|
||||||
|
|
||||||
if [ "$CLONE_FILTER" != "blob:none" ]; then
|
|
||||||
echo "Expected ./fetch-filter/.git/config to have 'remote.origin.partialclonefilter' set to 'blob:none'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -12,6 +12,6 @@ if [[ "$(git status --porcelain)" != "" ]]; then
|
||||||
echo ----------------------------------------
|
echo ----------------------------------------
|
||||||
echo Troubleshooting
|
echo Troubleshooting
|
||||||
echo ----------------------------------------
|
echo ----------------------------------------
|
||||||
echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run format && npm run build"
|
echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run all"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
#!/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
|
|
|
@ -1,63 +0,0 @@
|
||||||
#!/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
|
|
32
action.yml
32
action.yml
|
@ -8,7 +8,7 @@ inputs:
|
||||||
description: >
|
description: >
|
||||||
The branch, tag or SHA to checkout. When checking out the repository that
|
The branch, tag or SHA to checkout. When checking out the repository that
|
||||||
triggered a workflow, this defaults to the reference or SHA for that
|
triggered a workflow, this defaults to the reference or SHA for that
|
||||||
event. Otherwise, uses the default branch.
|
event. Otherwise, defaults to `master`.
|
||||||
token:
|
token:
|
||||||
description: >
|
description: >
|
||||||
Personal access token (PAT) used to fetch the repository. The PAT is configured
|
Personal access token (PAT) used to fetch the repository. The PAT is configured
|
||||||
|
@ -53,29 +53,9 @@ 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
|
||||||
filter:
|
|
||||||
description: >
|
|
||||||
Partially clone against a given filter.
|
|
||||||
Overrides sparse-checkout if set.
|
|
||||||
default: null
|
|
||||||
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.'
|
||||||
default: 1
|
default: 1
|
||||||
fetch-tags:
|
|
||||||
description: 'Whether to fetch tags, even if fetch-depth > 0.'
|
|
||||||
default: false
|
|
||||||
show-progress:
|
|
||||||
description: 'Whether to show progress status output when fetching.'
|
|
||||||
default: true
|
|
||||||
lfs:
|
lfs:
|
||||||
description: 'Whether to download Git-LFS files'
|
description: 'Whether to download Git-LFS files'
|
||||||
default: false
|
default: false
|
||||||
|
@ -88,13 +68,7 @@ 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: node20
|
using: node12
|
||||||
main: dist/index.js
|
main: dist/index.js
|
||||||
post: dist/index.js
|
post: dist/index.js
|
||||||
|
|
|
@ -24,31 +24,19 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
|
||||||
description: >
|
description: >
|
||||||
The branch, tag or SHA to checkout. When checking out the repository that
|
The branch, tag or SHA to checkout. When checking out the repository that
|
||||||
triggered a workflow, this defaults to the reference or SHA for that
|
triggered a workflow, this defaults to the reference or SHA for that
|
||||||
event. Otherwise, uses the default branch.
|
event. Otherwise, defaults to `master`.
|
||||||
token:
|
token:
|
||||||
description: >
|
description: >
|
||||||
Personal access token (PAT) used to fetch the repository. The PAT is configured
|
Personal access token (PAT) used to fetch the repository. The PAT is configured
|
||||||
with the local git config, which enables your scripts to run authenticated git
|
with the local git config, which enables your scripts to run authenticated git
|
||||||
commands. The post-job step removes the PAT.
|
commands. The post-job step removes the PAT. [Learn more about creating and using
|
||||||
|
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
|
||||||
|
|
||||||
We recommend using a service account with the least permissions necessary.
|
|
||||||
Also when generating a new PAT, select the least scopes necessary.
|
|
||||||
|
|
||||||
|
|
||||||
[Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
|
|
||||||
default: ${{ github.token }}
|
default: ${{ github.token }}
|
||||||
ssh-key:
|
ssh-key:
|
||||||
description: >
|
description: >
|
||||||
SSH key used to fetch the repository. The SSH key is configured with the local
|
SSH key used to fetch the repository. SSH key is configured with the local
|
||||||
git config, which enables your scripts to run authenticated git commands.
|
git config, which enables your scripts to run authenticated git commands.
|
||||||
The post-job step removes the SSH key.
|
The post-job step removes the SSH key. [Learn more about creating and using
|
||||||
|
|
||||||
|
|
||||||
We recommend using a service account with the least permissions necessary.
|
|
||||||
|
|
||||||
|
|
||||||
[Learn more about creating and using
|
|
||||||
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
|
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
|
||||||
ssh-known-hosts:
|
ssh-known-hosts:
|
||||||
description: >
|
description: >
|
||||||
|
@ -56,10 +44,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
|
||||||
SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example,
|
SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example,
|
||||||
`ssh-keyscan github.com`. The public key for github.com is always implicitly added.
|
`ssh-keyscan github.com`. The public key for github.com is always implicitly added.
|
||||||
ssh-strict:
|
ssh-strict:
|
||||||
description: >
|
description: 'Whether to perform strict host key checking'
|
||||||
Whether to perform strict host key checking. When true, adds the options `StrictHostKeyChecking=yes`
|
|
||||||
and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to
|
|
||||||
configure additional hosts.
|
|
||||||
default: true
|
default: true
|
||||||
persist-credentials:
|
persist-credentials:
|
||||||
description: 'Whether to configure the token or SSH key with the local git config'
|
description: 'Whether to configure the token or SSH key with the local git config'
|
||||||
|
@ -70,7 +55,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
|
||||||
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
|
||||||
fetch-depth:
|
fetch-depth:
|
||||||
description: 'Number of commits to fetch. 0 indicates all history for all tags and branches.'
|
description: 'Number of commits to fetch. 0 indicates all history.'
|
||||||
default: 1
|
default: 1
|
||||||
lfs:
|
lfs:
|
||||||
description: 'Whether to download Git-LFS files'
|
description: 'Whether to download Git-LFS files'
|
||||||
|
@ -79,11 +64,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
|
||||||
description: >
|
description: >
|
||||||
Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
Whether to checkout submodules: `true` to checkout submodules or `recursive` to
|
||||||
recursively checkout submodules.
|
recursively checkout submodules.
|
||||||
|
default: 'false'
|
||||||
|
|
||||||
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
|
|
||||||
converted to HTTPS.
|
|
||||||
default: false
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
|
@ -181,7 +162,7 @@ GITHUB_WORKSPACE=/home/runner/work/foo/foo
|
||||||
RUNNER_WORKSPACE=/home/runner/work/foo
|
RUNNER_WORKSPACE=/home/runner/work/foo
|
||||||
```
|
```
|
||||||
|
|
||||||
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 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 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.
|
||||||
|
|
||||||
|
@ -277,7 +258,7 @@ Note:
|
||||||
### Branching strategy and release tags
|
### Branching strategy and release tags
|
||||||
|
|
||||||
- Create a servicing branch for V1: `releases/v1`
|
- Create a servicing branch for V1: `releases/v1`
|
||||||
- Merge the changes into the default branch
|
- Merge the changes into `master`
|
||||||
- Release using a new tag `preview`
|
- Release using a new tag `preview`
|
||||||
- When stable, release using a new tag `v2`
|
- When stable, release using a new tag `v2`
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
|
@ -1,16 +1,14 @@
|
||||||
{
|
{
|
||||||
"name": "checkout",
|
"name": "checkout",
|
||||||
"version": "4.1.0",
|
"version": "2.0.2",
|
||||||
"description": "checkout action",
|
"description": "checkout action",
|
||||||
"main": "lib/main.js",
|
"main": "lib/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && ncc build && node lib/misc/generate-docs.js",
|
"build": "tsc && ncc build && node lib/misc/generate-docs.js",
|
||||||
"format": "prettier --write '**/*.ts'",
|
"format": "prettier --write **/*.ts",
|
||||||
"format-check": "prettier --check '**/*.ts'",
|
"format-check": "prettier --check **/*.ts",
|
||||||
"lint": "eslint src/**/*.ts",
|
"lint": "eslint src/**/*.ts",
|
||||||
"test": "jest",
|
"test": "jest"
|
||||||
"licensed-check": "src/misc/licensed-check.sh",
|
|
||||||
"licensed-generate": "src/misc/licensed-generate.sh"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -28,28 +26,27 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/actions/checkout#readme",
|
"homepage": "https://github.com/actions/checkout#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.1.3",
|
||||||
"@actions/exec": "^1.0.1",
|
"@actions/exec": "^1.0.1",
|
||||||
"@actions/github": "^5.0.0",
|
"@actions/github": "^2.0.2",
|
||||||
"@actions/io": "^1.1.3",
|
"@actions/io": "^1.0.1",
|
||||||
"@actions/tool-cache": "^1.1.2",
|
"@actions/tool-cache": "^1.1.2",
|
||||||
"uuid": "^3.3.3"
|
"uuid": "^3.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^24.0.23",
|
||||||
"@types/node": "^20.5.3",
|
"@types/node": "^12.7.12",
|
||||||
"@types/uuid": "^3.4.6",
|
"@types/uuid": "^3.4.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/parser": "^2.8.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@zeit/ncc": "^0.20.5",
|
||||||
"@vercel/ncc": "^0.36.1",
|
"eslint": "^5.16.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint-plugin-github": "^2.0.0",
|
||||||
"eslint-plugin-github": "^4.3.2",
|
"eslint-plugin-jest": "^22.21.0",
|
||||||
"eslint-plugin-jest": "^25.7.0",
|
"jest": "^24.9.0",
|
||||||
"jest": "^27.3.0",
|
"jest-circus": "^24.9.0",
|
||||||
"jest-circus": "^27.3.0",
|
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
"ts-jest": "^27.0.7",
|
"ts-jest": "^24.2.0",
|
||||||
"typescript": "^4.4.4"
|
"typescript": "^3.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ export function directoryExistsSync(path: string, required?: boolean): boolean {
|
||||||
try {
|
try {
|
||||||
stats = fs.statSync(path)
|
stats = fs.statSync(path)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if ((error as any)?.code === 'ENOENT') {
|
if (error.code === 'ENOENT') {
|
||||||
if (!required) {
|
if (!required) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,7 @@ export function directoryExistsSync(path: string, required?: boolean): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Encountered an error when checking whether path '${path}' exists: ${(error as any)
|
`Encountered an error when checking whether path '${path}' exists: ${error.message}`
|
||||||
?.message ?? error}`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,13 +39,12 @@ export function existsSync(path: string): boolean {
|
||||||
try {
|
try {
|
||||||
fs.statSync(path)
|
fs.statSync(path)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if ((error as any)?.code === 'ENOENT') {
|
if (error.code === 'ENOENT') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Encountered an error when checking whether path '${path}' exists: ${(error as any)
|
`Encountered an error when checking whether path '${path}' exists: ${error.message}`
|
||||||
?.message ?? error}`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +60,12 @@ export function fileExistsSync(path: string): boolean {
|
||||||
try {
|
try {
|
||||||
stats = fs.statSync(path)
|
stats = fs.statSync(path)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if ((error as any)?.code === 'ENOENT') {
|
if (error.code === 'ENOENT') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Encountered an error when checking whether path '${path}' exists: ${(error as any)
|
`Encountered an error when checking whether path '${path}' exists: ${error.message}`
|
||||||
?.message ?? error}`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,21 +7,20 @@ import * as os from 'os'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as regexpHelper from './regexp-helper'
|
import * as regexpHelper from './regexp-helper'
|
||||||
import * as stateHelper from './state-helper'
|
import * as stateHelper from './state-helper'
|
||||||
import * as urlHelper from './url-helper'
|
|
||||||
import {default as uuid} from 'uuid/v4'
|
import {default as uuid} from 'uuid/v4'
|
||||||
import {IGitCommandManager} from './git-command-manager'
|
import {IGitCommandManager} from './git-command-manager'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import {IGitSourceSettings} from './git-source-settings'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
const HOSTNAME = 'github.com'
|
||||||
const SSH_COMMAND_KEY = 'core.sshCommand'
|
const SSH_COMMAND_KEY = 'core.sshCommand'
|
||||||
|
|
||||||
export interface IGitAuthHelper {
|
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>
|
||||||
removeGlobalConfig(): Promise<void>
|
removeGlobalAuth(): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAuthHelper(
|
export function createAuthHelper(
|
||||||
|
@ -34,26 +33,24 @@ export function createAuthHelper(
|
||||||
class GitAuthHelper {
|
class GitAuthHelper {
|
||||||
private readonly git: IGitCommandManager
|
private readonly git: IGitCommandManager
|
||||||
private readonly settings: IGitSourceSettings
|
private readonly settings: IGitSourceSettings
|
||||||
private readonly tokenConfigKey: string
|
private readonly tokenConfigKey: string = `http.https://${HOSTNAME}/.extraheader`
|
||||||
private readonly tokenConfigValue: string
|
|
||||||
private readonly tokenPlaceholderConfigValue: string
|
private readonly tokenPlaceholderConfigValue: string
|
||||||
private readonly insteadOfKey: string
|
private readonly insteadOfKey: string = `url.https://${HOSTNAME}/.insteadOf`
|
||||||
private readonly insteadOfValues: string[] = []
|
private readonly insteadOfValue: string = `git@${HOSTNAME}:`
|
||||||
private sshCommand = ''
|
private sshCommand = ''
|
||||||
private sshKeyPath = ''
|
private sshKeyPath = ''
|
||||||
private sshKnownHostsPath = ''
|
private sshKnownHostsPath = ''
|
||||||
private temporaryHomePath = ''
|
private temporaryHomePath = ''
|
||||||
|
private tokenConfigValue: string
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
gitCommandManager: IGitCommandManager,
|
gitCommandManager: IGitCommandManager,
|
||||||
gitSourceSettings: IGitSourceSettings | undefined
|
gitSourceSettings?: IGitSourceSettings
|
||||||
) {
|
) {
|
||||||
this.git = gitCommandManager
|
this.git = gitCommandManager
|
||||||
this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings)
|
this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings)
|
||||||
|
|
||||||
// Token auth header
|
// Token auth header
|
||||||
const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl)
|
|
||||||
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}`,
|
||||||
'utf8'
|
'utf8'
|
||||||
|
@ -61,15 +58,6 @@ class GitAuthHelper {
|
||||||
core.setSecret(basicCredential)
|
core.setSecret(basicCredential)
|
||||||
this.tokenPlaceholderConfigValue = `AUTHORIZATION: basic ***`
|
this.tokenPlaceholderConfigValue = `AUTHORIZATION: basic ***`
|
||||||
this.tokenConfigValue = `AUTHORIZATION: basic ${basicCredential}`
|
this.tokenConfigValue = `AUTHORIZATION: basic ${basicCredential}`
|
||||||
|
|
||||||
// Instead of SSH URL
|
|
||||||
this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf` // "origin" is SCHEME://HOSTNAME[:PORT]
|
|
||||||
this.insteadOfValues.push(`git@${serverUrl.hostname}:`)
|
|
||||||
if (this.settings.workflowOrganizationId) {
|
|
||||||
this.insteadOfValues.push(
|
|
||||||
`org-${this.settings.workflowOrganizationId}@github.com:`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async configureAuth(): Promise<void> {
|
async configureAuth(): Promise<void> {
|
||||||
|
@ -81,11 +69,7 @@ class GitAuthHelper {
|
||||||
await this.configureToken()
|
await this.configureToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
async configureTempGlobalConfig(): Promise<string> {
|
async configureGlobalAuth(): Promise<void> {
|
||||||
// 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')
|
||||||
|
@ -104,7 +88,7 @@ class GitAuthHelper {
|
||||||
await fs.promises.stat(gitConfigPath)
|
await fs.promises.stat(gitConfigPath)
|
||||||
configExists = true
|
configExists = true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ((err as any)?.code !== 'ENOENT') {
|
if (err.code !== 'ENOENT') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,28 +99,20 @@ class GitAuthHelper {
|
||||||
await fs.promises.writeFile(newGitConfigPath, '')
|
await fs.promises.writeFile(newGitConfigPath, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override HOME
|
|
||||||
core.info(
|
|
||||||
`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`
|
|
||||||
)
|
|
||||||
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 {
|
try {
|
||||||
|
// Override HOME
|
||||||
|
core.info(
|
||||||
|
`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`
|
||||||
|
)
|
||||||
|
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
|
||||||
|
|
||||||
// Configure the token
|
// Configure the token
|
||||||
await this.configureToken(newGitConfigPath, true)
|
await this.configureToken(newGitConfigPath, true)
|
||||||
|
|
||||||
// Configure HTTPS instead of SSH
|
// Configure HTTPS instead of SSH
|
||||||
await this.git.tryConfigUnset(this.insteadOfKey, true)
|
await this.git.tryConfigUnset(this.insteadOfKey, true)
|
||||||
if (!this.settings.sshKey) {
|
if (!this.settings.sshKey) {
|
||||||
for (const insteadOfValue of this.insteadOfValues) {
|
await this.git.config(this.insteadOfKey, this.insteadOfValue, true)
|
||||||
await this.git.config(this.insteadOfKey, insteadOfValue, true, true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Unset in case somehow written to the real global config
|
// Unset in case somehow written to the real global config
|
||||||
|
@ -157,8 +133,7 @@ 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(
|
||||||
// wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
|
`git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url`,
|
||||||
`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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -167,7 +142,7 @@ class GitAuthHelper {
|
||||||
output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []
|
output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []
|
||||||
for (const configPath of configPaths) {
|
for (const configPath of configPaths) {
|
||||||
core.debug(`Replacing token placeholder in '${configPath}'`)
|
core.debug(`Replacing token placeholder in '${configPath}'`)
|
||||||
await this.replaceTokenPlaceholder(configPath)
|
this.replaceTokenPlaceholder(configPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.settings.sshKey) {
|
if (this.settings.sshKey) {
|
||||||
|
@ -178,12 +153,10 @@ class GitAuthHelper {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Configure HTTPS instead of SSH
|
// Configure HTTPS instead of SSH
|
||||||
for (const insteadOfValue of this.insteadOfValues) {
|
await this.git.submoduleForeach(
|
||||||
await this.git.submoduleForeach(
|
`git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`,
|
||||||
`git config --local --add '${this.insteadOfKey}' '${insteadOfValue}'`,
|
this.settings.nestedSubmodules
|
||||||
this.settings.nestedSubmodules
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,12 +166,10 @@ class GitAuthHelper {
|
||||||
await this.removeToken()
|
await this.removeToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeGlobalConfig(): Promise<void> {
|
async removeGlobalAuth(): Promise<void> {
|
||||||
if (this.temporaryHomePath?.length > 0) {
|
core.debug(`Unsetting HOME override`)
|
||||||
core.debug(`Unsetting HOME override`)
|
this.git.removeEnvironmentVariable('HOME')
|
||||||
this.git.removeEnvironmentVariable('HOME')
|
await io.rmRF(this.temporaryHomePath)
|
||||||
await io.rmRF(this.temporaryHomePath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async configureSsh(): Promise<void> {
|
private async configureSsh(): Promise<void> {
|
||||||
|
@ -236,7 +207,7 @@ class GitAuthHelper {
|
||||||
await fs.promises.readFile(userKnownHostsPath)
|
await fs.promises.readFile(userKnownHostsPath)
|
||||||
).toString()
|
).toString()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if ((err as any)?.code !== 'ENOENT') {
|
if (err.code !== 'ENOENT') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +218,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 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`
|
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`
|
||||||
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)
|
||||||
|
@ -325,7 +296,7 @@ class GitAuthHelper {
|
||||||
try {
|
try {
|
||||||
await io.rmRF(keyPath)
|
await io.rmRF(keyPath)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
core.debug(`${(err as any)?.message ?? err}`)
|
core.debug(err.message)
|
||||||
core.warning(`Failed to remove SSH key '${keyPath}'`)
|
core.warning(`Failed to remove SSH key '${keyPath}'`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,8 +337,7 @@ class GitAuthHelper {
|
||||||
|
|
||||||
const pattern = regexpHelper.escape(configKey)
|
const pattern = regexpHelper.escape(configKey)
|
||||||
await this.git.submoduleForeach(
|
await this.git.submoduleForeach(
|
||||||
// wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
|
`git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :`,
|
||||||
`sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`,
|
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
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'
|
||||||
import * as refHelper from './ref-helper'
|
|
||||||
import * as regexpHelper from './regexp-helper'
|
import * as regexpHelper from './regexp-helper'
|
||||||
import * as retryHelper from './retry-helper'
|
import * as retryHelper from './retry-helper'
|
||||||
import {GitVersion} from './git-version'
|
import {GitVersion} from './git-version'
|
||||||
|
@ -17,60 +15,41 @@ 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(
|
||||||
configKey: string,
|
configKey: string,
|
||||||
configValue: string,
|
configValue: string,
|
||||||
globalConfig?: boolean,
|
globalConfig?: boolean
|
||||||
add?: boolean
|
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
|
configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
|
||||||
fetch(
|
fetch(fetchDepth: number, refSpec: string[]): Promise<void>
|
||||||
refSpec: string[],
|
|
||||||
options: {
|
|
||||||
filter?: string
|
|
||||||
fetchDepth?: number
|
|
||||||
fetchTags?: boolean
|
|
||||||
showProgress?: boolean
|
|
||||||
}
|
|
||||||
): Promise<void>
|
|
||||||
getDefaultBranch(repositoryUrl: string): Promise<string>
|
|
||||||
getWorkingDirectory(): string
|
getWorkingDirectory(): string
|
||||||
init(): Promise<void>
|
init(): Promise<void>
|
||||||
isDetached(): Promise<boolean>
|
isDetached(): Promise<boolean>
|
||||||
lfsFetch(ref: string): Promise<void>
|
lfsFetch(ref: string): Promise<void>
|
||||||
lfsInstall(): Promise<void>
|
lfsInstall(): Promise<void>
|
||||||
log1(format?: string): Promise<string>
|
log1(): Promise<void>
|
||||||
remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
|
remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
|
||||||
removeEnvironmentVariable(name: string): void
|
removeEnvironmentVariable(name: string): void
|
||||||
revParse(ref: string): Promise<string>
|
|
||||||
setEnvironmentVariable(name: string, value: string): void
|
setEnvironmentVariable(name: string, value: string): void
|
||||||
shaExists(sha: string): Promise<boolean>
|
setRemoteUrl(url: string): Promise<void>
|
||||||
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>
|
||||||
tryDisableAutomaticGarbageCollection(): Promise<boolean>
|
tryDisableAutomaticGarbageCollection(): Promise<boolean>
|
||||||
tryGetFetchUrl(): Promise<string>
|
tryGetRemoteUrl(): Promise<string>
|
||||||
tryReset(): Promise<boolean>
|
tryReset(): Promise<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
return await GitCommandManager.createCommandManager(workingDirectory, lfs)
|
||||||
workingDirectory,
|
|
||||||
lfs,
|
|
||||||
doSparseCheckout
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GitCommandManager {
|
class GitCommandManager {
|
||||||
|
@ -80,7 +59,6 @@ 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()
|
||||||
|
@ -112,11 +90,8 @@ 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
|
||||||
// TODO(https://github.com/actions/checkout/issues/786): this implementation uses
|
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.
|
||||||
// "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) {
|
||||||
|
@ -125,73 +100,24 @@ class GitCommandManager {
|
||||||
args.push('--branches')
|
args.push('--branches')
|
||||||
}
|
}
|
||||||
|
|
||||||
const stderr: string[] = []
|
const output = await this.execGit(args)
|
||||||
const errline: string[] = []
|
|
||||||
const stdout: string[] = []
|
|
||||||
const stdline: string[] = []
|
|
||||||
|
|
||||||
const listeners = {
|
for (let branch of output.stdout.trim().split('\n')) {
|
||||||
stderr: (data: Buffer) => {
|
|
||||||
stderr.push(data.toString())
|
|
||||||
},
|
|
||||||
errline: (data: Buffer) => {
|
|
||||||
errline.push(data.toString())
|
|
||||||
},
|
|
||||||
stdout: (data: Buffer) => {
|
|
||||||
stdout.push(data.toString())
|
|
||||||
},
|
|
||||||
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()
|
branch = branch.trim()
|
||||||
if (!branch) {
|
if (branch) {
|
||||||
continue
|
if (branch.startsWith('refs/heads/')) {
|
||||||
}
|
branch = branch.substr('refs/heads/'.length)
|
||||||
|
} else if (branch.startsWith('refs/remotes/')) {
|
||||||
|
branch = branch.substr('refs/remotes/'.length)
|
||||||
|
}
|
||||||
|
|
||||||
if (branch.startsWith('refs/heads/')) {
|
result.push(branch)
|
||||||
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) {
|
||||||
|
@ -211,15 +137,14 @@ class GitCommandManager {
|
||||||
async config(
|
async config(
|
||||||
configKey: string,
|
configKey: string,
|
||||||
configValue: string,
|
configValue: string,
|
||||||
globalConfig?: boolean,
|
globalConfig?: boolean
|
||||||
add?: boolean
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const args: string[] = ['config', globalConfig ? '--global' : '--local']
|
await this.execGit([
|
||||||
if (add) {
|
'config',
|
||||||
args.push('--add')
|
globalConfig ? '--global' : '--local',
|
||||||
}
|
configKey,
|
||||||
args.push(...[configKey, configValue])
|
configValue
|
||||||
await this.execGit(args)
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
async configExists(
|
async configExists(
|
||||||
|
@ -240,31 +165,18 @@ class GitCommandManager {
|
||||||
return output.exitCode === 0
|
return output.exitCode === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetch(
|
async fetch(fetchDepth: number, refSpec: string[]): Promise<void> {
|
||||||
refSpec: string[],
|
const args = [
|
||||||
options: {
|
'-c',
|
||||||
filter?: string
|
'protocol.version=2',
|
||||||
fetchDepth?: number
|
'fetch',
|
||||||
fetchTags?: boolean
|
'--no-tags',
|
||||||
showProgress?: boolean
|
'--prune',
|
||||||
}
|
'--progress',
|
||||||
): Promise<void> {
|
'--no-recurse-submodules'
|
||||||
const args = ['-c', 'protocol.version=2', 'fetch']
|
]
|
||||||
if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) {
|
if (fetchDepth > 0) {
|
||||||
args.push('--no-tags')
|
args.push(`--depth=${fetchDepth}`)
|
||||||
}
|
|
||||||
|
|
||||||
args.push('--prune', '--no-recurse-submodules')
|
|
||||||
if (options.showProgress) {
|
|
||||||
args.push('--progress')
|
|
||||||
}
|
|
||||||
|
|
||||||
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')
|
||||||
|
@ -284,34 +196,6 @@ class GitCommandManager {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDefaultBranch(repositoryUrl: string): Promise<string> {
|
|
||||||
let output: GitOutput | undefined
|
|
||||||
await retryHelper.execute(async () => {
|
|
||||||
output = await this.execGit([
|
|
||||||
'ls-remote',
|
|
||||||
'--quiet',
|
|
||||||
'--exit-code',
|
|
||||||
'--symref',
|
|
||||||
repositoryUrl,
|
|
||||||
'HEAD'
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
if (output) {
|
|
||||||
// Satisfy compiler, will always be set
|
|
||||||
for (let line of output.stdout.trim().split('\n')) {
|
|
||||||
line = line.trim()
|
|
||||||
if (line.startsWith('ref:') || line.endsWith('HEAD')) {
|
|
||||||
return line
|
|
||||||
.substr('ref:'.length, line.length - 'ref:'.length - 'HEAD'.length)
|
|
||||||
.trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Unexpected output when retrieving default branch')
|
|
||||||
}
|
|
||||||
|
|
||||||
getWorkingDirectory(): string {
|
getWorkingDirectory(): string {
|
||||||
return this.workingDirectory
|
return this.workingDirectory
|
||||||
}
|
}
|
||||||
|
@ -342,11 +226,8 @@ class GitCommandManager {
|
||||||
await this.execGit(['lfs', 'install', '--local'])
|
await this.execGit(['lfs', 'install', '--local'])
|
||||||
}
|
}
|
||||||
|
|
||||||
async log1(format?: string): Promise<string> {
|
async log1(): Promise<void> {
|
||||||
const args = format ? ['log', '-1', format] : ['log', '-1']
|
await this.execGit(['log', '-1'])
|
||||||
const silent = format ? false : true
|
|
||||||
const output = await this.execGit(args, false, silent)
|
|
||||||
return output.stdout
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async remoteAdd(remoteName: string, remoteUrl: string): Promise<void> {
|
async remoteAdd(remoteName: string, remoteUrl: string): Promise<void> {
|
||||||
|
@ -357,25 +238,12 @@ class GitCommandManager {
|
||||||
delete this.gitEnv[name]
|
delete this.gitEnv[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves a ref to a SHA. For a branch or lightweight tag, the commit SHA is returned.
|
|
||||||
* For an annotated tag, the tag SHA is returned.
|
|
||||||
* @param {string} ref For example: 'refs/heads/main' or '/refs/tags/v1'
|
|
||||||
* @returns {Promise<string>}
|
|
||||||
*/
|
|
||||||
async revParse(ref: string): Promise<string> {
|
|
||||||
const output = await this.execGit(['rev-parse', ref])
|
|
||||||
return output.stdout.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
setEnvironmentVariable(name: string, value: string): void {
|
setEnvironmentVariable(name: string, value: string): void {
|
||||||
this.gitEnv[name] = value
|
this.gitEnv[name] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
async shaExists(sha: string): Promise<boolean> {
|
async setRemoteUrl(value: string): Promise<void> {
|
||||||
const args = ['rev-parse', '--verify', '--quiet', `${sha}^{object}`]
|
await this.config('git.remote.url', value)
|
||||||
const output = await this.execGit(args, true)
|
|
||||||
return output.exitCode === 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async submoduleForeach(command: string, recursive: boolean): Promise<string> {
|
async submoduleForeach(command: string, recursive: boolean): Promise<string> {
|
||||||
|
@ -412,12 +280,6 @@ 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()
|
||||||
|
@ -452,7 +314,7 @@ class GitCommandManager {
|
||||||
return output.exitCode === 0
|
return output.exitCode === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
async tryGetFetchUrl(): Promise<string> {
|
async tryGetRemoteUrl(): Promise<string> {
|
||||||
const output = await this.execGit(
|
const output = await this.execGit(
|
||||||
['config', '--local', '--get', 'remote.origin.url'],
|
['config', '--local', '--get', 'remote.origin.url'],
|
||||||
true
|
true
|
||||||
|
@ -477,23 +339,16 @@ 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(
|
await result.initializeCommandManager(workingDirectory, lfs)
|
||||||
workingDirectory,
|
|
||||||
lfs,
|
|
||||||
doSparseCheckout
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private async execGit(
|
private async execGit(
|
||||||
args: string[],
|
args: string[],
|
||||||
allowAllExitCodes = false,
|
allowAllExitCodes = false
|
||||||
silent = false,
|
|
||||||
customListeners = {}
|
|
||||||
): Promise<GitOutput> {
|
): Promise<GitOutput> {
|
||||||
fshelper.directoryExistsSync(this.workingDirectory, true)
|
fshelper.directoryExistsSync(this.workingDirectory, true)
|
||||||
|
|
||||||
|
@ -507,36 +362,27 @@ class GitCommandManager {
|
||||||
env[key] = this.gitEnv[key]
|
env[key] = this.gitEnv[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultListener = {
|
|
||||||
stdout: (data: Buffer) => {
|
|
||||||
stdout.push(data.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergedListeners = {...defaultListener, ...customListeners}
|
|
||||||
|
|
||||||
const stdout: string[] = []
|
const stdout: string[] = []
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
cwd: this.workingDirectory,
|
cwd: this.workingDirectory,
|
||||||
env,
|
env,
|
||||||
silent,
|
|
||||||
ignoreReturnCode: allowAllExitCodes,
|
ignoreReturnCode: allowAllExitCodes,
|
||||||
listeners: mergedListeners
|
listeners: {
|
||||||
|
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
|
||||||
|
|
||||||
|
@ -599,16 +445,6 @@ 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}`)
|
||||||
|
|
|
@ -5,20 +5,29 @@ 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'
|
||||||
import {IGitCommandManager} from './git-command-manager'
|
import {IGitCommandManager} from './git-command-manager'
|
||||||
|
import {IGitSourceSettings} from './git-source-settings'
|
||||||
|
|
||||||
export async function prepareExistingDirectory(
|
export async function prepareExistingDirectory(
|
||||||
git: IGitCommandManager | undefined,
|
git: IGitCommandManager | undefined,
|
||||||
repositoryPath: string,
|
repositoryPath: string,
|
||||||
repositoryUrl: string,
|
preferredRemoteUrl: string,
|
||||||
clean: boolean,
|
allowedRemoteUrls: string[],
|
||||||
ref: string
|
clean: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
assert.ok(repositoryPath, 'Expected repositoryPath to be defined')
|
assert.ok(repositoryPath, 'Expected repositoryPath to be defined')
|
||||||
assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined')
|
assert.ok(preferredRemoteUrl, 'Expected preferredRemoteUrl to be defined')
|
||||||
|
assert.ok(allowedRemoteUrls, 'Expected allowedRemoteUrls to be defined')
|
||||||
|
assert.ok(
|
||||||
|
allowedRemoteUrls.length,
|
||||||
|
'Expected allowedRemoteUrls to have at least one value'
|
||||||
|
)
|
||||||
|
|
||||||
// Indicates whether to delete the directory contents
|
// Indicates whether to delete the directory contents
|
||||||
let remove = false
|
let remove = false
|
||||||
|
|
||||||
|
// The remote URL
|
||||||
|
let remoteUrl: string
|
||||||
|
|
||||||
// Check whether using git or REST API
|
// Check whether using git or REST API
|
||||||
if (!git) {
|
if (!git) {
|
||||||
remove = true
|
remove = true
|
||||||
|
@ -26,7 +35,7 @@ export async function prepareExistingDirectory(
|
||||||
// Fetch URL does not match
|
// Fetch URL does not match
|
||||||
else if (
|
else if (
|
||||||
!fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) ||
|
!fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) ||
|
||||||
repositoryUrl !== (await git.tryGetFetchUrl())
|
allowedRemoteUrls.indexOf((remoteUrl = await git.tryGetRemoteUrl())) < 0
|
||||||
) {
|
) {
|
||||||
remove = true
|
remove = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -39,9 +48,7 @@ export async function prepareExistingDirectory(
|
||||||
try {
|
try {
|
||||||
await io.rmRF(lockPath)
|
await io.rmRF(lockPath)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.debug(
|
core.debug(`Unable to delete '${lockPath}'. ${error.message}`)
|
||||||
`Unable to delete '${lockPath}'. ${(error as any)?.message ?? error}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,41 +65,19 @@ export async function prepareExistingDirectory(
|
||||||
await git.branchDelete(false, branch)
|
await git.branchDelete(false, branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any conflicting refs/remotes/origin/*
|
// Remove all refs/remotes/origin/* to avoid conflicts
|
||||||
// Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar
|
branches = await git.branchList(true)
|
||||||
// Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo
|
for (const branch of branches) {
|
||||||
if (ref) {
|
await git.branchDelete(true, branch)
|
||||||
ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`
|
|
||||||
if (ref.startsWith('refs/heads/')) {
|
|
||||||
const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length)
|
|
||||||
const upperName1Slash = `${upperName1}/`
|
|
||||||
branches = await git.branchList(true)
|
|
||||||
for (const branch of branches) {
|
|
||||||
const upperName2 = branch.substr('origin/'.length).toUpperCase()
|
|
||||||
const upperName2Slash = `${upperName2}/`
|
|
||||||
if (
|
|
||||||
upperName1.startsWith(upperName2Slash) ||
|
|
||||||
upperName2.startsWith(upperName1Slash)
|
|
||||||
) {
|
|
||||||
await git.branchDelete(true, branch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 further 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 futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`
|
||||||
)
|
)
|
||||||
remove = true
|
remove = true
|
||||||
} else if (!(await git.tryReset())) {
|
} else if (!(await git.tryReset())) {
|
||||||
|
@ -106,6 +91,13 @@ export async function prepareExistingDirectory(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update to the preferred remote URL
|
||||||
|
if (remoteUrl !== preferredRemoteUrl) {
|
||||||
|
core.startGroup('Updating the remote URL')
|
||||||
|
await git.setRemoteUrl(preferredRemoteUrl)
|
||||||
|
core.endGroup()
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning(
|
core.warning(
|
||||||
`Unable to prepare the existing repository. The repository will be recreated instead.`
|
`Unable to prepare the existing repository. The repository will be recreated instead.`
|
||||||
|
|
|
@ -8,16 +8,27 @@ import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as refHelper from './ref-helper'
|
import * as refHelper from './ref-helper'
|
||||||
import * as stateHelper from './state-helper'
|
import * as stateHelper from './state-helper'
|
||||||
import * as urlHelper from './url-helper'
|
|
||||||
import {IGitCommandManager} from './git-command-manager'
|
import {IGitCommandManager} from './git-command-manager'
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import {IGitSourceSettings} from './git-source-settings'
|
||||||
|
|
||||||
|
const hostname = 'github.com'
|
||||||
|
|
||||||
export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
||||||
// Repository URL
|
|
||||||
core.info(
|
core.info(
|
||||||
`Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}`
|
`Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}`
|
||||||
)
|
)
|
||||||
const repositoryUrl = urlHelper.getFetchUrl(settings)
|
|
||||||
|
// Remote URL
|
||||||
|
const httpsUrl = `https://${hostname}/${encodeURIComponent(
|
||||||
|
settings.repositoryOwner
|
||||||
|
)}/${encodeURIComponent(settings.repositoryName)}`
|
||||||
|
const sshUrl = `git@${hostname}:${encodeURIComponent(
|
||||||
|
settings.repositoryOwner
|
||||||
|
)}/${encodeURIComponent(settings.repositoryName)}.git`
|
||||||
|
|
||||||
|
// Always fetch the workflow repository using the token, not the SSH key
|
||||||
|
const initialRemoteUrl =
|
||||||
|
!settings.sshKey || settings.isWorkflowRepository ? httpsUrl : sshUrl
|
||||||
|
|
||||||
// Remove conflicting file path
|
// Remove conflicting file path
|
||||||
if (fsHelper.fileExistsSync(settings.repositoryPath)) {
|
if (fsHelper.fileExistsSync(settings.repositoryPath)) {
|
||||||
|
@ -36,116 +47,73 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
||||||
const git = await getGitCommandManager(settings)
|
const git = await getGitCommandManager(settings)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
let authHelper: gitAuthHelper.IGitAuthHelper | null = null
|
// Prepare existing directory, otherwise recreate
|
||||||
try {
|
if (isExisting) {
|
||||||
if (git) {
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
git,
|
||||||
if (settings.setSafeDirectory) {
|
settings.repositoryPath,
|
||||||
// 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
|
initialRemoteUrl,
|
||||||
// Otherwise all git commands we run in a container fail
|
[httpsUrl, sshUrl],
|
||||||
await authHelper.configureTempGlobalConfig()
|
settings.clean
|
||||||
core.info(
|
)
|
||||||
`Adding repository directory to the temporary git global config as a safe directory`
|
}
|
||||||
)
|
|
||||||
|
|
||||||
await git
|
if (!git) {
|
||||||
.config('safe.directory', settings.repositoryPath, true, true)
|
// Downloading using REST API
|
||||||
.catch(error => {
|
core.info(`The repository will be downloaded using the GitHub REST API`)
|
||||||
core.info(
|
core.info(
|
||||||
`Failed to initialize safe directory with error: ${error}`
|
`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
|
||||||
)
|
)
|
||||||
})
|
if (settings.submodules) {
|
||||||
|
throw new Error(
|
||||||
stateHelper.setSafeDirectory()
|
`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(
|
||||||
// Prepare existing directory, otherwise recreate
|
`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.`
|
||||||
if (isExisting) {
|
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
|
||||||
git,
|
|
||||||
settings.repositoryPath,
|
|
||||||
repositoryUrl,
|
|
||||||
settings.clean,
|
|
||||||
settings.ref
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!git) {
|
await githubApiHelper.downloadRepository(
|
||||||
// Downloading using REST API
|
settings.authToken,
|
||||||
core.info(`The repository will be downloaded using the GitHub REST API`)
|
settings.repositoryOwner,
|
||||||
core.info(
|
settings.repositoryName,
|
||||||
`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
|
settings.ref,
|
||||||
)
|
settings.commit,
|
||||||
if (settings.submodules) {
|
settings.repositoryPath
|
||||||
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.`
|
return
|
||||||
)
|
}
|
||||||
} 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.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await githubApiHelper.downloadRepository(
|
// Save state for POST action
|
||||||
settings.authToken,
|
stateHelper.setRepositoryPath(settings.repositoryPath)
|
||||||
settings.repositoryOwner,
|
|
||||||
settings.repositoryName,
|
|
||||||
settings.ref,
|
|
||||||
settings.commit,
|
|
||||||
settings.repositoryPath,
|
|
||||||
settings.githubServerUrl
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save state for POST action
|
// Initialize the repository
|
||||||
stateHelper.setRepositoryPath(settings.repositoryPath)
|
if (
|
||||||
|
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
||||||
// Initialize the repository
|
) {
|
||||||
if (
|
core.startGroup('Initializing the repository')
|
||||||
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
await git.init()
|
||||||
) {
|
await git.remoteAdd('origin', initialRemoteUrl)
|
||||||
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()
|
||||||
|
}
|
||||||
|
|
||||||
// If we didn't initialize it above, do it now
|
// Disable automatic garbage collection
|
||||||
if (!authHelper) {
|
core.startGroup('Disabling automatic garbage collection')
|
||||||
authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
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()
|
||||||
|
|
||||||
|
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()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
// Determine the default branch
|
|
||||||
if (!settings.ref && !settings.commit) {
|
|
||||||
core.startGroup('Determining the default branch')
|
|
||||||
if (settings.sshKey) {
|
|
||||||
settings.ref = await git.getDefaultBranch(repositoryUrl)
|
|
||||||
} else {
|
|
||||||
settings.ref = await githubApiHelper.getDefaultBranch(
|
|
||||||
settings.authToken,
|
|
||||||
settings.repositoryOwner,
|
|
||||||
settings.repositoryName,
|
|
||||||
settings.githubServerUrl
|
|
||||||
)
|
|
||||||
}
|
|
||||||
core.endGroup()
|
|
||||||
}
|
|
||||||
|
|
||||||
// LFS install
|
// LFS install
|
||||||
if (settings.lfs) {
|
if (settings.lfs) {
|
||||||
await git.lfsInstall()
|
await git.lfsInstall()
|
||||||
|
@ -153,39 +121,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
||||||
|
|
||||||
// Fetch
|
// Fetch
|
||||||
core.startGroup('Fetching the repository')
|
core.startGroup('Fetching the repository')
|
||||||
const fetchOptions: {
|
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
|
||||||
filter?: string
|
await git.fetch(settings.fetchDepth, refSpec)
|
||||||
fetchDepth?: number
|
|
||||||
fetchTags?: boolean
|
|
||||||
showProgress?: boolean
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
if (settings.filter) {
|
|
||||||
fetchOptions.filter = settings.filter
|
|
||||||
} else if (settings.sparseCheckout) {
|
|
||||||
fetchOptions.filter = 'blob:none'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.fetchDepth <= 0) {
|
|
||||||
// Fetch all branches and tags
|
|
||||||
let refSpec = refHelper.getRefSpecForAllHistory(
|
|
||||||
settings.ref,
|
|
||||||
settings.commit
|
|
||||||
)
|
|
||||||
await git.fetch(refSpec, fetchOptions)
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
if (!(await refHelper.testRef(git, settings.ref, settings.commit))) {
|
|
||||||
refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
|
|
||||||
await git.fetch(refSpec, fetchOptions)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fetchOptions.fetchDepth = settings.fetchDepth
|
|
||||||
fetchOptions.fetchTags = settings.fetchTags
|
|
||||||
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
|
|
||||||
await git.fetch(refSpec, fetchOptions)
|
|
||||||
}
|
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
// Checkout info
|
// Checkout info
|
||||||
|
@ -200,21 +137,16 @@ 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.
|
||||||
// For sparse checkouts, let `checkout` fetch the needed objects lazily.
|
if (settings.lfs) {
|
||||||
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
|
// Fix URL when using SSH
|
||||||
if (settings.sparseCheckout) {
|
if (settings.sshKey && initialRemoteUrl !== sshUrl) {
|
||||||
core.startGroup('Setting up sparse checkout')
|
core.startGroup('Updating the remote URL')
|
||||||
if (settings.sparseCheckoutConeMode) {
|
await git.setRemoteUrl(sshUrl)
|
||||||
await git.sparseCheckout(settings.sparseCheckout)
|
|
||||||
} else {
|
|
||||||
await git.sparseCheckoutNonConeMode(settings.sparseCheckout)
|
|
||||||
}
|
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,54 +157,45 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
||||||
|
|
||||||
// Submodules
|
// Submodules
|
||||||
if (settings.submodules) {
|
if (settings.submodules) {
|
||||||
// Temporarily override global config
|
try {
|
||||||
core.startGroup('Setting up auth for fetching submodules')
|
// Temporarily override global config
|
||||||
await authHelper.configureGlobalAuth()
|
core.startGroup('Setting up auth for fetching submodules')
|
||||||
core.endGroup()
|
await authHelper.configureGlobalAuth()
|
||||||
|
|
||||||
// Checkout submodules
|
|
||||||
core.startGroup('Fetching submodules')
|
|
||||||
await git.submoduleSync(settings.nestedSubmodules)
|
|
||||||
await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
|
|
||||||
await git.submoduleForeach(
|
|
||||||
'git config --local gc.auto 0',
|
|
||||||
settings.nestedSubmodules
|
|
||||||
)
|
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
// Persist credentials
|
|
||||||
if (settings.persistCredentials) {
|
|
||||||
core.startGroup('Persisting credentials for submodules')
|
|
||||||
await authHelper.configureSubmoduleAuth()
|
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
|
// Checkout submodules
|
||||||
|
core.startGroup('Fetching submodules')
|
||||||
|
await git.submoduleSync(settings.nestedSubmodules)
|
||||||
|
await git.submoduleUpdate(
|
||||||
|
settings.fetchDepth,
|
||||||
|
settings.nestedSubmodules
|
||||||
|
)
|
||||||
|
await git.submoduleForeach(
|
||||||
|
'git config --local gc.auto 0',
|
||||||
|
settings.nestedSubmodules
|
||||||
|
)
|
||||||
|
core.endGroup()
|
||||||
|
|
||||||
|
// Persist credentials
|
||||||
|
if (settings.persistCredentials) {
|
||||||
|
core.startGroup('Persisting credentials for submodules')
|
||||||
|
await authHelper.configureSubmoduleAuth()
|
||||||
|
core.endGroup()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// Remove temporary global config override
|
||||||
|
await authHelper.removeGlobalAuth()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get commit information
|
// Dump some info about the checked out commit
|
||||||
const commitInfo = await git.log1()
|
await git.log1()
|
||||||
|
|
||||||
// Log commit sha
|
|
||||||
await git.log1("--format='%H'")
|
|
||||||
|
|
||||||
// Check for incorrect pull request merge commit
|
|
||||||
await refHelper.checkCommitInfo(
|
|
||||||
settings.authToken,
|
|
||||||
commitInfo,
|
|
||||||
settings.repositoryOwner,
|
|
||||||
settings.repositoryName,
|
|
||||||
settings.ref,
|
|
||||||
settings.commit,
|
|
||||||
settings.githubServerUrl
|
|
||||||
)
|
|
||||||
} finally {
|
} finally {
|
||||||
// Remove auth
|
// Remove auth
|
||||||
if (authHelper) {
|
if (!settings.persistCredentials) {
|
||||||
if (!settings.persistCredentials) {
|
core.startGroup('Removing auth')
|
||||||
core.startGroup('Removing auth')
|
await authHelper.removeAuth()
|
||||||
await authHelper.removeAuth()
|
core.endGroup()
|
||||||
core.endGroup()
|
|
||||||
}
|
|
||||||
authHelper.removeGlobalConfig()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,37 +211,14 @@ export async function cleanup(repositoryPath: string): Promise<void> {
|
||||||
|
|
||||||
let git: IGitCommandManager
|
let git: IGitCommandManager
|
||||||
try {
|
try {
|
||||||
git = await gitCommandManager.createCommandManager(
|
git = await gitCommandManager.createCommandManager(repositoryPath, false)
|
||||||
repositoryPath,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove auth
|
// Remove auth
|
||||||
const authHelper = gitAuthHelper.createAuthHelper(git)
|
const authHelper = gitAuthHelper.createAuthHelper(git)
|
||||||
try {
|
await authHelper.removeAuth()
|
||||||
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(
|
||||||
|
@ -328,8 +228,7 @@ 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
|
||||||
|
|
|
@ -14,6 +14,11 @@ export interface IGitSourceSettings {
|
||||||
*/
|
*/
|
||||||
repositoryName: string
|
repositoryName: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the repository is main workflow repository
|
||||||
|
*/
|
||||||
|
isWorkflowRepository: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ref to fetch
|
* The ref to fetch
|
||||||
*/
|
*/
|
||||||
|
@ -29,36 +34,11 @@ export interface IGitSourceSettings {
|
||||||
*/
|
*/
|
||||||
clean: boolean
|
clean: boolean
|
||||||
|
|
||||||
/**
|
|
||||||
* The filter determining which objects to include
|
|
||||||
*/
|
|
||||||
filter: string | undefined
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 use the --progress option when fetching
|
|
||||||
*/
|
|
||||||
showProgress: boolean
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether to fetch LFS objects
|
* Indicates whether to fetch LFS objects
|
||||||
*/
|
*/
|
||||||
|
@ -98,19 +78,4 @@ export interface IGitSourceSettings {
|
||||||
* Indicates whether to persist the credentials on disk to enable scripting authenticated git commands
|
* Indicates whether to persist the credentials on disk to enable scripting authenticated git commands
|
||||||
*/
|
*/
|
||||||
persistCredentials: boolean
|
persistCredentials: boolean
|
||||||
|
|
||||||
/**
|
|
||||||
* Organization ID for the currently running workflow (used for auth settings)
|
|
||||||
*/
|
|
||||||
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 {getServerApiUrl} from './url-helper'
|
import {ReposGetArchiveLinkParams} from '@octokit/rest'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
|
||||||
|
@ -17,19 +17,12 @@ 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
|
|
||||||
if (!ref && !commit) {
|
|
||||||
core.info('Determining the default branch')
|
|
||||||
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, baseUrl)
|
return await downloadArchive(authToken, owner, repo, ref, commit)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Write archive to disk
|
// Write archive to disk
|
||||||
|
@ -48,7 +41,7 @@ export async function downloadRepository(
|
||||||
} else {
|
} else {
|
||||||
await toolCache.extractTar(archivePath, extractPath)
|
await toolCache.extractTar(archivePath, extractPath)
|
||||||
}
|
}
|
||||||
await io.rmRF(archivePath)
|
io.rmRF(archivePath)
|
||||||
|
|
||||||
// Determine the path of the repository content. The archive contains
|
// Determine the path of the repository content. The archive contains
|
||||||
// a top-level folder and the repository content is inside.
|
// a top-level folder and the repository content is inside.
|
||||||
|
@ -71,53 +64,7 @@ export async function downloadRepository(
|
||||||
await io.mv(sourcePath, targetPath)
|
await io.mv(sourcePath, targetPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await io.rmRF(extractPath)
|
io.rmRF(extractPath)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks up the default branch name
|
|
||||||
*/
|
|
||||||
export async function getDefaultBranch(
|
|
||||||
authToken: string,
|
|
||||||
owner: string,
|
|
||||||
repo: string,
|
|
||||||
baseUrl?: string
|
|
||||||
): Promise<string> {
|
|
||||||
return await retryHelper.execute(async () => {
|
|
||||||
core.info('Retrieving the default branch name')
|
|
||||||
const octokit = github.getOctokit(authToken, {
|
|
||||||
baseUrl: getServerApiUrl(baseUrl)
|
|
||||||
})
|
|
||||||
let result: string
|
|
||||||
try {
|
|
||||||
// Get the default branch from the repo info
|
|
||||||
const response = await octokit.rest.repos.get({owner, repo})
|
|
||||||
result = response.data.default_branch
|
|
||||||
assert.ok(result, 'default_branch cannot be empty')
|
|
||||||
} catch (err) {
|
|
||||||
// Handle .wiki repo
|
|
||||||
if (
|
|
||||||
(err as any)?.status === 404 &&
|
|
||||||
repo.toUpperCase().endsWith('.WIKI')
|
|
||||||
) {
|
|
||||||
result = 'master'
|
|
||||||
}
|
|
||||||
// Otherwise error
|
|
||||||
else {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the default branch
|
|
||||||
core.info(`Default branch '${result}'`)
|
|
||||||
|
|
||||||
// Prefix with 'refs/heads'
|
|
||||||
if (!result.startsWith('refs/')) {
|
|
||||||
result = `refs/heads/${result}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadArchive(
|
async function downloadArchive(
|
||||||
|
@ -125,19 +72,21 @@ 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 = github.getOctokit(authToken, {
|
const octokit = new github.GitHub(authToken)
|
||||||
baseUrl: getServerApiUrl(baseUrl)
|
const params: ReposGetArchiveLinkParams = {
|
||||||
})
|
|
||||||
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
|
||||||
})
|
}
|
||||||
return Buffer.from(response.data as ArrayBuffer) // response.data is ArrayBuffer
|
const response = await octokit.repos.getArchiveLink(params)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,11 @@ import * as core from '@actions/core'
|
||||||
import * as fsHelper from './fs-helper'
|
import * as fsHelper from './fs-helper'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as workflowContextHelper from './workflow-context-helper'
|
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
import {IGitSourceSettings} from './git-source-settings'
|
||||||
|
|
||||||
export async function getInputs(): Promise<IGitSourceSettings> {
|
const hostname = 'github.com'
|
||||||
|
|
||||||
|
export function getInputs(): IGitSourceSettings {
|
||||||
const result = ({} as unknown) as IGitSourceSettings
|
const result = ({} as unknown) as IGitSourceSettings
|
||||||
|
|
||||||
// GitHub workspace
|
// GitHub workspace
|
||||||
|
@ -52,23 +53,27 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workflow repository?
|
// Workflow repository?
|
||||||
const isWorkflowRepository =
|
result.isWorkflowRepository =
|
||||||
qualifiedRepository.toUpperCase() ===
|
qualifiedRepository.toUpperCase() ===
|
||||||
`${github.context.repo.owner}/${github.context.repo.repo}`.toUpperCase()
|
`${github.context.repo.owner}/${github.context.repo.repo}`.toUpperCase()
|
||||||
|
|
||||||
// Source branch, source version
|
// Source branch, source version
|
||||||
result.ref = core.getInput('ref')
|
result.ref = core.getInput('ref')
|
||||||
if (!result.ref) {
|
if (!result.ref) {
|
||||||
if (isWorkflowRepository) {
|
if (result.isWorkflowRepository) {
|
||||||
result.ref = github.context.ref
|
result.ref = github.context.ref
|
||||||
result.commit = github.context.sha
|
result.commit = github.context.sha
|
||||||
|
|
||||||
// Some events have an unqualifed ref. For example when a PR is merged (pull_request closed event),
|
// Some events have an unqualifed ref. For example when a PR is merged (pull_request closed event),
|
||||||
// the ref is unqualifed like "main" instead of "refs/heads/main".
|
// the ref is unqualifed like "master" instead of "refs/heads/master".
|
||||||
if (result.commit && result.ref && !result.ref.startsWith('refs/')) {
|
if (result.commit && result.ref && !result.ref.startsWith('refs/')) {
|
||||||
result.ref = `refs/heads/${result.ref}`
|
result.ref = `refs/heads/${result.ref}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!result.ref && !result.commit) {
|
||||||
|
result.ref = 'refs/heads/master'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// SHA?
|
// SHA?
|
||||||
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) {
|
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) {
|
||||||
|
@ -82,25 +87,6 @@ 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}`)
|
||||||
|
|
||||||
// Filter
|
|
||||||
const filter = core.getInput('filter')
|
|
||||||
if (filter) {
|
|
||||||
result.filter = filter
|
|
||||||
}
|
|
||||||
|
|
||||||
core.debug(`filter = ${result.filter}`)
|
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -108,16 +94,6 @@ 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}`)
|
|
||||||
|
|
||||||
// Show fetch progress
|
|
||||||
result.showProgress =
|
|
||||||
(core.getInput('show-progress') || 'true').toUpperCase() === 'TRUE'
|
|
||||||
core.debug(`show progress = ${result.showProgress}`)
|
|
||||||
|
|
||||||
// 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}`)
|
||||||
|
@ -136,7 +112,7 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
||||||
core.debug(`recursive submodules = ${result.nestedSubmodules}`)
|
core.debug(`recursive submodules = ${result.nestedSubmodules}`)
|
||||||
|
|
||||||
// Auth token
|
// Auth token
|
||||||
result.authToken = core.getInput('token', {required: true})
|
result.authToken = core.getInput('token')
|
||||||
|
|
||||||
// SSH
|
// SSH
|
||||||
result.sshKey = core.getInput('ssh-key')
|
result.sshKey = core.getInput('ssh-key')
|
||||||
|
@ -148,16 +124,5 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
||||||
result.persistCredentials =
|
result.persistCredentials =
|
||||||
(core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'
|
(core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'
|
||||||
|
|
||||||
// Workflow organization ID
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import * as stateHelper from './state-helper'
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const sourceSettings = await inputHelper.getInputs()
|
const sourceSettings = inputHelper.getInputs()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Register problem matcher
|
// Register problem matcher
|
||||||
|
@ -24,7 +24,7 @@ async function run(): Promise<void> {
|
||||||
coreCommand.issueCommand('remove-matcher', {owner: 'checkout-git'}, '')
|
coreCommand.issueCommand('remove-matcher', {owner: 'checkout-git'}, '')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(`${(error as any)?.message ?? error}`)
|
core.setFailed(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ async function cleanup(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await gitSourceProvider.cleanup(stateHelper.RepositoryPath)
|
await gitSourceProvider.cleanup(stateHelper.RepositoryPath)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning(`${(error as any)?.message ?? error}`)
|
core.warning(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ import * as yaml from 'js-yaml'
|
||||||
|
|
||||||
function updateUsage(
|
function updateUsage(
|
||||||
actionReference: string,
|
actionReference: string,
|
||||||
actionYamlPath = 'action.yml',
|
actionYamlPath: string = 'action.yml',
|
||||||
readmePath = 'README.md',
|
readmePath: string = 'README.md',
|
||||||
startToken = '<!-- start usage -->',
|
startToken: string = '<!-- start usage -->',
|
||||||
endToken = '<!-- end usage -->'
|
endToken: string = '<!-- end usage -->'
|
||||||
): void {
|
): void {
|
||||||
if (!actionReference) {
|
if (!actionReference) {
|
||||||
throw new Error('Parameter actionReference must not be empty')
|
throw new Error('Parameter actionReference must not be empty')
|
||||||
|
@ -120,7 +120,7 @@ function updateUsage(
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUsage(
|
updateUsage(
|
||||||
'actions/checkout@v4',
|
'actions/checkout@v2',
|
||||||
path.join(__dirname, '..', '..', 'action.yml'),
|
path.join(__dirname, '..', '..', 'action.yml'),
|
||||||
path.join(__dirname, '..', '..', 'README.md')
|
path.join(__dirname, '..', '..', 'README.md')
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
src/misc/licensed-download.sh
|
|
||||||
|
|
||||||
echo 'Running: licensed cached'
|
|
||||||
_temp/licensed-3.6.0/licensed status
|
|
|
@ -1,24 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ ! -f _temp/licensed-3.6.0.done ]; then
|
|
||||||
echo 'Clearing temp'
|
|
||||||
rm -rf _temp/licensed-3.6.0 || true
|
|
||||||
|
|
||||||
echo 'Downloading licensed'
|
|
||||||
mkdir -p _temp/licensed-3.6.0
|
|
||||||
pushd _temp/licensed-3.6.0
|
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
||||||
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
|
|
||||||
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
|
|
||||||
|
|
||||||
echo 'Extracting licenesed'
|
|
||||||
tar -xzf licensed.tar.gz
|
|
||||||
popd
|
|
||||||
touch _temp/licensed-3.6.0.done
|
|
||||||
else
|
|
||||||
echo 'Licensed already downloaded'
|
|
||||||
fi
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
src/misc/licensed-download.sh
|
|
||||||
|
|
||||||
echo 'Running: licensed cached'
|
|
||||||
_temp/licensed-3.6.0/licensed cache
|
|
|
@ -1,9 +1,4 @@
|
||||||
import {IGitCommandManager} from './git-command-manager'
|
import {IGitCommandManager} from './git-command-manager'
|
||||||
import * as core from '@actions/core'
|
|
||||||
import * as github from '@actions/github'
|
|
||||||
import {getServerApiUrl, isGhes} from './url-helper'
|
|
||||||
|
|
||||||
export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
|
|
||||||
|
|
||||||
export interface ICheckoutInfo {
|
export interface ICheckoutInfo {
|
||||||
ref: string
|
ref: string
|
||||||
|
@ -62,16 +57,6 @@ export async function getCheckoutInfo(
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRefSpecForAllHistory(ref: string, commit: string): string[] {
|
|
||||||
const result = ['+refs/heads/*:refs/remotes/origin/*', tagsRefSpec]
|
|
||||||
if (ref && ref.toUpperCase().startsWith('REFS/PULL/')) {
|
|
||||||
const branch = ref.substring('refs/pull/'.length)
|
|
||||||
result.push(`+${commit || ref}:refs/remotes/pull/${branch}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRefSpec(ref: string, commit: string): string[] {
|
export function getRefSpec(ref: string, commit: string): string[] {
|
||||||
if (!ref && !commit) {
|
if (!ref && !commit) {
|
||||||
throw new Error('Args ref and commit cannot both be empty')
|
throw new Error('Args ref and commit cannot both be empty')
|
||||||
|
@ -122,162 +107,3 @@ export function getRefSpec(ref: string, commit: string): string[] {
|
||||||
return [`+${ref}:${ref}`]
|
return [`+${ref}:${ref}`]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests whether the initial fetch created the ref at the expected commit
|
|
||||||
*/
|
|
||||||
export async function testRef(
|
|
||||||
git: IGitCommandManager,
|
|
||||||
ref: string,
|
|
||||||
commit: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
if (!git) {
|
|
||||||
throw new Error('Arg git cannot be empty')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ref && !commit) {
|
|
||||||
throw new Error('Args ref and commit cannot both be empty')
|
|
||||||
}
|
|
||||||
|
|
||||||
// No SHA? Nothing to test
|
|
||||||
if (!commit) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// SHA only?
|
|
||||||
else if (!ref) {
|
|
||||||
return await git.shaExists(commit)
|
|
||||||
}
|
|
||||||
|
|
||||||
const upperRef = ref.toUpperCase()
|
|
||||||
|
|
||||||
// refs/heads/
|
|
||||||
if (upperRef.startsWith('REFS/HEADS/')) {
|
|
||||||
const branch = ref.substring('refs/heads/'.length)
|
|
||||||
return (
|
|
||||||
(await git.branchExists(true, `origin/${branch}`)) &&
|
|
||||||
commit === (await git.revParse(`refs/remotes/origin/${branch}`))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// refs/pull/
|
|
||||||
else if (upperRef.startsWith('REFS/PULL/')) {
|
|
||||||
// Assume matches because fetched using the commit
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// refs/tags/
|
|
||||||
else if (upperRef.startsWith('REFS/TAGS/')) {
|
|
||||||
const tagName = ref.substring('refs/tags/'.length)
|
|
||||||
return (
|
|
||||||
(await git.tagExists(tagName)) && commit === (await git.revParse(ref))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// Unexpected
|
|
||||||
else {
|
|
||||||
core.debug(`Unexpected ref format '${ref}' when testing ref info`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkCommitInfo(
|
|
||||||
token: string,
|
|
||||||
commitInfo: string,
|
|
||||||
repositoryOwner: string,
|
|
||||||
repositoryName: string,
|
|
||||||
ref: string,
|
|
||||||
commit: string,
|
|
||||||
baseUrl?: string
|
|
||||||
): Promise<void> {
|
|
||||||
try {
|
|
||||||
// GHES?
|
|
||||||
if (isGhes(baseUrl)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auth token?
|
|
||||||
if (!token) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public PR synchronize, for workflow repo?
|
|
||||||
if (
|
|
||||||
fromPayload('repository.private') !== false ||
|
|
||||||
github.context.eventName !== 'pull_request' ||
|
|
||||||
fromPayload('action') !== 'synchronize' ||
|
|
||||||
repositoryOwner !== github.context.repo.owner ||
|
|
||||||
repositoryName !== github.context.repo.repo ||
|
|
||||||
ref !== github.context.ref ||
|
|
||||||
!ref.startsWith('refs/pull/') ||
|
|
||||||
commit !== github.context.sha
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Head SHA
|
|
||||||
const expectedHeadSha = fromPayload('after')
|
|
||||||
if (!expectedHeadSha) {
|
|
||||||
core.debug('Unable to determine head sha')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base SHA
|
|
||||||
const expectedBaseSha = fromPayload('pull_request.base.sha')
|
|
||||||
if (!expectedBaseSha) {
|
|
||||||
core.debug('Unable to determine base sha')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expected message?
|
|
||||||
const expectedMessage = `Merge ${expectedHeadSha} into ${expectedBaseSha}`
|
|
||||||
if (commitInfo.indexOf(expectedMessage) >= 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract details from message
|
|
||||||
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/)
|
|
||||||
if (!match) {
|
|
||||||
core.debug('Unexpected message format')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post telemetry
|
|
||||||
const actualHeadSha = match[1]
|
|
||||||
if (actualHeadSha !== expectedHeadSha) {
|
|
||||||
core.debug(
|
|
||||||
`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`
|
|
||||||
)
|
|
||||||
const octokit = github.getOctokit(token, {
|
|
||||||
baseUrl: getServerApiUrl(baseUrl),
|
|
||||||
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload(
|
|
||||||
'number'
|
|
||||||
)};run_id=${
|
|
||||||
process.env['GITHUB_RUN_ID']
|
|
||||||
};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
|
|
||||||
})
|
|
||||||
await octokit.rest.repos.get({
|
|
||||||
owner: repositoryOwner,
|
|
||||||
repo: repositoryName
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
core.debug(
|
|
||||||
`Error when validating commit info: ${(err as any)?.stack ?? err}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromPayload(path: string): any {
|
|
||||||
return select(github.context.payload, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
function select(obj: any, path: string): any {
|
|
||||||
if (!obj) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const i = path.indexOf('.')
|
|
||||||
if (i < 0) {
|
|
||||||
return obj[path]
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = path.substr(0, i)
|
|
||||||
return select(obj[key], path.substr(i + 1))
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export class RetryHelper {
|
||||||
try {
|
try {
|
||||||
return await action()
|
return await action()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
core.info((err as any)?.message)
|
core.info(err.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep
|
// Sleep
|
||||||
|
|
|
@ -1,60 +1,58 @@
|
||||||
import * as core from '@actions/core'
|
import * as coreCommand from '@actions/core/lib/command'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the POST action is running
|
* Indicates whether the POST action is running
|
||||||
*/
|
*/
|
||||||
export const IsPost = !!core.getState('isPost')
|
export const IsPost = !!process.env['STATE_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 = core.getState('repositoryPath')
|
export const 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 = core.getState('sshKeyPath')
|
export const SshKeyPath = (process.env['STATE_sshKeyPath'] as string) || ''
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 = core.getState('sshKnownHostsPath')
|
export const 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) {
|
||||||
core.saveState('repositoryPath', repositoryPath)
|
coreCommand.issueCommand(
|
||||||
|
'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) {
|
||||||
core.saveState('sshKeyPath', sshKeyPath)
|
coreCommand.issueCommand('save-state', {name: '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) {
|
||||||
core.saveState('sshKnownHostsPath', sshKnownHostsPath)
|
coreCommand.issueCommand(
|
||||||
}
|
'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) {
|
||||||
core.saveState('isPost', 'true')
|
coreCommand.issueCommand('save-state', {name: 'isPost'}, 'true')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
import * as assert from 'assert'
|
|
||||||
import {URL} from 'url'
|
|
||||||
import {IGitSourceSettings} from './git-source-settings'
|
|
||||||
|
|
||||||
export function getFetchUrl(settings: IGitSourceSettings): string {
|
|
||||||
assert.ok(
|
|
||||||
settings.repositoryOwner,
|
|
||||||
'settings.repositoryOwner must be defined'
|
|
||||||
)
|
|
||||||
assert.ok(settings.repositoryName, 'settings.repositoryName must be defined')
|
|
||||||
const serviceUrl = getServerUrl(settings.githubServerUrl)
|
|
||||||
const encodedOwner = encodeURIComponent(settings.repositoryOwner)
|
|
||||||
const encodedName = encodeURIComponent(settings.repositoryName)
|
|
||||||
if (settings.sshKey) {
|
|
||||||
return `git@${serviceUrl.hostname}:${encodedOwner}/${encodedName}.git`
|
|
||||||
}
|
|
||||||
|
|
||||||
// "origin" is SCHEME://HOSTNAME[:PORT]
|
|
||||||
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getServerUrl(url?: string): URL {
|
|
||||||
let urlValue =
|
|
||||||
url && url.trim().length > 0
|
|
||||||
? url
|
|
||||||
: process.env['GITHUB_SERVER_URL'] || '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'
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
import * as core from '@actions/core'
|
|
||||||
import * as fs from 'fs'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the organization ID of the running workflow or undefined if the value cannot be loaded from the GITHUB_EVENT_PATH
|
|
||||||
*/
|
|
||||||
export async function getOrganizationId(): Promise<number | undefined> {
|
|
||||||
try {
|
|
||||||
const eventPath = process.env.GITHUB_EVENT_PATH
|
|
||||||
if (!eventPath) {
|
|
||||||
core.debug(`GITHUB_EVENT_PATH is not defined`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = await fs.promises.readFile(eventPath, {encoding: 'utf8'})
|
|
||||||
const event = JSON.parse(content)
|
|
||||||
const id = event?.repository?.owner?.id
|
|
||||||
if (typeof id !== 'number') {
|
|
||||||
core.debug('Repository owner ID not found within GITHUB event info')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return id as number
|
|
||||||
} catch (err) {
|
|
||||||
core.debug(
|
|
||||||
`Unable to load organization ID from GITHUB_EVENT_PATH: ${(err as any)
|
|
||||||
.message || err}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,8 +10,7 @@
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true
|
||||||
"skipLibCheck": true
|
|
||||||
},
|
},
|
||||||
"exclude": ["__test__", "lib", "node_modules"]
|
"exclude": ["__test__", "lib", "node_modules"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue