├── CODEOWNERS ├── .prettierignore ├── .eslintignore ├── .gitattributes ├── .gitignore ├── src ├── regexp-helper.ts ├── misc │ ├── licensed-check.sh │ ├── licensed-generate.sh │ ├── licensed-download.sh │ └── generate-docs.ts ├── workflow-context-helper.ts ├── main.ts ├── url-helper.ts ├── retry-helper.ts ├── fs-helper.ts ├── state-helper.ts ├── git-version.ts ├── git-source-settings.ts ├── git-directory-helper.ts ├── github-api-helper.ts ├── input-helper.ts ├── ref-helper.ts └── git-source-provider.ts ├── __test__ ├── override-git-version.cmd ├── override-git-version.sh ├── modify-work-tree.sh ├── verify-lfs.sh ├── verify-side-by-side.sh ├── verify-submodules-false.sh ├── verify-clean.sh ├── verify-fetch-filter.sh ├── verify-basic.sh ├── verify-no-unstaged-changes.sh ├── verify-submodules-true.sh ├── verify-submodules-recursive.sh ├── verify-sparse-checkout-non-cone-mode.sh ├── verify-sparse-checkout.sh ├── git-version.test.ts ├── retry-helper.test.ts ├── input-helper.test.ts ├── ref-helper.test.ts └── git-command-manager.test.ts ├── .licensed.yml ├── .prettierrc.json ├── jest.config.js ├── .github └── workflows │ ├── licensed.yml │ ├── update-main-version.yml │ ├── check-dist.yml │ ├── codeql-analysis.yml │ └── test.yml ├── dist └── problem-matcher.json ├── tsconfig.json ├── .licenses └── npm │ ├── semver.dep.yml │ ├── wrappy.dep.yml │ ├── once.dep.yml │ ├── universal-user-agent.dep.yml │ ├── deprecation.dep.yml │ ├── @actions │ ├── exec.dep.yml │ ├── io.dep.yml │ ├── core.dep.yml │ ├── github.dep.yml │ ├── tool-cache.dep.yml │ └── http-client.dep.yml │ ├── uuid-8.3.2.dep.yml │ ├── @octokit │ ├── openapi-types.dep.yml │ ├── types.dep.yml │ ├── plugin-paginate-rest.dep.yml │ ├── plugin-rest-endpoint-methods.dep.yml │ ├── core.dep.yml │ ├── graphql.dep.yml │ ├── request-error.dep.yml │ ├── endpoint.dep.yml │ ├── auth-token.dep.yml │ └── request.dep.yml │ ├── tr46.dep.yml │ ├── function-bind.dep.yml │ ├── has.dep.yml │ ├── call-bind.dep.yml │ ├── has-symbols.dep.yml │ ├── side-channel.dep.yml │ ├── get-intrinsic.dep.yml │ ├── whatwg-url.dep.yml │ ├── underscore.dep.yml │ ├── tunnel.dep.yml │ ├── webidl-conversions.dep.yml │ ├── uuid-3.3.3.dep.yml │ ├── is-plain-object.dep.yml │ ├── qs.dep.yml │ ├── object-inspect.dep.yml │ ├── node-fetch.dep.yml │ └── before-after-hook.dep.yml ├── LICENSE ├── CONTRIBUTING.md ├── package.json ├── .eslintrc.json ├── action.yml ├── CHANGELOG.md ├── README.md └── adrs └── 0153-checkout-v2.md /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @actions/actions-launch 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .licenses/** -diff linguist-generated=true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __test__/_temp 2 | _temp/ 3 | lib/ 4 | node_modules/ 5 | .vscode/ -------------------------------------------------------------------------------- /src/regexp-helper.ts: -------------------------------------------------------------------------------- 1 | export function escape(value: string): string { 2 | return value.replace(/[^a-zA-Z0-9_]/g, x => { 3 | return `\\${x}` 4 | }) 5 | } 6 | -------------------------------------------------------------------------------- /src/misc/licensed-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | src/misc/licensed-download.sh 6 | 7 | echo 'Running: licensed cached' 8 | _temp/licensed-3.6.0/licensed status -------------------------------------------------------------------------------- /src/misc/licensed-generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | src/misc/licensed-download.sh 6 | 7 | echo 'Running: licensed cached' 8 | _temp/licensed-3.6.0/licensed cache -------------------------------------------------------------------------------- /__test__/override-git-version.cmd: -------------------------------------------------------------------------------- 1 | 2 | mkdir override-git-version 3 | cd override-git-version 4 | echo @echo override git version 1.2.3 > git.cmd 5 | echo "%CD%" >> $GITHUB_PATH 6 | cd .. 7 | -------------------------------------------------------------------------------- /.licensed.yml: -------------------------------------------------------------------------------- 1 | sources: 2 | npm: true 3 | 4 | allowed: 5 | - apache-2.0 6 | - bsd-2-clause 7 | - bsd-3-clause 8 | - isc 9 | - mit 10 | - cc0-1.0 11 | - unlicense 12 | 13 | reviewed: 14 | npm: -------------------------------------------------------------------------------- /__test__/override-git-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir override-git-version 4 | cd override-git-version 5 | echo "#!/bin/sh" > git 6 | echo "echo override git version 1.2.3" >> git 7 | chmod +x git 8 | echo "$(pwd)" >> $GITHUB_PATH 9 | cd .. 10 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "arrowParens": "avoid", 10 | "parser": "typescript" 11 | } -------------------------------------------------------------------------------- /__test__/modify-work-tree.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "./basic/basic-file.txt" ]; then 4 | echo "Expected basic file does not exist" 5 | exit 1 6 | fi 7 | 8 | echo hello >> ./basic/basic-file.txt 9 | echo hello >> ./basic/new-file.txt 10 | git -C ./basic status -------------------------------------------------------------------------------- /__test__/verify-lfs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "./lfs/regular-file.txt" ]; then 4 | echo "Expected regular file does not exist" 5 | exit 1 6 | fi 7 | 8 | if [ ! -f "./lfs/lfs-file.bin" ]; then 9 | echo "Expected lfs file does not exist" 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | moduleFileExtensions: ['js', 'ts'], 4 | testEnvironment: 'node', 5 | testMatch: ['**/*.test.ts'], 6 | testRunner: 'jest-circus/runner', 7 | transform: { 8 | '^.+\\.ts$': 'ts-jest' 9 | }, 10 | verbose: true 11 | } -------------------------------------------------------------------------------- /.github/workflows/licensed.yml: -------------------------------------------------------------------------------- 1 | name: Licensed 2 | 3 | on: 4 | push: {branches: main} 5 | pull_request: {branches: main} 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | name: Check licenses 11 | steps: 12 | - uses: actions/checkout@v3 13 | - run: npm ci 14 | - run: npm run licensed-check -------------------------------------------------------------------------------- /dist/problem-matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "checkout-git", 5 | "pattern": [ 6 | { 7 | "regexp": "^(fatal|error): (.*)$", 8 | "message": 2 9 | } 10 | ] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /__test__/verify-side-by-side.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "./side-by-side-1/side-by-side-test-file-1.txt" ]; then 4 | echo "Expected file 1 does not exist" 5 | exit 1 6 | fi 7 | 8 | if [ ! -f "./side-by-side-2/side-by-side-test-file-2.txt" ]; then 9 | echo "Expected file 2 does not exist" 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /__test__/verify-submodules-false.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "./submodules-false/regular-file.txt" ]; then 4 | echo "Expected regular file does not exist" 5 | exit 1 6 | fi 7 | 8 | if [ -f "./submodules-false/submodule-level-1/submodule-file.txt" ]; then 9 | echo "Unexpected submodule file exists" 10 | exit 1 11 | fi -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es6" 7 | ], 8 | "outDir": "./lib", 9 | "rootDir": "./src", 10 | "declaration": true, 11 | "strict": true, 12 | "noImplicitAny": false, 13 | "esModuleInterop": true, 14 | "skipLibCheck": true 15 | }, 16 | "exclude": ["__test__", "lib", "node_modules"] 17 | } 18 | -------------------------------------------------------------------------------- /__test__/verify-clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "$(git -C ./basic status --porcelain)" != "" ]]; then 4 | echo ---------------------------------------- 5 | echo git status 6 | echo ---------------------------------------- 7 | git status 8 | echo ---------------------------------------- 9 | echo git diff 10 | echo ---------------------------------------- 11 | git diff 12 | exit 1 13 | fi 14 | -------------------------------------------------------------------------------- /__test__/verify-fetch-filter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Verify .git folder 4 | if [ ! -d "./fetch-filter/.git" ]; then 5 | echo "Expected ./fetch-filter/.git folder to exist" 6 | exit 1 7 | fi 8 | 9 | # Verify .git/config contains partialclonefilter 10 | 11 | CLONE_FILTER=$(git -C fetch-filter config --local --get remote.origin.partialclonefilter) 12 | 13 | if [ "$CLONE_FILTER" != "blob:none" ]; then 14 | echo "Expected ./fetch-filter/.git/config to have 'remote.origin.partialclonefilter' set to 'blob:none'" 15 | exit 1 16 | fi 17 | -------------------------------------------------------------------------------- /__test__/verify-basic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -f "./basic/basic-file.txt" ]; then 4 | echo "Expected basic file does not exist" 5 | exit 1 6 | fi 7 | 8 | if [ "$1" = "--archive" ]; then 9 | # Verify no .git folder 10 | if [ -d "./basic/.git" ]; then 11 | echo "Did not expect ./basic/.git folder to exist" 12 | exit 1 13 | fi 14 | else 15 | # Verify .git folder 16 | if [ ! -d "./basic/.git" ]; then 17 | echo "Expected ./basic/.git folder to exist" 18 | exit 1 19 | fi 20 | 21 | # Verify auth token 22 | cd basic 23 | git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main 24 | fi 25 | -------------------------------------------------------------------------------- /__test__/verify-no-unstaged-changes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "$(git status --porcelain)" != "" ]]; then 4 | echo ---------------------------------------- 5 | echo git status 6 | echo ---------------------------------------- 7 | git status 8 | echo ---------------------------------------- 9 | echo git diff 10 | echo ---------------------------------------- 11 | git diff 12 | echo ---------------------------------------- 13 | echo Troubleshooting 14 | echo ---------------------------------------- 15 | echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run format && npm run build" 16 | exit 1 17 | fi 18 | -------------------------------------------------------------------------------- /src/misc/licensed-download.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ ! -f _temp/licensed-3.6.0.done ]; then 6 | echo 'Clearing temp' 7 | rm -rf _temp/licensed-3.6.0 || true 8 | 9 | echo 'Downloading licensed' 10 | mkdir -p _temp/licensed-3.6.0 11 | pushd _temp/licensed-3.6.0 12 | if [[ "$OSTYPE" == "darwin"* ]]; then 13 | curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.6.0/licensed-3.6.0-darwin-x64.tar.gz 14 | else 15 | curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.6.0/licensed-3.6.0-linux-x64.tar.gz 16 | fi 17 | 18 | echo 'Extracting licenesed' 19 | tar -xzf licensed.tar.gz 20 | popd 21 | touch _temp/licensed-3.6.0.done 22 | else 23 | echo 'Licensed already downloaded' 24 | fi 25 | -------------------------------------------------------------------------------- /__test__/verify-submodules-true.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "./submodules-true/regular-file.txt" ]; then 4 | echo "Expected regular file does not exist" 5 | exit 1 6 | fi 7 | 8 | if [ ! -f "./submodules-true/submodule-level-1/submodule-file.txt" ]; then 9 | echo "Expected submodule file does not exist" 10 | exit 1 11 | fi 12 | 13 | if [ -f "./submodules-true/submodule-level-1/submodule-level-2/nested-submodule-file.txt" ]; then 14 | echo "Unexpected nested submodule file exists" 15 | exit 1 16 | fi 17 | 18 | echo "Testing persisted credential" 19 | pushd ./submodules-true/submodule-level-1 20 | git config --local --name-only --get-regexp http.+extraheader && git fetch 21 | if [ "$?" != "0" ]; then 22 | echo "Failed to validate persisted credential" 23 | popd 24 | exit 1 25 | fi 26 | popd 27 | -------------------------------------------------------------------------------- /__test__/verify-submodules-recursive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "./submodules-recursive/regular-file.txt" ]; then 4 | echo "Expected regular file does not exist" 5 | exit 1 6 | fi 7 | 8 | if [ ! -f "./submodules-recursive/submodule-level-1/submodule-file.txt" ]; then 9 | echo "Expected submodule file does not exist" 10 | exit 1 11 | fi 12 | 13 | if [ ! -f "./submodules-recursive/submodule-level-1/submodule-level-2/nested-submodule-file.txt" ]; then 14 | echo "Expected nested submodule file does not exists" 15 | exit 1 16 | fi 17 | 18 | echo "Testing persisted credential" 19 | pushd ./submodules-recursive/submodule-level-1/submodule-level-2 20 | git config --local --name-only --get-regexp http.+extraheader && git fetch 21 | if [ "$?" != "0" ]; then 22 | echo "Failed to validate persisted credential" 23 | popd 24 | exit 1 25 | fi 26 | popd 27 | -------------------------------------------------------------------------------- /src/workflow-context-helper.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as fs from 'fs' 3 | 4 | /** 5 | * Gets the organization ID of the running workflow or undefined if the value cannot be loaded from the GITHUB_EVENT_PATH 6 | */ 7 | export async function getOrganizationId(): Promise { 8 | try { 9 | const eventPath = process.env.GITHUB_EVENT_PATH 10 | if (!eventPath) { 11 | core.debug(`GITHUB_EVENT_PATH is not defined`) 12 | return 13 | } 14 | 15 | const content = await fs.promises.readFile(eventPath, {encoding: 'utf8'}) 16 | const event = JSON.parse(content) 17 | const id = event?.repository?.owner?.id 18 | if (typeof id !== 'number') { 19 | core.debug('Repository owner ID not found within GITHUB event info') 20 | return 21 | } 22 | 23 | return id as number 24 | } catch (err) { 25 | core.debug( 26 | `Unable to load organization ID from GITHUB_EVENT_PATH: ${(err as any) 27 | .message || err}` 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/update-main-version.yml: -------------------------------------------------------------------------------- 1 | name: Update Main Version 2 | run-name: Move ${{ github.event.inputs.major_version }} to ${{ github.event.inputs.target }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | target: 8 | description: The tag or reference to use 9 | required: true 10 | major_version: 11 | type: choice 12 | description: The major version to update 13 | options: 14 | - v4 15 | - v3 16 | - v2 17 | 18 | jobs: 19 | tag: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | with: 24 | fetch-depth: 0 25 | - name: Git config 26 | run: | 27 | git config user.name github-actions 28 | git config user.email github-actions@github.com 29 | - name: Tag new target 30 | run: git tag -f ${{ github.event.inputs.major_version }} ${{ github.event.inputs.target }} 31 | - name: Push new tag 32 | run: git push origin ${{ github.event.inputs.major_version }} --force 33 | -------------------------------------------------------------------------------- /.licenses/npm/semver.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: semver 3 | version: 6.3.0 4 | type: npm 5 | summary: The semantic version parser used by npm. 6 | homepage: 7 | license: isc 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The ISC License 12 | 13 | Copyright (c) Isaac Z. Schlueter and Contributors 14 | 15 | Permission to use, copy, modify, and/or distribute this software for any 16 | purpose with or without fee is hereby granted, provided that the above 17 | copyright notice and this permission notice appear in all copies. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 20 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 21 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 22 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 23 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 24 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 25 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26 | notices: [] 27 | -------------------------------------------------------------------------------- /.licenses/npm/wrappy.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: wrappy 3 | version: 1.0.2 4 | type: npm 5 | summary: Callback wrapping utility 6 | homepage: https://github.com/npm/wrappy 7 | license: isc 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The ISC License 12 | 13 | Copyright (c) Isaac Z. Schlueter and Contributors 14 | 15 | Permission to use, copy, modify, and/or distribute this software for any 16 | purpose with or without fee is hereby granted, provided that the above 17 | copyright notice and this permission notice appear in all copies. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 20 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 21 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 22 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 23 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 24 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 25 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26 | notices: [] 27 | -------------------------------------------------------------------------------- /.licenses/npm/once.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: once 3 | version: 1.4.0 4 | type: npm 5 | summary: Run a function exactly one time 6 | homepage: https://github.com/isaacs/once#readme 7 | license: isc 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The ISC License 12 | 13 | Copyright (c) Isaac Z. Schlueter and Contributors 14 | 15 | Permission to use, copy, modify, and/or distribute this software for any 16 | purpose with or without fee is hereby granted, provided that the above 17 | copyright notice and this permission notice appear in all copies. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 20 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 21 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 22 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 23 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 24 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 25 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26 | notices: [] 27 | -------------------------------------------------------------------------------- /.licenses/npm/universal-user-agent.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: universal-user-agent 3 | version: 6.0.0 4 | type: npm 5 | summary: Get a user agent string in both browser and node 6 | homepage: 7 | license: isc 8 | licenses: 9 | - sources: LICENSE.md 10 | text: | 11 | # [ISC License](https://spdx.org/licenses/ISC) 12 | 13 | Copyright (c) 2018, Gregor Martynus (https://github.com/gr2m) 14 | 15 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | - sources: README.md 19 | text: "[ISC](LICENSE.md)" 20 | notices: [] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2018 GitHub, Inc. and contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /.licenses/npm/deprecation.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: deprecation 3 | version: 2.3.1 4 | type: npm 5 | summary: Log a deprecation message with stack 6 | homepage: https://github.com/gr2m/deprecation#readme 7 | license: isc 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The ISC License 12 | 13 | Copyright (c) Gregor Martynus and contributors 14 | 15 | Permission to use, copy, modify, and/or distribute this software for any 16 | purpose with or without fee is hereby granted, provided that the above 17 | copyright notice and this permission notice appear in all copies. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 20 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 21 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 22 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 23 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 24 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 25 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26 | - sources: README.md 27 | text: "[ISC](LICENSE)" 28 | notices: [] 29 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as coreCommand from '@actions/core/lib/command' 3 | import * as gitSourceProvider from './git-source-provider' 4 | import * as inputHelper from './input-helper' 5 | import * as path from 'path' 6 | import * as stateHelper from './state-helper' 7 | 8 | async function run(): Promise { 9 | try { 10 | const sourceSettings = await inputHelper.getInputs() 11 | 12 | try { 13 | // Register problem matcher 14 | coreCommand.issueCommand( 15 | 'add-matcher', 16 | {}, 17 | path.join(__dirname, 'problem-matcher.json') 18 | ) 19 | 20 | // Get sources 21 | await gitSourceProvider.getSource(sourceSettings) 22 | } finally { 23 | // Unregister problem matcher 24 | coreCommand.issueCommand('remove-matcher', {owner: 'checkout-git'}, '') 25 | } 26 | } catch (error) { 27 | core.setFailed(`${(error as any)?.message ?? error}`) 28 | } 29 | } 30 | 31 | async function cleanup(): Promise { 32 | try { 33 | await gitSourceProvider.cleanup(stateHelper.RepositoryPath) 34 | } catch (error) { 35 | core.warning(`${(error as any)?.message ?? error}`) 36 | } 37 | } 38 | 39 | // Main 40 | if (!stateHelper.IsPost) { 41 | run() 42 | } 43 | // Post 44 | else { 45 | cleanup() 46 | } 47 | -------------------------------------------------------------------------------- /.licenses/npm/@actions/exec.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@actions/exec" 3 | version: 1.0.1 4 | type: npm 5 | summary: Actions exec lib 6 | homepage: https://github.com/actions/toolkit/tree/master/packages/exec 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.md 10 | text: |- 11 | Copyright 2019 GitHub 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | notices: [] 19 | -------------------------------------------------------------------------------- /.licenses/npm/@actions/io.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@actions/io" 3 | version: 1.1.3 4 | type: npm 5 | summary: Actions io lib 6 | homepage: https://github.com/actions/toolkit/tree/main/packages/io 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.md 10 | text: |- 11 | The MIT License (MIT) 12 | 13 | Copyright 2019 GitHub 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | notices: [] 21 | -------------------------------------------------------------------------------- /.licenses/npm/@actions/core.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@actions/core" 3 | version: 1.10.0 4 | type: npm 5 | summary: Actions core lib 6 | homepage: https://github.com/actions/toolkit/tree/main/packages/core 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.md 10 | text: |- 11 | The MIT License (MIT) 12 | 13 | Copyright 2019 GitHub 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | notices: [] 21 | -------------------------------------------------------------------------------- /.licenses/npm/@actions/github.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@actions/github" 3 | version: 5.1.1 4 | type: npm 5 | summary: Actions github lib 6 | homepage: https://github.com/actions/toolkit/tree/main/packages/github 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.md 10 | text: |- 11 | The MIT License (MIT) 12 | 13 | Copyright 2019 GitHub 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | notices: [] 21 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Submitting a pull request 4 | 5 | 1. Fork and clone the repository 6 | 1. Configure and install the dependencies: `npm install` 7 | 1. Create a new branch: `git checkout -b my-branch-name` 8 | 1. Make your change, add tests, and make sure the tests still pass: `npm run test` 9 | 1. Make sure your code is correctly formatted: `npm run format` 10 | 1. Update `dist/index.js` using `npm run build`. This creates a single javascript file that is used as an entrypoint for the action 11 | 1. Push to your fork and submit a pull request 12 | 1. Pat yourself on the back and wait for your pull request to be reviewed and merged 13 | 14 | Here are a few things you can do that will increase the likelihood of your pull request being accepted: 15 | 16 | - Write tests. 17 | - 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. 18 | 19 | ## Resources 20 | 21 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 22 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) 23 | - [GitHub Help](https://help.github.com) 24 | - [Writing good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 25 | 26 | Thanks! :heart: :heart: :heart: 27 | 28 | GitHub Actions Team :octocat: 29 | -------------------------------------------------------------------------------- /.licenses/npm/uuid-8.3.2.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: uuid 3 | version: 8.3.2 4 | type: npm 5 | summary: RFC4122 (v1, v4, and v5) UUIDs 6 | homepage: https://github.com/uuidjs/uuid#readme 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.md 10 | text: | 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2010-2020 Robert Kieffer and other contributors 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | notices: [] 21 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/openapi-types.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/openapi-types" 3 | version: 12.11.0 4 | type: npm 5 | summary: Generated TypeScript definitions based on GitHub's OpenAPI spec for api.github.com 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: |- 11 | Copyright 2020 Gregor Martynus 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | - sources: README.md 19 | text: "[MIT](LICENSE)" 20 | notices: [] 21 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/types.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/types" 3 | version: 6.41.0 4 | type: npm 5 | summary: Shared TypeScript definitions for Octokit projects 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | MIT License Copyright (c) 2019 Octokit contributors 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | - sources: README.md 19 | text: "[MIT](LICENSE)" 20 | notices: [] 21 | -------------------------------------------------------------------------------- /.licenses/npm/tr46.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: tr46 3 | version: 0.0.3 4 | type: npm 5 | summary: An implementation of the Unicode TR46 spec 6 | homepage: https://github.com/Sebmaster/tr46.js#readme 7 | license: mit 8 | licenses: 9 | - sources: Auto-generated MIT license text 10 | text: | 11 | MIT License 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | notices: [] 31 | -------------------------------------------------------------------------------- /__test__/verify-sparse-checkout-non-cone-mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Verify .git folder 4 | if [ ! -d "./sparse-checkout-non-cone-mode/.git" ]; then 5 | echo "Expected ./sparse-checkout-non-cone-mode/.git folder to exist" 6 | exit 1 7 | fi 8 | 9 | # Verify sparse-checkout (non-cone-mode) 10 | cd sparse-checkout-non-cone-mode 11 | 12 | ENABLED=$(git config --local --get-all core.sparseCheckout) 13 | 14 | if [ "$?" != "0" ]; then 15 | echo "Failed to verify that sparse-checkout is enabled" 16 | exit 1 17 | fi 18 | 19 | # Check that sparse-checkout is enabled 20 | if [ "$ENABLED" != "true" ]; then 21 | echo "Expected sparse-checkout to be enabled (is: $ENABLED)" 22 | exit 1 23 | fi 24 | 25 | SPARSE_CHECKOUT_FILE=$(git rev-parse --git-path info/sparse-checkout) 26 | 27 | if [ "$?" != "0" ]; then 28 | echo "Failed to validate sparse-checkout" 29 | exit 1 30 | fi 31 | 32 | # Check that sparse-checkout list is not empty 33 | if [ ! -f "$SPARSE_CHECKOUT_FILE" ]; then 34 | echo "Expected sparse-checkout file to exist" 35 | exit 1 36 | fi 37 | 38 | # Check that all folders from sparse-checkout exists 39 | for pattern in $(cat "$SPARSE_CHECKOUT_FILE") 40 | do 41 | if [ ! -d "${pattern#/}" ]; then 42 | echo "Expected directory '${pattern#/}' to exist" 43 | exit 1 44 | fi 45 | done 46 | 47 | # Verify that the root directory is not checked out 48 | if [ -f README.md ]; then 49 | echo "Expected top-level files not to exist" 50 | exit 1 51 | fi -------------------------------------------------------------------------------- /.licenses/npm/function-bind.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: function-bind 3 | version: 1.1.1 4 | type: npm 5 | summary: Implementation of Function.prototype.bind 6 | homepage: https://github.com/Raynos/function-bind 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: |+ 11 | Copyright (c) 2013 Raynos. 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | 31 | notices: [] 32 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/plugin-paginate-rest.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/plugin-paginate-rest" 3 | version: 2.21.3 4 | type: npm 5 | summary: Octokit plugin to paginate REST API endpoint responses 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | MIT License Copyright (c) 2019 Octokit contributors 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | - sources: README.md 19 | text: "[MIT](LICENSE)" 20 | notices: [] 21 | -------------------------------------------------------------------------------- /.licenses/npm/has.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: has 3 | version: 1.0.3 4 | type: npm 5 | summary: Object.prototype.hasOwnProperty.call shortcut 6 | homepage: https://github.com/tarruda/has 7 | license: mit 8 | licenses: 9 | - sources: LICENSE-MIT 10 | text: | 11 | Copyright (c) 2013 Thiago de Arruda 12 | 13 | Permission is hereby granted, free of charge, to any person 14 | obtaining a copy of this software and associated documentation 15 | files (the "Software"), to deal in the Software without 16 | restriction, including without limitation the rights to use, 17 | copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the 19 | Software is furnished to do so, subject to the following 20 | conditions: 21 | 22 | The above copyright notice and this permission notice shall be 23 | included in all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | OTHER DEALINGS IN THE SOFTWARE. 33 | notices: [] 34 | -------------------------------------------------------------------------------- /.licenses/npm/call-bind.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: call-bind 3 | version: 1.0.2 4 | type: npm 5 | summary: Robustly `.call.bind()` a function 6 | homepage: https://github.com/ljharb/call-bind#readme 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | MIT License 12 | 13 | Copyright (c) 2020 Jordan Harband 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | notices: [] 33 | -------------------------------------------------------------------------------- /.licenses/npm/@actions/tool-cache.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@actions/tool-cache" 3 | version: 1.1.2 4 | type: npm 5 | summary: Actions tool-cache lib 6 | homepage: https://github.com/actions/toolkit/tree/master/packages/exec 7 | license: mit 8 | licenses: 9 | - sources: Auto-generated MIT license text 10 | text: | 11 | MIT License 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | notices: [] 31 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/plugin-rest-endpoint-methods.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/plugin-rest-endpoint-methods" 3 | version: 5.16.2 4 | type: npm 5 | summary: Octokit plugin adding one method for all of api.github.com REST API endpoints 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | MIT License Copyright (c) 2019 Octokit contributors 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 18 | - sources: README.md 19 | text: "[MIT](LICENSE)" 20 | notices: [] 21 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/core.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/core" 3 | version: 3.6.0 4 | type: npm 5 | summary: Extendable client for GitHub's REST & GraphQL APIs 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The MIT License 12 | 13 | Copyright (c) 2019 Octokit contributors 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | - sources: README.md 33 | text: "[MIT](LICENSE)" 34 | notices: [] 35 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/graphql.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/graphql" 3 | version: 4.8.0 4 | type: npm 5 | summary: GitHub GraphQL API client for browsers and Node 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The MIT License 12 | 13 | Copyright (c) 2018 Octokit contributors 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | - sources: README.md 33 | text: "[MIT](LICENSE)" 34 | notices: [] 35 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/request-error.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/request-error" 3 | version: 2.1.0 4 | type: npm 5 | summary: Error class for Octokit request errors 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The MIT License 12 | 13 | Copyright (c) 2019 Octokit contributors 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | - sources: README.md 33 | text: "[MIT](LICENSE)" 34 | notices: [] 35 | -------------------------------------------------------------------------------- /.licenses/npm/has-symbols.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: has-symbols 3 | version: 1.0.2 4 | type: npm 5 | summary: Determine if the JS environment has Symbol support. Supports spec, or shams. 6 | homepage: https://github.com/inspect-js/has-symbols#readme 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | MIT License 12 | 13 | Copyright (c) 2016 Jordan Harband 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | notices: [] 33 | -------------------------------------------------------------------------------- /.licenses/npm/side-channel.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: side-channel 3 | version: 1.0.4 4 | type: npm 5 | summary: Store information about any JS value in a side channel. Uses WeakMap if available. 6 | homepage: https://github.com/ljharb/side-channel#readme 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | MIT License 12 | 13 | Copyright (c) 2019 Jordan Harband 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | notices: [] 33 | -------------------------------------------------------------------------------- /src/url-helper.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert' 2 | import {URL} from 'url' 3 | import {IGitSourceSettings} from './git-source-settings' 4 | 5 | export function getFetchUrl(settings: IGitSourceSettings): string { 6 | assert.ok( 7 | settings.repositoryOwner, 8 | 'settings.repositoryOwner must be defined' 9 | ) 10 | assert.ok(settings.repositoryName, 'settings.repositoryName must be defined') 11 | const serviceUrl = getServerUrl(settings.githubServerUrl) 12 | const encodedOwner = encodeURIComponent(settings.repositoryOwner) 13 | const encodedName = encodeURIComponent(settings.repositoryName) 14 | if (settings.sshKey) { 15 | return `git@${serviceUrl.hostname}:${encodedOwner}/${encodedName}.git` 16 | } 17 | 18 | // "origin" is SCHEME://HOSTNAME[:PORT] 19 | return `${serviceUrl.origin}/${encodedOwner}/${encodedName}` 20 | } 21 | 22 | export function getServerUrl(url?: string): URL { 23 | let urlValue = 24 | url && url.trim().length > 0 25 | ? url 26 | : process.env['GITHUB_SERVER_URL'] || 'https://github.com' 27 | return new URL(urlValue) 28 | } 29 | 30 | export function getServerApiUrl(url?: string): string { 31 | let apiUrl = 'https://api.github.com' 32 | 33 | if (isGhes(url)) { 34 | const serverUrl = getServerUrl(url) 35 | apiUrl = new URL(`${serverUrl.origin}/api/v3`).toString() 36 | } 37 | 38 | return apiUrl 39 | } 40 | 41 | export function isGhes(url?: string): boolean { 42 | const ghUrl = getServerUrl(url) 43 | 44 | return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM' 45 | } 46 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/endpoint.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/endpoint" 3 | version: 6.0.12 4 | type: npm 5 | summary: Turns REST API endpoints into generic request options 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The MIT License 12 | 13 | Copyright (c) 2018 Octokit contributors 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | - sources: README.md 33 | text: "[MIT](LICENSE)" 34 | notices: [] 35 | -------------------------------------------------------------------------------- /.licenses/npm/get-intrinsic.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: get-intrinsic 3 | version: 1.1.1 4 | type: npm 5 | summary: Get and robustly cache all JS language-level intrinsics at first require 6 | time 7 | homepage: https://github.com/ljharb/get-intrinsic#readme 8 | license: mit 9 | licenses: 10 | - sources: LICENSE 11 | text: | 12 | MIT License 13 | 14 | Copyright (c) 2020 Jordan Harband 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy 17 | of this software and associated documentation files (the "Software"), to deal 18 | in the Software without restriction, including without limitation the rights 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | copies of the Software, and to permit persons to whom the Software is 21 | furnished to do so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | notices: [] 34 | -------------------------------------------------------------------------------- /.licenses/npm/whatwg-url.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: whatwg-url 3 | version: 5.0.0 4 | type: npm 5 | summary: An implementation of the WHATWG URL Standard's URL API and parsing machinery 6 | homepage: https://github.com/jsdom/whatwg-url#readme 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.txt 10 | text: | 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2015–2016 Sebastian Mayr 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | notices: [] 33 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/auth-token.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/auth-token" 3 | version: 2.5.0 4 | type: npm 5 | summary: GitHub API token authentication for browsers and Node.js 6 | homepage: 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The MIT License 12 | 13 | Copyright (c) 2019 Octokit contributors 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | - sources: README.md 33 | text: "[MIT](LICENSE)" 34 | notices: [] 35 | -------------------------------------------------------------------------------- /.licenses/npm/@actions/http-client.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@actions/http-client" 3 | version: 2.1.0 4 | type: npm 5 | summary: Actions Http Client 6 | homepage: https://github.com/actions/toolkit/tree/main/packages/http-client 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | Actions Http Client for Node.js 12 | 13 | Copyright (c) GitHub, Inc. 14 | 15 | All rights reserved. 16 | 17 | MIT License 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 20 | associated documentation files (the "Software"), to deal in the Software without restriction, 21 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 22 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 23 | subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 28 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 29 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 31 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | notices: [] 33 | -------------------------------------------------------------------------------- /.licenses/npm/underscore.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: underscore 3 | version: 1.13.1 4 | type: npm 5 | summary: JavaScript's functional programming helper library. 6 | homepage: https://underscorejs.org 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | Copyright (c) 2009-2021 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors 12 | 13 | Permission is hereby granted, free of charge, to any person 14 | obtaining a copy of this software and associated documentation 15 | files (the "Software"), to deal in the Software without 16 | restriction, including without limitation the rights to use, 17 | copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the 19 | Software is furnished to do so, subject to the following 20 | conditions: 21 | 22 | The above copyright notice and this permission notice shall be 23 | included in all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 27 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 29 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 30 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 31 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | OTHER DEALINGS IN THE SOFTWARE. 33 | notices: [] 34 | -------------------------------------------------------------------------------- /.licenses/npm/@octokit/request.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "@octokit/request" 3 | version: 5.6.3 4 | type: npm 5 | summary: Send parameterized requests to GitHub's APIs with sensible defaults in browsers 6 | and Node 7 | homepage: 8 | license: mit 9 | licenses: 10 | - sources: LICENSE 11 | text: | 12 | The MIT License 13 | 14 | Copyright (c) 2018 Octokit contributors 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy 17 | of this software and associated documentation files (the "Software"), to deal 18 | in the Software without restriction, including without limitation the rights 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | copies of the Software, and to permit persons to whom the Software is 21 | furnished to do so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in 24 | all copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 32 | THE SOFTWARE. 33 | - sources: README.md 34 | text: "[MIT](LICENSE)" 35 | notices: [] 36 | -------------------------------------------------------------------------------- /.github/workflows/check-dist.yml: -------------------------------------------------------------------------------- 1 | # `dist/index.js` is a special file in Actions. 2 | # When you reference an action with `uses:` in a workflow, 3 | # `index.js` is the code that will run. 4 | # For our project, we generate this file through a build process 5 | # from other source files. 6 | # We need to make sure the checked-in `index.js` actually matches what we expect it to be. 7 | name: Check dist 8 | 9 | on: 10 | push: 11 | branches: 12 | - main 13 | paths-ignore: 14 | - '**.md' 15 | pull_request: 16 | paths-ignore: 17 | - '**.md' 18 | workflow_dispatch: 19 | 20 | jobs: 21 | check-dist: 22 | runs-on: ubuntu-latest 23 | 24 | steps: 25 | - uses: actions/checkout@v3 26 | 27 | - name: Set Node.js 20.x 28 | uses: actions/setup-node@v1 29 | with: 30 | node-version: 20.x 31 | 32 | - name: Install dependencies 33 | run: npm ci 34 | 35 | - name: Rebuild the index.js file 36 | run: npm run build 37 | 38 | - name: Compare the expected and actual dist/ directories 39 | run: | 40 | if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then 41 | echo "Detected uncommitted changes after build. See status below:" 42 | git diff 43 | exit 1 44 | fi 45 | 46 | # If dist/ was different than expected, upload the expected version as an artifact 47 | - uses: actions/upload-artifact@v2 48 | if: ${{ failure() && steps.diff.conclusion == 'failure' }} 49 | with: 50 | name: dist 51 | path: dist/ 52 | -------------------------------------------------------------------------------- /.licenses/npm/tunnel.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: tunnel 3 | version: 0.0.6 4 | type: npm 5 | summary: Node HTTP/HTTPS Agents for tunneling proxies 6 | homepage: https://github.com/koichik/node-tunnel/ 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2012 Koichi Kobayashi 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | - sources: README.md 33 | text: Licensed under the [MIT](https://github.com/koichik/node-tunnel/blob/master/LICENSE) 34 | license. 35 | notices: [] 36 | -------------------------------------------------------------------------------- /__test__/verify-sparse-checkout.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Verify .git folder 4 | if [ ! -d "./sparse-checkout/.git" ]; then 5 | echo "Expected ./sparse-checkout/.git folder to exist" 6 | exit 1 7 | fi 8 | 9 | # Verify sparse-checkout 10 | cd sparse-checkout 11 | 12 | SPARSE=$(git sparse-checkout list) 13 | 14 | if [ "$?" != "0" ]; then 15 | echo "Failed to validate sparse-checkout" 16 | exit 1 17 | fi 18 | 19 | # Check that sparse-checkout list is not empty 20 | if [ -z "$SPARSE" ]; then 21 | echo "Expected sparse-checkout list to not be empty" 22 | exit 1 23 | fi 24 | 25 | # Check that all folders of the sparse checkout exist 26 | for pattern in $SPARSE 27 | do 28 | if [ ! -d "$pattern" ]; then 29 | echo "Expected directory '$pattern' to exist" 30 | exit 1 31 | fi 32 | done 33 | 34 | checkSparse () { 35 | if [ ! -d "./$1" ]; then 36 | echo "Expected directory '$1' to exist" 37 | exit 1 38 | fi 39 | 40 | for file in $(git ls-tree -r --name-only HEAD $1) 41 | do 42 | if [ ! -f "$file" ]; then 43 | echo "Expected file '$file' to exist" 44 | exit 1 45 | fi 46 | done 47 | } 48 | 49 | # Check that all folders and their children have been checked out 50 | checkSparse __test__ 51 | checkSparse .github 52 | checkSparse dist 53 | 54 | # Check that only sparse-checkout folders have been checked out 55 | for pattern in $(git ls-tree --name-only HEAD) 56 | do 57 | if [ -d "$pattern" ]; then 58 | if [[ "$pattern" != "__test__" && "$pattern" != ".github" && "$pattern" != "dist" ]]; then 59 | echo "Expected directory '$pattern' to not exist" 60 | exit 1 61 | fi 62 | fi 63 | done -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "checkout", 3 | "version": "4.1.0", 4 | "description": "checkout action", 5 | "main": "lib/main.js", 6 | "scripts": { 7 | "build": "tsc && ncc build && node lib/misc/generate-docs.js", 8 | "format": "prettier --write '**/*.ts'", 9 | "format-check": "prettier --check '**/*.ts'", 10 | "lint": "eslint src/**/*.ts", 11 | "test": "jest", 12 | "licensed-check": "src/misc/licensed-check.sh", 13 | "licensed-generate": "src/misc/licensed-generate.sh" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/actions/checkout.git" 18 | }, 19 | "keywords": [ 20 | "github", 21 | "actions", 22 | "checkout" 23 | ], 24 | "author": "GitHub", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/actions/checkout/issues" 28 | }, 29 | "homepage": "https://github.com/actions/checkout#readme", 30 | "dependencies": { 31 | "@actions/core": "^1.10.0", 32 | "@actions/exec": "^1.0.1", 33 | "@actions/github": "^5.0.0", 34 | "@actions/io": "^1.1.3", 35 | "@actions/tool-cache": "^1.1.2", 36 | "uuid": "^3.3.3" 37 | }, 38 | "devDependencies": { 39 | "@types/jest": "^27.0.2", 40 | "@types/node": "^20.5.3", 41 | "@types/uuid": "^3.4.6", 42 | "@typescript-eslint/eslint-plugin": "^5.45.0", 43 | "@typescript-eslint/parser": "^5.45.0", 44 | "@vercel/ncc": "^0.36.1", 45 | "eslint": "^7.32.0", 46 | "eslint-plugin-github": "^4.3.2", 47 | "eslint-plugin-jest": "^25.7.0", 48 | "jest": "^27.3.0", 49 | "jest-circus": "^27.3.0", 50 | "js-yaml": "^3.13.1", 51 | "prettier": "^1.19.1", 52 | "ts-jest": "^27.0.7", 53 | "typescript": "^4.4.4" 54 | } 55 | } -------------------------------------------------------------------------------- /.licenses/npm/webidl-conversions.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: webidl-conversions 3 | version: 3.0.1 4 | type: npm 5 | summary: Implements the WebIDL algorithms for converting to and from JavaScript values 6 | homepage: https://github.com/jsdom/webidl-conversions#readme 7 | license: bsd-2-clause 8 | licenses: 9 | - sources: LICENSE.md 10 | text: | 11 | # The BSD 2-Clause License 12 | 13 | Copyright (c) 2014, Domenic Denicola 14 | All rights reserved. 15 | 16 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 17 | 18 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 19 | 20 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | notices: [] 24 | -------------------------------------------------------------------------------- /.licenses/npm/uuid-3.3.3.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: uuid 3 | version: 3.3.3 4 | type: npm 5 | summary: RFC4122 (v1, v4, and v5) UUIDs 6 | homepage: https://github.com/kelektiv/node-uuid#readme 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.md 10 | text: | 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2010-2016 Robert Kieffer and other contributors 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | notices: 33 | - sources: AUTHORS 34 | text: |- 35 | Robert Kieffer 36 | Christoph Tavan 37 | AJ ONeal 38 | Vincent Voyer 39 | Roman Shtylman 40 | -------------------------------------------------------------------------------- /src/retry-helper.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | 3 | const defaultMaxAttempts = 3 4 | const defaultMinSeconds = 10 5 | const defaultMaxSeconds = 20 6 | 7 | export class RetryHelper { 8 | private maxAttempts: number 9 | private minSeconds: number 10 | private maxSeconds: number 11 | 12 | constructor( 13 | maxAttempts: number = defaultMaxAttempts, 14 | minSeconds: number = defaultMinSeconds, 15 | maxSeconds: number = defaultMaxSeconds 16 | ) { 17 | this.maxAttempts = maxAttempts 18 | this.minSeconds = Math.floor(minSeconds) 19 | this.maxSeconds = Math.floor(maxSeconds) 20 | if (this.minSeconds > this.maxSeconds) { 21 | throw new Error('min seconds should be less than or equal to max seconds') 22 | } 23 | } 24 | 25 | async execute(action: () => Promise): Promise { 26 | let attempt = 1 27 | while (attempt < this.maxAttempts) { 28 | // Try 29 | try { 30 | return await action() 31 | } catch (err) { 32 | core.info((err as any)?.message) 33 | } 34 | 35 | // Sleep 36 | const seconds = this.getSleepAmount() 37 | core.info(`Waiting ${seconds} seconds before trying again`) 38 | await this.sleep(seconds) 39 | attempt++ 40 | } 41 | 42 | // Last attempt 43 | return await action() 44 | } 45 | 46 | private getSleepAmount(): number { 47 | return ( 48 | Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) + 49 | this.minSeconds 50 | ) 51 | } 52 | 53 | private async sleep(seconds: number): Promise { 54 | return new Promise(resolve => setTimeout(resolve, seconds * 1000)) 55 | } 56 | } 57 | 58 | export async function execute(action: () => Promise): Promise { 59 | const retryHelper = new RetryHelper() 60 | return await retryHelper.execute(action) 61 | } 62 | -------------------------------------------------------------------------------- /.licenses/npm/is-plain-object.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: is-plain-object 3 | version: 5.0.0 4 | type: npm 5 | summary: Returns true if an object was created by the `Object` constructor, or Object.create(null). 6 | homepage: https://github.com/jonschlinkert/is-plain-object 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2014-2017, Jon Schlinkert. 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | - sources: README.md 33 | text: |- 34 | Copyright © 2019, [Jon Schlinkert](https://github.com/jonschlinkert). 35 | Released under the [MIT License](LICENSE). 36 | 37 | *** 38 | 39 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on April 28, 2019._ 40 | notices: [] 41 | -------------------------------------------------------------------------------- /src/fs-helper.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | 3 | export function directoryExistsSync(path: string, required?: boolean): boolean { 4 | if (!path) { 5 | throw new Error("Arg 'path' must not be empty") 6 | } 7 | 8 | let stats: fs.Stats 9 | try { 10 | stats = fs.statSync(path) 11 | } catch (error) { 12 | if ((error as any)?.code === 'ENOENT') { 13 | if (!required) { 14 | return false 15 | } 16 | 17 | throw new Error(`Directory '${path}' does not exist`) 18 | } 19 | 20 | throw new Error( 21 | `Encountered an error when checking whether path '${path}' exists: ${(error as any) 22 | ?.message ?? error}` 23 | ) 24 | } 25 | 26 | if (stats.isDirectory()) { 27 | return true 28 | } else if (!required) { 29 | return false 30 | } 31 | 32 | throw new Error(`Directory '${path}' does not exist`) 33 | } 34 | 35 | export function existsSync(path: string): boolean { 36 | if (!path) { 37 | throw new Error("Arg 'path' must not be empty") 38 | } 39 | 40 | try { 41 | fs.statSync(path) 42 | } catch (error) { 43 | if ((error as any)?.code === 'ENOENT') { 44 | return false 45 | } 46 | 47 | throw new Error( 48 | `Encountered an error when checking whether path '${path}' exists: ${(error as any) 49 | ?.message ?? error}` 50 | ) 51 | } 52 | 53 | return true 54 | } 55 | 56 | export function fileExistsSync(path: string): boolean { 57 | if (!path) { 58 | throw new Error("Arg 'path' must not be empty") 59 | } 60 | 61 | let stats: fs.Stats 62 | try { 63 | stats = fs.statSync(path) 64 | } catch (error) { 65 | if ((error as any)?.code === 'ENOENT') { 66 | return false 67 | } 68 | 69 | throw new Error( 70 | `Encountered an error when checking whether path '${path}' exists: ${(error as any) 71 | ?.message ?? error}` 72 | ) 73 | } 74 | 75 | if (!stats.isDirectory()) { 76 | return true 77 | } 78 | 79 | return false 80 | } 81 | -------------------------------------------------------------------------------- /src/state-helper.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | 3 | /** 4 | * Indicates whether the POST action is running 5 | */ 6 | export const IsPost = !!core.getState('isPost') 7 | 8 | /** 9 | * The repository path for the POST action. The value is empty during the MAIN action. 10 | */ 11 | export const RepositoryPath = core.getState('repositoryPath') 12 | 13 | /** 14 | * The set-safe-directory for the POST action. The value is set if input: 'safe-directory' is set during the MAIN action. 15 | */ 16 | export const PostSetSafeDirectory = core.getState('setSafeDirectory') === 'true' 17 | 18 | /** 19 | * The SSH key path for the POST action. The value is empty during the MAIN action. 20 | */ 21 | export const SshKeyPath = core.getState('sshKeyPath') 22 | 23 | /** 24 | * The SSH known hosts path for the POST action. The value is empty during the MAIN action. 25 | */ 26 | export const SshKnownHostsPath = core.getState('sshKnownHostsPath') 27 | 28 | /** 29 | * Save the repository path so the POST action can retrieve the value. 30 | */ 31 | export function setRepositoryPath(repositoryPath: string) { 32 | core.saveState('repositoryPath', repositoryPath) 33 | } 34 | 35 | /** 36 | * Save the SSH key path so the POST action can retrieve the value. 37 | */ 38 | export function setSshKeyPath(sshKeyPath: string) { 39 | core.saveState('sshKeyPath', sshKeyPath) 40 | } 41 | 42 | /** 43 | * Save the SSH known hosts path so the POST action can retrieve the value. 44 | */ 45 | export function setSshKnownHostsPath(sshKnownHostsPath: string) { 46 | core.saveState('sshKnownHostsPath', sshKnownHostsPath) 47 | } 48 | 49 | /** 50 | * Save the set-safe-directory input so the POST action can retrieve the value. 51 | */ 52 | export function setSafeDirectory() { 53 | core.saveState('setSafeDirectory', 'true') 54 | } 55 | 56 | // Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic. 57 | // This is necessary since we don't have a separate entry point. 58 | if (!IsPost) { 59 | core.saveState('isPost', 'true') 60 | } 61 | -------------------------------------------------------------------------------- /.licenses/npm/qs.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: qs 3 | version: 6.11.0 4 | type: npm 5 | summary: A querystring parser that supports nesting and arrays, with a depth limit 6 | homepage: https://github.com/ljharb/qs 7 | license: bsd-3-clause 8 | licenses: 9 | - sources: LICENSE.md 10 | text: | 11 | BSD 3-Clause License 12 | 13 | Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/contributors) 14 | All rights reserved. 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions are met: 18 | 19 | 1. Redistributions of source code must retain the above copyright notice, this 20 | list of conditions and the following disclaimer. 21 | 22 | 2. Redistributions in binary form must reproduce the above copyright notice, 23 | this list of conditions and the following disclaimer in the documentation 24 | and/or other materials provided with the distribution. 25 | 26 | 3. Neither the name of the copyright holder nor the names of its 27 | contributors may be used to endorse or promote products derived from 28 | this software without specific prior written permission. 29 | 30 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 31 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 33 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 34 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 36 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 37 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | notices: [] 41 | -------------------------------------------------------------------------------- /src/git-version.ts: -------------------------------------------------------------------------------- 1 | export class GitVersion { 2 | private readonly major: number = NaN 3 | private readonly minor: number = NaN 4 | private readonly patch: number = NaN 5 | 6 | /** 7 | * Used for comparing the version of git and git-lfs against the minimum required version 8 | * @param version the version string, e.g. 1.2 or 1.2.3 9 | */ 10 | constructor(version?: string) { 11 | if (version) { 12 | const match = version.match(/^(\d+)\.(\d+)(\.(\d+))?$/) 13 | if (match) { 14 | this.major = Number(match[1]) 15 | this.minor = Number(match[2]) 16 | if (match[4]) { 17 | this.patch = Number(match[4]) 18 | } 19 | } 20 | } 21 | } 22 | 23 | /** 24 | * Compares the instance against a minimum required version 25 | * @param minimum Minimum version 26 | */ 27 | checkMinimum(minimum: GitVersion): boolean { 28 | if (!minimum.isValid()) { 29 | throw new Error('Arg minimum is not a valid version') 30 | } 31 | 32 | // Major is insufficient 33 | if (this.major < minimum.major) { 34 | return false 35 | } 36 | 37 | // Major is equal 38 | if (this.major === minimum.major) { 39 | // Minor is insufficient 40 | if (this.minor < minimum.minor) { 41 | return false 42 | } 43 | 44 | // Minor is equal 45 | if (this.minor === minimum.minor) { 46 | // Patch is insufficient 47 | if (this.patch && this.patch < (minimum.patch || 0)) { 48 | return false 49 | } 50 | } 51 | } 52 | 53 | return true 54 | } 55 | 56 | /** 57 | * Indicates whether the instance was constructed from a valid version string 58 | */ 59 | isValid(): boolean { 60 | return !isNaN(this.major) 61 | } 62 | 63 | /** 64 | * Returns the version as a string, e.g. 1.2 or 1.2.3 65 | */ 66 | toString(): string { 67 | let result = '' 68 | if (this.isValid()) { 69 | result = `${this.major}.${this.minor}` 70 | if (!isNaN(this.patch)) { 71 | result += `.${this.patch}` 72 | } 73 | } 74 | 75 | return result 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '28 9 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # 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 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v3 43 | 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | - run: npm ci 54 | - run: npm run build 55 | - run: rm -rf dist # We want code scanning to analyze lib instead (individual .js files) 56 | 57 | - name: Perform CodeQL Analysis 58 | uses: github/codeql-action/analyze@v2 59 | -------------------------------------------------------------------------------- /__test__/git-version.test.ts: -------------------------------------------------------------------------------- 1 | import {GitVersion} from '../lib/git-version' 2 | 3 | describe('git-version tests', () => { 4 | it('basics', async () => { 5 | let version = new GitVersion('') 6 | expect(version.isValid()).toBeFalsy() 7 | 8 | version = new GitVersion('asdf') 9 | expect(version.isValid()).toBeFalsy() 10 | 11 | version = new GitVersion('1.2') 12 | expect(version.isValid()).toBeTruthy() 13 | expect(version.toString()).toBe('1.2') 14 | 15 | version = new GitVersion('1.2.3') 16 | expect(version.isValid()).toBeTruthy() 17 | expect(version.toString()).toBe('1.2.3') 18 | }) 19 | 20 | it('check minimum', async () => { 21 | let version = new GitVersion('4.5') 22 | expect(version.checkMinimum(new GitVersion('3.6'))).toBeTruthy() 23 | expect(version.checkMinimum(new GitVersion('3.6.7'))).toBeTruthy() 24 | expect(version.checkMinimum(new GitVersion('4.4'))).toBeTruthy() 25 | expect(version.checkMinimum(new GitVersion('4.5'))).toBeTruthy() 26 | expect(version.checkMinimum(new GitVersion('4.5.0'))).toBeTruthy() 27 | expect(version.checkMinimum(new GitVersion('4.6'))).toBeFalsy() 28 | expect(version.checkMinimum(new GitVersion('4.6.0'))).toBeFalsy() 29 | expect(version.checkMinimum(new GitVersion('5.1'))).toBeFalsy() 30 | expect(version.checkMinimum(new GitVersion('5.1.2'))).toBeFalsy() 31 | 32 | version = new GitVersion('4.5.6') 33 | expect(version.checkMinimum(new GitVersion('3.6'))).toBeTruthy() 34 | expect(version.checkMinimum(new GitVersion('3.6.7'))).toBeTruthy() 35 | expect(version.checkMinimum(new GitVersion('4.4'))).toBeTruthy() 36 | expect(version.checkMinimum(new GitVersion('4.5'))).toBeTruthy() 37 | expect(version.checkMinimum(new GitVersion('4.5.5'))).toBeTruthy() 38 | expect(version.checkMinimum(new GitVersion('4.5.6'))).toBeTruthy() 39 | expect(version.checkMinimum(new GitVersion('4.5.7'))).toBeFalsy() 40 | expect(version.checkMinimum(new GitVersion('4.6'))).toBeFalsy() 41 | expect(version.checkMinimum(new GitVersion('4.6.0'))).toBeFalsy() 42 | expect(version.checkMinimum(new GitVersion('5.1'))).toBeFalsy() 43 | expect(version.checkMinimum(new GitVersion('5.1.2'))).toBeFalsy() 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["jest", "@typescript-eslint"], 3 | "extends": ["plugin:github/recommended"], 4 | "parser": "@typescript-eslint/parser", 5 | "parserOptions": { 6 | "ecmaVersion": 9, 7 | "sourceType": "module", 8 | "project": "./tsconfig.json" 9 | }, 10 | "rules": { 11 | "eslint-comments/no-use": "off", 12 | "import/no-namespace": "off", 13 | "no-unused-vars": "off", 14 | "@typescript-eslint/no-unused-vars": "error", 15 | "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}], 16 | "@typescript-eslint/no-require-imports": "error", 17 | "@typescript-eslint/array-type": "error", 18 | "@typescript-eslint/await-thenable": "error", 19 | "camelcase": "off", 20 | "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}], 21 | "@typescript-eslint/func-call-spacing": ["error", "never"], 22 | "@typescript-eslint/no-array-constructor": "error", 23 | "@typescript-eslint/no-empty-interface": "error", 24 | "@typescript-eslint/no-explicit-any": "error", 25 | "@typescript-eslint/no-extraneous-class": "error", 26 | "@typescript-eslint/no-floating-promises": "error", 27 | "@typescript-eslint/no-for-in-array": "error", 28 | "@typescript-eslint/no-inferrable-types": "error", 29 | "@typescript-eslint/no-misused-new": "error", 30 | "@typescript-eslint/no-namespace": "error", 31 | "@typescript-eslint/no-non-null-assertion": "warn", 32 | "@typescript-eslint/no-unnecessary-qualifier": "error", 33 | "@typescript-eslint/no-unnecessary-type-assertion": "error", 34 | "@typescript-eslint/no-useless-constructor": "error", 35 | "@typescript-eslint/no-var-requires": "error", 36 | "@typescript-eslint/prefer-for-of": "warn", 37 | "@typescript-eslint/prefer-function-type": "warn", 38 | "@typescript-eslint/prefer-includes": "error", 39 | "@typescript-eslint/prefer-string-starts-ends-with": "error", 40 | "@typescript-eslint/promise-function-async": "error", 41 | "@typescript-eslint/require-array-sort-compare": "error", 42 | "@typescript-eslint/restrict-plus-operands": "error", 43 | "semi": "off", 44 | "@typescript-eslint/semi": ["error", "never"], 45 | "@typescript-eslint/type-annotation-spacing": "error", 46 | "@typescript-eslint/unbound-method": "error" 47 | }, 48 | "env": { 49 | "node": true, 50 | "es6": true, 51 | "jest/globals": true 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.licenses/npm/object-inspect.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: object-inspect 3 | version: 1.11.0 4 | type: npm 5 | summary: string representations of objects in node and the browser 6 | homepage: https://github.com/inspect-js/object-inspect 7 | license: mit 8 | licenses: 9 | - sources: LICENSE 10 | text: | 11 | MIT License 12 | 13 | Copyright (c) 2013 James Halliday 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | - sources: readme.markdown 33 | text: |- 34 | MIT 35 | 36 | [1]: https://npmjs.org/package/object-inspect 37 | [2]: https://versionbadg.es/inspect-js/object-inspect.svg 38 | [5]: https://david-dm.org/inspect-js/object-inspect.svg 39 | [6]: https://david-dm.org/inspect-js/object-inspect 40 | [7]: https://david-dm.org/inspect-js/object-inspect/dev-status.svg 41 | [8]: https://david-dm.org/inspect-js/object-inspect#info=devDependencies 42 | [11]: https://nodei.co/npm/object-inspect.png?downloads=true&stars=true 43 | [license-image]: https://img.shields.io/npm/l/object-inspect.svg 44 | [license-url]: LICENSE 45 | [downloads-image]: https://img.shields.io/npm/dm/object-inspect.svg 46 | [downloads-url]: https://npm-stat.com/charts.html?package=object-inspect 47 | [codecov-image]: https://codecov.io/gh/inspect-js/object-inspect/branch/main/graphs/badge.svg 48 | [codecov-url]: https://app.codecov.io/gh/inspect-js/object-inspect/ 49 | [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/inspect-js/object-inspect 50 | [actions-url]: https://github.com/inspect-js/object-inspect/actions 51 | notices: [] 52 | -------------------------------------------------------------------------------- /__test__/retry-helper.test.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import {RetryHelper} from '../lib/retry-helper' 3 | 4 | let info: string[] 5 | let retryHelper: any 6 | 7 | describe('retry-helper tests', () => { 8 | beforeAll(() => { 9 | // Mock @actions/core info() 10 | jest.spyOn(core, 'info').mockImplementation((message: string) => { 11 | info.push(message) 12 | }) 13 | 14 | retryHelper = new RetryHelper(3, 0, 0) 15 | }) 16 | 17 | beforeEach(() => { 18 | // Reset info 19 | info = [] 20 | }) 21 | 22 | afterAll(() => { 23 | // Restore 24 | jest.restoreAllMocks() 25 | }) 26 | 27 | it('first attempt succeeds', async () => { 28 | const actual = await retryHelper.execute(async () => { 29 | return 'some result' 30 | }) 31 | expect(actual).toBe('some result') 32 | expect(info).toHaveLength(0) 33 | }) 34 | 35 | it('second attempt succeeds', async () => { 36 | let attempts = 0 37 | const actual = await retryHelper.execute(() => { 38 | if (++attempts == 1) { 39 | throw new Error('some error') 40 | } 41 | 42 | return Promise.resolve('some result') 43 | }) 44 | expect(attempts).toBe(2) 45 | expect(actual).toBe('some result') 46 | expect(info).toHaveLength(2) 47 | expect(info[0]).toBe('some error') 48 | expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) 49 | }) 50 | 51 | it('third attempt succeeds', async () => { 52 | let attempts = 0 53 | const actual = await retryHelper.execute(() => { 54 | if (++attempts < 3) { 55 | throw new Error(`some error ${attempts}`) 56 | } 57 | 58 | return Promise.resolve('some result') 59 | }) 60 | expect(attempts).toBe(3) 61 | expect(actual).toBe('some result') 62 | expect(info).toHaveLength(4) 63 | expect(info[0]).toBe('some error 1') 64 | expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) 65 | expect(info[2]).toBe('some error 2') 66 | expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) 67 | }) 68 | 69 | it('all attempts fail succeeds', async () => { 70 | let attempts = 0 71 | let error: Error = (null as unknown) as Error 72 | try { 73 | await retryHelper.execute(() => { 74 | throw new Error(`some error ${++attempts}`) 75 | }) 76 | } catch (err) { 77 | error = err as Error 78 | } 79 | expect(error.message).toBe('some error 3') 80 | expect(attempts).toBe(3) 81 | expect(info).toHaveLength(4) 82 | expect(info[0]).toBe('some error 1') 83 | expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) 84 | expect(info[2]).toBe('some error 2') 85 | expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /src/git-source-settings.ts: -------------------------------------------------------------------------------- 1 | export interface IGitSourceSettings { 2 | /** 3 | * The location on disk where the repository will be placed 4 | */ 5 | repositoryPath: string 6 | 7 | /** 8 | * The repository owner 9 | */ 10 | repositoryOwner: string 11 | 12 | /** 13 | * The repository name 14 | */ 15 | repositoryName: string 16 | 17 | /** 18 | * The ref to fetch 19 | */ 20 | ref: string 21 | 22 | /** 23 | * The commit to checkout 24 | */ 25 | commit: string 26 | 27 | /** 28 | * Indicates whether to clean the repository 29 | */ 30 | clean: boolean 31 | 32 | /** 33 | * The filter determining which objects to include 34 | */ 35 | filter: string | undefined 36 | 37 | /** 38 | * The array of folders to make the sparse checkout 39 | */ 40 | sparseCheckout: string[] 41 | 42 | /** 43 | * Indicates whether to use cone mode in the sparse checkout (if any) 44 | */ 45 | sparseCheckoutConeMode: boolean 46 | 47 | /** 48 | * The depth when fetching 49 | */ 50 | fetchDepth: number 51 | 52 | /** 53 | * Fetch tags, even if fetchDepth > 0 (default: false) 54 | */ 55 | fetchTags: boolean 56 | 57 | /** 58 | * Indicates whether to use the --progress option when fetching 59 | */ 60 | showProgress: boolean 61 | 62 | /** 63 | * Indicates whether to fetch LFS objects 64 | */ 65 | lfs: boolean 66 | 67 | /** 68 | * Indicates whether to checkout submodules 69 | */ 70 | submodules: boolean 71 | 72 | /** 73 | * Indicates whether to recursively checkout submodules 74 | */ 75 | nestedSubmodules: boolean 76 | 77 | /** 78 | * The auth token to use when fetching the repository 79 | */ 80 | authToken: string 81 | 82 | /** 83 | * The SSH key to configure 84 | */ 85 | sshKey: string 86 | 87 | /** 88 | * Additional SSH known hosts 89 | */ 90 | sshKnownHosts: string 91 | 92 | /** 93 | * Indicates whether the server must be a known host 94 | */ 95 | sshStrict: boolean 96 | 97 | /** 98 | * Indicates whether to persist the credentials on disk to enable scripting authenticated git commands 99 | */ 100 | persistCredentials: boolean 101 | 102 | /** 103 | * Organization ID for the currently running workflow (used for auth settings) 104 | */ 105 | workflowOrganizationId: number | undefined 106 | 107 | /** 108 | * Indicates whether to add repositoryPath as safe.directory in git global config 109 | */ 110 | setSafeDirectory: boolean 111 | 112 | /** 113 | * User override on the GitHub Server/Host URL that hosts the repository to be cloned 114 | */ 115 | githubServerUrl: string | undefined 116 | } 117 | -------------------------------------------------------------------------------- /.licenses/npm/node-fetch.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: node-fetch 3 | version: 2.6.9 4 | type: npm 5 | summary: A light-weight module that brings window.fetch to node.js 6 | homepage: https://github.com/bitinn/node-fetch 7 | license: mit 8 | licenses: 9 | - sources: LICENSE.md 10 | text: |+ 11 | The MIT License (MIT) 12 | 13 | Copyright (c) 2016 David Frank 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | 33 | - sources: README.md 34 | text: |- 35 | MIT 36 | 37 | [npm-image]: https://flat.badgen.net/npm/v/node-fetch 38 | [npm-url]: https://www.npmjs.com/package/node-fetch 39 | [travis-image]: https://flat.badgen.net/travis/bitinn/node-fetch 40 | [travis-url]: https://travis-ci.org/bitinn/node-fetch 41 | [codecov-image]: https://flat.badgen.net/codecov/c/github/bitinn/node-fetch/master 42 | [codecov-url]: https://codecov.io/gh/bitinn/node-fetch 43 | [install-size-image]: https://flat.badgen.net/packagephobia/install/node-fetch 44 | [install-size-url]: https://packagephobia.now.sh/result?p=node-fetch 45 | [discord-image]: https://img.shields.io/discord/619915844268326952?color=%237289DA&label=Discord&style=flat-square 46 | [discord-url]: https://discord.gg/Zxbndcm 47 | [opencollective-image]: https://opencollective.com/node-fetch/backers.svg 48 | [opencollective-url]: https://opencollective.com/node-fetch 49 | [whatwg-fetch]: https://fetch.spec.whatwg.org/ 50 | [response-init]: https://fetch.spec.whatwg.org/#responseinit 51 | [node-readable]: https://nodejs.org/api/stream.html#stream_readable_streams 52 | [mdn-headers]: https://developer.mozilla.org/en-US/docs/Web/API/Headers 53 | [LIMITS.md]: https://github.com/bitinn/node-fetch/blob/master/LIMITS.md 54 | [ERROR-HANDLING.md]: https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md 55 | [UPGRADE-GUIDE.md]: https://github.com/bitinn/node-fetch/blob/master/UPGRADE-GUIDE.md 56 | notices: [] 57 | -------------------------------------------------------------------------------- /src/misc/generate-docs.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import * as os from 'os' 3 | import * as path from 'path' 4 | import * as yaml from 'js-yaml' 5 | 6 | // 7 | // SUMMARY 8 | // 9 | // This script rebuilds the usage section in the README.md to be consistent with the action.yml 10 | 11 | function updateUsage( 12 | actionReference: string, 13 | actionYamlPath = 'action.yml', 14 | readmePath = 'README.md', 15 | startToken = '', 16 | endToken = '' 17 | ): void { 18 | if (!actionReference) { 19 | throw new Error('Parameter actionReference must not be empty') 20 | } 21 | 22 | // Load the action.yml 23 | const actionYaml = yaml.safeLoad(fs.readFileSync(actionYamlPath).toString()) 24 | 25 | // Load the README 26 | const originalReadme = fs.readFileSync(readmePath).toString() 27 | 28 | // Find the start token 29 | const startTokenIndex = originalReadme.indexOf(startToken) 30 | if (startTokenIndex < 0) { 31 | throw new Error(`Start token '${startToken}' not found`) 32 | } 33 | 34 | // Find the end token 35 | const endTokenIndex = originalReadme.indexOf(endToken) 36 | if (endTokenIndex < 0) { 37 | throw new Error(`End token '${endToken}' not found`) 38 | } else if (endTokenIndex < startTokenIndex) { 39 | throw new Error('Start token must appear before end token') 40 | } 41 | 42 | // Build the new README 43 | const newReadme: string[] = [] 44 | 45 | // Append the beginning 46 | newReadme.push(originalReadme.substr(0, startTokenIndex + startToken.length)) 47 | 48 | // Build the new usage section 49 | newReadme.push('```yaml', `- uses: ${actionReference}`, ' with:') 50 | const inputs = actionYaml.inputs 51 | let firstInput = true 52 | for (const key of Object.keys(inputs)) { 53 | const input = inputs[key] 54 | 55 | // Line break between inputs 56 | if (!firstInput) { 57 | newReadme.push('') 58 | } 59 | 60 | // Constrain the width of the description 61 | const width = 80 62 | let description = (input.description as string) 63 | .trimRight() 64 | .replace(/\r\n/g, '\n') // Convert CR to LF 65 | .replace(/ +/g, ' ') // Squash consecutive spaces 66 | .replace(/ \n/g, '\n') // Squash space followed by newline 67 | while (description) { 68 | // Longer than width? Find a space to break apart 69 | let segment: string = description 70 | if (description.length > width) { 71 | segment = description.substr(0, width + 1) 72 | while (!segment.endsWith(' ') && !segment.endsWith('\n') && segment) { 73 | segment = segment.substr(0, segment.length - 1) 74 | } 75 | 76 | // Trimmed too much? 77 | if (segment.length < width * 0.67) { 78 | segment = description 79 | } 80 | } else { 81 | segment = description 82 | } 83 | 84 | // Check for newline 85 | const newlineIndex = segment.indexOf('\n') 86 | if (newlineIndex >= 0) { 87 | segment = segment.substr(0, newlineIndex + 1) 88 | } 89 | 90 | // Append segment 91 | newReadme.push(` # ${segment}`.trimRight()) 92 | 93 | // Remaining 94 | description = description.substr(segment.length) 95 | } 96 | 97 | if (input.default !== undefined) { 98 | // Append blank line if description had paragraphs 99 | if ((input.description as string).trimRight().match(/\n[ ]*\r?\n/)) { 100 | newReadme.push(` #`) 101 | } 102 | 103 | // Default 104 | newReadme.push(` # Default: ${input.default}`) 105 | } 106 | 107 | // Input name 108 | newReadme.push(` ${key}: ''`) 109 | 110 | firstInput = false 111 | } 112 | 113 | newReadme.push('```') 114 | 115 | // Append the end 116 | newReadme.push(originalReadme.substr(endTokenIndex)) 117 | 118 | // Write the new README 119 | fs.writeFileSync(readmePath, newReadme.join(os.EOL)) 120 | } 121 | 122 | updateUsage( 123 | 'actions/checkout@v4', 124 | path.join(__dirname, '..', '..', 'action.yml'), 125 | path.join(__dirname, '..', '..', 'README.md') 126 | ) 127 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Checkout' 2 | description: 'Checkout a Git repository at a particular version' 3 | inputs: 4 | repository: 5 | description: 'Repository name with owner. For example, actions/checkout' 6 | default: ${{ github.repository }} 7 | ref: 8 | description: > 9 | The branch, tag or SHA to checkout. When checking out the repository that 10 | triggered a workflow, this defaults to the reference or SHA for that 11 | event. Otherwise, uses the default branch. 12 | token: 13 | description: > 14 | Personal access token (PAT) used to fetch the repository. The PAT is configured 15 | with the local git config, which enables your scripts to run authenticated git 16 | commands. The post-job step removes the PAT. 17 | 18 | 19 | We recommend using a service account with the least permissions necessary. 20 | Also when generating a new PAT, select the least scopes necessary. 21 | 22 | 23 | [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) 24 | default: ${{ github.token }} 25 | ssh-key: 26 | description: > 27 | SSH key used to fetch the repository. The SSH key is configured with the local 28 | git config, which enables your scripts to run authenticated git commands. 29 | The post-job step removes the SSH key. 30 | 31 | 32 | We recommend using a service account with the least permissions necessary. 33 | 34 | 35 | [Learn more about creating and using 36 | encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets) 37 | ssh-known-hosts: 38 | description: > 39 | Known hosts in addition to the user and global host key database. The public 40 | SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example, 41 | `ssh-keyscan github.com`. The public key for github.com is always implicitly added. 42 | ssh-strict: 43 | description: > 44 | Whether to perform strict host key checking. When true, adds the options `StrictHostKeyChecking=yes` 45 | and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to 46 | configure additional hosts. 47 | default: true 48 | persist-credentials: 49 | description: 'Whether to configure the token or SSH key with the local git config' 50 | default: true 51 | path: 52 | description: 'Relative path under $GITHUB_WORKSPACE to place the repository' 53 | clean: 54 | description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching' 55 | default: true 56 | filter: 57 | description: > 58 | Partially clone against a given filter. 59 | Overrides sparse-checkout if set. 60 | default: null 61 | sparse-checkout: 62 | description: > 63 | Do a sparse checkout on given patterns. 64 | Each pattern should be separated with new lines. 65 | default: null 66 | sparse-checkout-cone-mode: 67 | description: > 68 | Specifies whether to use cone-mode when doing a sparse checkout. 69 | default: true 70 | fetch-depth: 71 | description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.' 72 | default: 1 73 | fetch-tags: 74 | description: 'Whether to fetch tags, even if fetch-depth > 0.' 75 | default: false 76 | show-progress: 77 | description: 'Whether to show progress status output when fetching.' 78 | default: true 79 | lfs: 80 | description: 'Whether to download Git-LFS files' 81 | default: false 82 | submodules: 83 | description: > 84 | Whether to checkout submodules: `true` to checkout submodules or `recursive` to 85 | recursively checkout submodules. 86 | 87 | 88 | When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are 89 | converted to HTTPS. 90 | default: false 91 | set-safe-directory: 92 | description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory ` 93 | default: true 94 | github-server-url: 95 | 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 96 | required: false 97 | runs: 98 | using: node20 99 | main: dist/index.js 100 | post: dist/index.js 101 | -------------------------------------------------------------------------------- /src/git-directory-helper.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert' 2 | import * as core from '@actions/core' 3 | import * as fs from 'fs' 4 | import * as fsHelper from './fs-helper' 5 | import * as io from '@actions/io' 6 | import * as path from 'path' 7 | import {IGitCommandManager} from './git-command-manager' 8 | 9 | export async function prepareExistingDirectory( 10 | git: IGitCommandManager | undefined, 11 | repositoryPath: string, 12 | repositoryUrl: string, 13 | clean: boolean, 14 | ref: string 15 | ): Promise { 16 | assert.ok(repositoryPath, 'Expected repositoryPath to be defined') 17 | assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined') 18 | 19 | // Indicates whether to delete the directory contents 20 | let remove = false 21 | 22 | // Check whether using git or REST API 23 | if (!git) { 24 | remove = true 25 | } 26 | // Fetch URL does not match 27 | else if ( 28 | !fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) || 29 | repositoryUrl !== (await git.tryGetFetchUrl()) 30 | ) { 31 | remove = true 32 | } else { 33 | // Delete any index.lock and shallow.lock left by a previously canceled run or crashed git process 34 | const lockPaths = [ 35 | path.join(repositoryPath, '.git', 'index.lock'), 36 | path.join(repositoryPath, '.git', 'shallow.lock') 37 | ] 38 | for (const lockPath of lockPaths) { 39 | try { 40 | await io.rmRF(lockPath) 41 | } catch (error) { 42 | core.debug( 43 | `Unable to delete '${lockPath}'. ${(error as any)?.message ?? error}` 44 | ) 45 | } 46 | } 47 | 48 | try { 49 | core.startGroup('Removing previously created refs, to avoid conflicts') 50 | // Checkout detached HEAD 51 | if (!(await git.isDetached())) { 52 | await git.checkoutDetach() 53 | } 54 | 55 | // Remove all refs/heads/* 56 | let branches = await git.branchList(false) 57 | for (const branch of branches) { 58 | await git.branchDelete(false, branch) 59 | } 60 | 61 | // Remove any conflicting refs/remotes/origin/* 62 | // Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar 63 | // Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo 64 | if (ref) { 65 | ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}` 66 | if (ref.startsWith('refs/heads/')) { 67 | const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length) 68 | const upperName1Slash = `${upperName1}/` 69 | branches = await git.branchList(true) 70 | for (const branch of branches) { 71 | const upperName2 = branch.substr('origin/'.length).toUpperCase() 72 | const upperName2Slash = `${upperName2}/` 73 | if ( 74 | upperName1.startsWith(upperName2Slash) || 75 | upperName2.startsWith(upperName1Slash) 76 | ) { 77 | await git.branchDelete(true, branch) 78 | } 79 | } 80 | } 81 | } 82 | core.endGroup() 83 | 84 | // Check for submodules and delete any existing files if submodules are present 85 | if (!(await git.submoduleStatus())) { 86 | remove = true 87 | core.info('Bad Submodules found, removing existing files') 88 | } 89 | 90 | // Clean 91 | if (clean) { 92 | core.startGroup('Cleaning the repository') 93 | if (!(await git.tryClean())) { 94 | core.debug( 95 | `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}'.` 96 | ) 97 | remove = true 98 | } else if (!(await git.tryReset())) { 99 | remove = true 100 | } 101 | core.endGroup() 102 | 103 | if (remove) { 104 | core.warning( 105 | `Unable to clean or reset the repository. The repository will be recreated instead.` 106 | ) 107 | } 108 | } 109 | } catch (error) { 110 | core.warning( 111 | `Unable to prepare the existing repository. The repository will be recreated instead.` 112 | ) 113 | remove = true 114 | } 115 | } 116 | 117 | if (remove) { 118 | // Delete the contents of the directory. Don't delete the directory itself 119 | // since it might be the current working directory. 120 | core.info(`Deleting the contents of '${repositoryPath}'`) 121 | for (const file of await fs.promises.readdir(repositoryPath)) { 122 | await io.rmRF(path.join(repositoryPath, file)) 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/github-api-helper.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert' 2 | import * as core from '@actions/core' 3 | import * as fs from 'fs' 4 | import * as github from '@actions/github' 5 | import * as io from '@actions/io' 6 | import * as path from 'path' 7 | import * as retryHelper from './retry-helper' 8 | import * as toolCache from '@actions/tool-cache' 9 | import {default as uuid} from 'uuid/v4' 10 | import {getServerApiUrl} from './url-helper' 11 | 12 | const IS_WINDOWS = process.platform === 'win32' 13 | 14 | export async function downloadRepository( 15 | authToken: string, 16 | owner: string, 17 | repo: string, 18 | ref: string, 19 | commit: string, 20 | repositoryPath: string, 21 | baseUrl?: string 22 | ): Promise { 23 | // Determine the default branch 24 | if (!ref && !commit) { 25 | core.info('Determining the default branch') 26 | ref = await getDefaultBranch(authToken, owner, repo, baseUrl) 27 | } 28 | 29 | // Download the archive 30 | let archiveData = await retryHelper.execute(async () => { 31 | core.info('Downloading the archive') 32 | return await downloadArchive(authToken, owner, repo, ref, commit, baseUrl) 33 | }) 34 | 35 | // Write archive to disk 36 | core.info('Writing archive to disk') 37 | const uniqueId = uuid() 38 | const archivePath = path.join(repositoryPath, `${uniqueId}.tar.gz`) 39 | await fs.promises.writeFile(archivePath, archiveData) 40 | archiveData = Buffer.from('') // Free memory 41 | 42 | // Extract archive 43 | core.info('Extracting the archive') 44 | const extractPath = path.join(repositoryPath, uniqueId) 45 | await io.mkdirP(extractPath) 46 | if (IS_WINDOWS) { 47 | await toolCache.extractZip(archivePath, extractPath) 48 | } else { 49 | await toolCache.extractTar(archivePath, extractPath) 50 | } 51 | await io.rmRF(archivePath) 52 | 53 | // Determine the path of the repository content. The archive contains 54 | // a top-level folder and the repository content is inside. 55 | const archiveFileNames = await fs.promises.readdir(extractPath) 56 | assert.ok( 57 | archiveFileNames.length == 1, 58 | 'Expected exactly one directory inside archive' 59 | ) 60 | const archiveVersion = archiveFileNames[0] // The top-level folder name includes the short SHA 61 | core.info(`Resolved version ${archiveVersion}`) 62 | const tempRepositoryPath = path.join(extractPath, archiveVersion) 63 | 64 | // Move the files 65 | for (const fileName of await fs.promises.readdir(tempRepositoryPath)) { 66 | const sourcePath = path.join(tempRepositoryPath, fileName) 67 | const targetPath = path.join(repositoryPath, fileName) 68 | if (IS_WINDOWS) { 69 | await io.cp(sourcePath, targetPath, {recursive: true}) // Copy on Windows (Windows Defender may have a lock) 70 | } else { 71 | await io.mv(sourcePath, targetPath) 72 | } 73 | } 74 | await io.rmRF(extractPath) 75 | } 76 | 77 | /** 78 | * Looks up the default branch name 79 | */ 80 | export async function getDefaultBranch( 81 | authToken: string, 82 | owner: string, 83 | repo: string, 84 | baseUrl?: string 85 | ): Promise { 86 | return await retryHelper.execute(async () => { 87 | core.info('Retrieving the default branch name') 88 | const octokit = github.getOctokit(authToken, { 89 | baseUrl: getServerApiUrl(baseUrl) 90 | }) 91 | let result: string 92 | try { 93 | // Get the default branch from the repo info 94 | const response = await octokit.rest.repos.get({owner, repo}) 95 | result = response.data.default_branch 96 | assert.ok(result, 'default_branch cannot be empty') 97 | } catch (err) { 98 | // Handle .wiki repo 99 | if ( 100 | (err as any)?.status === 404 && 101 | repo.toUpperCase().endsWith('.WIKI') 102 | ) { 103 | result = 'master' 104 | } 105 | // Otherwise error 106 | else { 107 | throw err 108 | } 109 | } 110 | 111 | // Print the default branch 112 | core.info(`Default branch '${result}'`) 113 | 114 | // Prefix with 'refs/heads' 115 | if (!result.startsWith('refs/')) { 116 | result = `refs/heads/${result}` 117 | } 118 | 119 | return result 120 | }) 121 | } 122 | 123 | async function downloadArchive( 124 | authToken: string, 125 | owner: string, 126 | repo: string, 127 | ref: string, 128 | commit: string, 129 | baseUrl?: string 130 | ): Promise { 131 | const octokit = github.getOctokit(authToken, { 132 | baseUrl: getServerApiUrl(baseUrl) 133 | }) 134 | const download = IS_WINDOWS 135 | ? octokit.rest.repos.downloadZipballArchive 136 | : octokit.rest.repos.downloadTarballArchive 137 | const response = await download({ 138 | owner: owner, 139 | repo: repo, 140 | ref: commit || ref 141 | }) 142 | return Buffer.from(response.data as ArrayBuffer) // response.data is ArrayBuffer 143 | } 144 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v4.1.0 4 | - [Add support for partial checkout filters](https://github.com/actions/checkout/pull/1396) 5 | 6 | ## v4.0.0 7 | - [Support fetching without the --progress option](https://github.com/actions/checkout/pull/1067) 8 | - [Update to node20](https://github.com/actions/checkout/pull/1436) 9 | 10 | ## v3.6.0 11 | - [Fix: Mark test scripts with Bash'isms to be run via Bash](https://github.com/actions/checkout/pull/1377) 12 | - [Add option to fetch tags even if fetch-depth > 0](https://github.com/actions/checkout/pull/579) 13 | 14 | ## v3.5.3 15 | - [Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in](https://github.com/actions/checkout/pull/1196) 16 | - [Fix typos found by codespell](https://github.com/actions/checkout/pull/1287) 17 | - [Add support for sparse checkouts](https://github.com/actions/checkout/pull/1369) 18 | 19 | ## v3.5.2 20 | - [Fix api endpoint for GHES](https://github.com/actions/checkout/pull/1289) 21 | 22 | ## v3.5.1 23 | - [Fix slow checkout on Windows](https://github.com/actions/checkout/pull/1246) 24 | 25 | ## v3.5.0 26 | * [Add new public key for known_hosts](https://github.com/actions/checkout/pull/1237) 27 | 28 | ## v3.4.0 29 | - [Upgrade codeql actions to v2](https://github.com/actions/checkout/pull/1209) 30 | - [Upgrade dependencies](https://github.com/actions/checkout/pull/1210) 31 | - [Upgrade @actions/io](https://github.com/actions/checkout/pull/1225) 32 | 33 | ## v3.3.0 34 | - [Implement branch list using callbacks from exec function](https://github.com/actions/checkout/pull/1045) 35 | - [Add in explicit reference to private checkout options](https://github.com/actions/checkout/pull/1050) 36 | - [Fix comment typos (that got added in #770)](https://github.com/actions/checkout/pull/1057) 37 | 38 | ## v3.2.0 39 | - [Add GitHub Action to perform release](https://github.com/actions/checkout/pull/942) 40 | - [Fix status badge](https://github.com/actions/checkout/pull/967) 41 | - [Replace datadog/squid with ubuntu/squid Docker image](https://github.com/actions/checkout/pull/1002) 42 | - [Wrap pipeline commands for submoduleForeach in quotes](https://github.com/actions/checkout/pull/964) 43 | - [Update @actions/io to 1.1.2](https://github.com/actions/checkout/pull/1029) 44 | - [Upgrading version to 3.2.0](https://github.com/actions/checkout/pull/1039) 45 | 46 | ## v3.1.0 47 | - [Use @actions/core `saveState` and `getState`](https://github.com/actions/checkout/pull/939) 48 | - [Add `github-server-url` input](https://github.com/actions/checkout/pull/922) 49 | 50 | ## v3.0.2 51 | - [Add input `set-safe-directory`](https://github.com/actions/checkout/pull/770) 52 | 53 | ## v3.0.1 54 | - [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) 55 | - [Bumped various npm package versions](https://github.com/actions/checkout/pull/744) 56 | 57 | ## v3.0.0 58 | 59 | - [Update to node 16](https://github.com/actions/checkout/pull/689) 60 | 61 | ## v2.3.1 62 | 63 | - [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284) 64 | 65 | ## v2.3.0 66 | 67 | - [Fallback to the default branch](https://github.com/actions/checkout/pull/278) 68 | 69 | ## v2.2.0 70 | 71 | - [Fetch all history for all tags and branches when fetch-depth=0](https://github.com/actions/checkout/pull/258) 72 | 73 | ## v2.1.1 74 | 75 | - Changes to support GHES ([here](https://github.com/actions/checkout/pull/236) and [here](https://github.com/actions/checkout/pull/248)) 76 | 77 | ## v2.1.0 78 | 79 | - [Group output](https://github.com/actions/checkout/pull/191) 80 | - [Changes to support GHES alpha release](https://github.com/actions/checkout/pull/199) 81 | - [Persist core.sshCommand for submodules](https://github.com/actions/checkout/pull/184) 82 | - [Add support ssh](https://github.com/actions/checkout/pull/163) 83 | - [Convert submodule SSH URL to HTTPS, when not using SSH](https://github.com/actions/checkout/pull/179) 84 | - [Add submodule support](https://github.com/actions/checkout/pull/157) 85 | - [Follow proxy settings](https://github.com/actions/checkout/pull/144) 86 | - [Fix ref for pr closed event when a pr is merged](https://github.com/actions/checkout/pull/141) 87 | - [Fix issue checking detached when git less than 2.22](https://github.com/actions/checkout/pull/128) 88 | 89 | ## v2.0.0 90 | 91 | - [Do not pass cred on command line](https://github.com/actions/checkout/pull/108) 92 | - [Add input persist-credentials](https://github.com/actions/checkout/pull/107) 93 | - [Fallback to REST API to download repo](https://github.com/actions/checkout/pull/104) 94 | 95 | ## v2 (beta) 96 | 97 | - Improved fetch performance 98 | - The default behavior now fetches only the SHA being checked-out 99 | - Script authenticated git commands 100 | - Persists `with.token` in the local git config 101 | - Enables your scripts to run authenticated git commands 102 | - Post-job cleanup removes the token 103 | - Coming soon: Opt out by setting `with.persist-credentials` to `false` 104 | - Creates a local branch 105 | - No longer detached HEAD when checking out a branch 106 | - A local branch is created with the corresponding upstream branch set 107 | - Improved layout 108 | - `with.path` is always relative to `github.workspace` 109 | - Aligns better with container actions, where `github.workspace` gets mapped in 110 | - Removed input `submodules` 111 | 112 | 113 | ## v1 114 | 115 | Refer [here](https://github.com/actions/checkout/blob/v1/CHANGELOG.md) for the V1 changelog 116 | -------------------------------------------------------------------------------- /__test__/input-helper.test.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as fsHelper from '../lib/fs-helper' 3 | import * as github from '@actions/github' 4 | import * as inputHelper from '../lib/input-helper' 5 | import * as path from 'path' 6 | import * as workflowContextHelper from '../lib/workflow-context-helper' 7 | import {IGitSourceSettings} from '../lib/git-source-settings' 8 | 9 | const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE'] 10 | const gitHubWorkspace = path.resolve('/checkout-tests/workspace') 11 | 12 | // Inputs for mock @actions/core 13 | let inputs = {} as any 14 | 15 | // Shallow clone original @actions/github context 16 | let originalContext = {...github.context} 17 | 18 | describe('input-helper tests', () => { 19 | beforeAll(() => { 20 | // Mock getInput 21 | jest.spyOn(core, 'getInput').mockImplementation((name: string) => { 22 | return inputs[name] 23 | }) 24 | 25 | // Mock error/warning/info/debug 26 | jest.spyOn(core, 'error').mockImplementation(jest.fn()) 27 | jest.spyOn(core, 'warning').mockImplementation(jest.fn()) 28 | jest.spyOn(core, 'info').mockImplementation(jest.fn()) 29 | jest.spyOn(core, 'debug').mockImplementation(jest.fn()) 30 | 31 | // Mock github context 32 | jest.spyOn(github.context, 'repo', 'get').mockImplementation(() => { 33 | return { 34 | owner: 'some-owner', 35 | repo: 'some-repo' 36 | } 37 | }) 38 | github.context.ref = 'refs/heads/some-ref' 39 | github.context.sha = '1234567890123456789012345678901234567890' 40 | 41 | // Mock ./fs-helper directoryExistsSync() 42 | jest 43 | .spyOn(fsHelper, 'directoryExistsSync') 44 | .mockImplementation((path: string) => path == gitHubWorkspace) 45 | 46 | // Mock ./workflowContextHelper getOrganizationId() 47 | jest 48 | .spyOn(workflowContextHelper, 'getOrganizationId') 49 | .mockImplementation(() => Promise.resolve(123456)) 50 | 51 | // GitHub workspace 52 | process.env['GITHUB_WORKSPACE'] = gitHubWorkspace 53 | }) 54 | 55 | beforeEach(() => { 56 | // Reset inputs 57 | inputs = {} 58 | }) 59 | 60 | afterAll(() => { 61 | // Restore GitHub workspace 62 | delete process.env['GITHUB_WORKSPACE'] 63 | if (originalGitHubWorkspace) { 64 | process.env['GITHUB_WORKSPACE'] = originalGitHubWorkspace 65 | } 66 | 67 | // Restore @actions/github context 68 | github.context.ref = originalContext.ref 69 | github.context.sha = originalContext.sha 70 | 71 | // Restore 72 | jest.restoreAllMocks() 73 | }) 74 | 75 | it('sets defaults', async () => { 76 | const settings: IGitSourceSettings = await inputHelper.getInputs() 77 | expect(settings).toBeTruthy() 78 | expect(settings.authToken).toBeFalsy() 79 | expect(settings.clean).toBe(true) 80 | expect(settings.commit).toBeTruthy() 81 | expect(settings.commit).toBe('1234567890123456789012345678901234567890') 82 | expect(settings.filter).toBe(undefined) 83 | expect(settings.sparseCheckout).toBe(undefined) 84 | expect(settings.sparseCheckoutConeMode).toBe(true) 85 | expect(settings.fetchDepth).toBe(1) 86 | expect(settings.fetchTags).toBe(false) 87 | expect(settings.showProgress).toBe(true) 88 | expect(settings.lfs).toBe(false) 89 | expect(settings.ref).toBe('refs/heads/some-ref') 90 | expect(settings.repositoryName).toBe('some-repo') 91 | expect(settings.repositoryOwner).toBe('some-owner') 92 | expect(settings.repositoryPath).toBe(gitHubWorkspace) 93 | expect(settings.setSafeDirectory).toBe(true) 94 | }) 95 | 96 | it('qualifies ref', async () => { 97 | let originalRef = github.context.ref 98 | try { 99 | github.context.ref = 'some-unqualified-ref' 100 | const settings: IGitSourceSettings = await inputHelper.getInputs() 101 | expect(settings).toBeTruthy() 102 | expect(settings.commit).toBe('1234567890123456789012345678901234567890') 103 | expect(settings.ref).toBe('refs/heads/some-unqualified-ref') 104 | } finally { 105 | github.context.ref = originalRef 106 | } 107 | }) 108 | 109 | it('requires qualified repo', async () => { 110 | inputs.repository = 'some-unqualified-repo' 111 | try { 112 | await inputHelper.getInputs() 113 | throw 'should not reach here' 114 | } catch (err) { 115 | expect(`(${(err as any).message}`).toMatch( 116 | "Invalid repository 'some-unqualified-repo'" 117 | ) 118 | } 119 | }) 120 | 121 | it('roots path', async () => { 122 | inputs.path = 'some-directory/some-subdirectory' 123 | const settings: IGitSourceSettings = await inputHelper.getInputs() 124 | expect(settings.repositoryPath).toBe( 125 | path.join(gitHubWorkspace, 'some-directory', 'some-subdirectory') 126 | ) 127 | }) 128 | 129 | it('sets ref to empty when explicit sha', async () => { 130 | inputs.ref = '1111111111222222222233333333334444444444' 131 | const settings: IGitSourceSettings = await inputHelper.getInputs() 132 | expect(settings.ref).toBeFalsy() 133 | expect(settings.commit).toBe('1111111111222222222233333333334444444444') 134 | }) 135 | 136 | it('sets sha to empty when explicit ref', async () => { 137 | inputs.ref = 'refs/heads/some-other-ref' 138 | const settings: IGitSourceSettings = await inputHelper.getInputs() 139 | expect(settings.ref).toBe('refs/heads/some-other-ref') 140 | expect(settings.commit).toBeFalsy() 141 | }) 142 | 143 | it('sets workflow organization ID', async () => { 144 | const settings: IGitSourceSettings = await inputHelper.getInputs() 145 | expect(settings.workflowOrganizationId).toBe(123456) 146 | }) 147 | }) 148 | -------------------------------------------------------------------------------- /src/input-helper.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as fsHelper from './fs-helper' 3 | import * as github from '@actions/github' 4 | import * as path from 'path' 5 | import * as workflowContextHelper from './workflow-context-helper' 6 | import {IGitSourceSettings} from './git-source-settings' 7 | 8 | export async function getInputs(): Promise { 9 | const result = ({} as unknown) as IGitSourceSettings 10 | 11 | // GitHub workspace 12 | let githubWorkspacePath = process.env['GITHUB_WORKSPACE'] 13 | if (!githubWorkspacePath) { 14 | throw new Error('GITHUB_WORKSPACE not defined') 15 | } 16 | githubWorkspacePath = path.resolve(githubWorkspacePath) 17 | core.debug(`GITHUB_WORKSPACE = '${githubWorkspacePath}'`) 18 | fsHelper.directoryExistsSync(githubWorkspacePath, true) 19 | 20 | // Qualified repository 21 | const qualifiedRepository = 22 | core.getInput('repository') || 23 | `${github.context.repo.owner}/${github.context.repo.repo}` 24 | core.debug(`qualified repository = '${qualifiedRepository}'`) 25 | const splitRepository = qualifiedRepository.split('/') 26 | if ( 27 | splitRepository.length !== 2 || 28 | !splitRepository[0] || 29 | !splitRepository[1] 30 | ) { 31 | throw new Error( 32 | `Invalid repository '${qualifiedRepository}'. Expected format {owner}/{repo}.` 33 | ) 34 | } 35 | result.repositoryOwner = splitRepository[0] 36 | result.repositoryName = splitRepository[1] 37 | 38 | // Repository path 39 | result.repositoryPath = core.getInput('path') || '.' 40 | result.repositoryPath = path.resolve( 41 | githubWorkspacePath, 42 | result.repositoryPath 43 | ) 44 | if ( 45 | !(result.repositoryPath + path.sep).startsWith( 46 | githubWorkspacePath + path.sep 47 | ) 48 | ) { 49 | throw new Error( 50 | `Repository path '${result.repositoryPath}' is not under '${githubWorkspacePath}'` 51 | ) 52 | } 53 | 54 | // Workflow repository? 55 | const isWorkflowRepository = 56 | qualifiedRepository.toUpperCase() === 57 | `${github.context.repo.owner}/${github.context.repo.repo}`.toUpperCase() 58 | 59 | // Source branch, source version 60 | result.ref = core.getInput('ref') 61 | if (!result.ref) { 62 | if (isWorkflowRepository) { 63 | result.ref = github.context.ref 64 | result.commit = github.context.sha 65 | 66 | // Some events have an unqualifed ref. For example when a PR is merged (pull_request closed event), 67 | // the ref is unqualifed like "main" instead of "refs/heads/main". 68 | if (result.commit && result.ref && !result.ref.startsWith('refs/')) { 69 | result.ref = `refs/heads/${result.ref}` 70 | } 71 | } 72 | } 73 | // SHA? 74 | else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) { 75 | result.commit = result.ref 76 | result.ref = '' 77 | } 78 | core.debug(`ref = '${result.ref}'`) 79 | core.debug(`commit = '${result.commit}'`) 80 | 81 | // Clean 82 | result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE' 83 | core.debug(`clean = ${result.clean}`) 84 | 85 | // Filter 86 | const filter = core.getInput('filter') 87 | if (filter) { 88 | result.filter = filter 89 | } 90 | 91 | core.debug(`filter = ${result.filter}`) 92 | 93 | // Sparse checkout 94 | const sparseCheckout = core.getMultilineInput('sparse-checkout') 95 | if (sparseCheckout.length) { 96 | result.sparseCheckout = sparseCheckout 97 | core.debug(`sparse checkout = ${result.sparseCheckout}`) 98 | } 99 | 100 | result.sparseCheckoutConeMode = 101 | (core.getInput('sparse-checkout-cone-mode') || 'true').toUpperCase() === 102 | 'TRUE' 103 | 104 | // Fetch depth 105 | result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1')) 106 | if (isNaN(result.fetchDepth) || result.fetchDepth < 0) { 107 | result.fetchDepth = 0 108 | } 109 | core.debug(`fetch depth = ${result.fetchDepth}`) 110 | 111 | // Fetch tags 112 | result.fetchTags = 113 | (core.getInput('fetch-tags') || 'false').toUpperCase() === 'TRUE' 114 | core.debug(`fetch tags = ${result.fetchTags}`) 115 | 116 | // Show fetch progress 117 | result.showProgress = 118 | (core.getInput('show-progress') || 'true').toUpperCase() === 'TRUE' 119 | core.debug(`show progress = ${result.showProgress}`) 120 | 121 | // LFS 122 | result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE' 123 | core.debug(`lfs = ${result.lfs}`) 124 | 125 | // Submodules 126 | result.submodules = false 127 | result.nestedSubmodules = false 128 | const submodulesString = (core.getInput('submodules') || '').toUpperCase() 129 | if (submodulesString == 'RECURSIVE') { 130 | result.submodules = true 131 | result.nestedSubmodules = true 132 | } else if (submodulesString == 'TRUE') { 133 | result.submodules = true 134 | } 135 | core.debug(`submodules = ${result.submodules}`) 136 | core.debug(`recursive submodules = ${result.nestedSubmodules}`) 137 | 138 | // Auth token 139 | result.authToken = core.getInput('token', {required: true}) 140 | 141 | // SSH 142 | result.sshKey = core.getInput('ssh-key') 143 | result.sshKnownHosts = core.getInput('ssh-known-hosts') 144 | result.sshStrict = 145 | (core.getInput('ssh-strict') || 'true').toUpperCase() === 'TRUE' 146 | 147 | // Persist credentials 148 | result.persistCredentials = 149 | (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE' 150 | 151 | // Workflow organization ID 152 | result.workflowOrganizationId = await workflowContextHelper.getOrganizationId() 153 | 154 | // Set safe.directory in git global config. 155 | result.setSafeDirectory = 156 | (core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE' 157 | 158 | // Determine the GitHub URL that the repository is being hosted from 159 | result.githubServerUrl = core.getInput('github-server-url') 160 | core.debug(`GitHub Host URL = ${result.githubServerUrl}`) 161 | 162 | return result 163 | } 164 | -------------------------------------------------------------------------------- /__test__/ref-helper.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert' 2 | import * as refHelper from '../lib/ref-helper' 3 | import {IGitCommandManager} from '../lib/git-command-manager' 4 | 5 | const commit = '1234567890123456789012345678901234567890' 6 | let git: IGitCommandManager 7 | 8 | describe('ref-helper tests', () => { 9 | beforeEach(() => { 10 | git = ({} as unknown) as IGitCommandManager 11 | }) 12 | 13 | it('getCheckoutInfo requires git', async () => { 14 | const git = (null as unknown) as IGitCommandManager 15 | try { 16 | await refHelper.getCheckoutInfo(git, 'refs/heads/my/branch', commit) 17 | throw new Error('Should not reach here') 18 | } catch (err) { 19 | expect((err as any)?.message).toBe('Arg git cannot be empty') 20 | } 21 | }) 22 | 23 | it('getCheckoutInfo requires ref or commit', async () => { 24 | try { 25 | await refHelper.getCheckoutInfo(git, '', '') 26 | throw new Error('Should not reach here') 27 | } catch (err) { 28 | expect((err as any)?.message).toBe( 29 | 'Args ref and commit cannot both be empty' 30 | ) 31 | } 32 | }) 33 | 34 | it('getCheckoutInfo sha only', async () => { 35 | const checkoutInfo = await refHelper.getCheckoutInfo(git, '', commit) 36 | expect(checkoutInfo.ref).toBe(commit) 37 | expect(checkoutInfo.startPoint).toBeFalsy() 38 | }) 39 | 40 | it('getCheckoutInfo refs/heads/', async () => { 41 | const checkoutInfo = await refHelper.getCheckoutInfo( 42 | git, 43 | 'refs/heads/my/branch', 44 | commit 45 | ) 46 | expect(checkoutInfo.ref).toBe('my/branch') 47 | expect(checkoutInfo.startPoint).toBe('refs/remotes/origin/my/branch') 48 | }) 49 | 50 | it('getCheckoutInfo refs/pull/', async () => { 51 | const checkoutInfo = await refHelper.getCheckoutInfo( 52 | git, 53 | 'refs/pull/123/merge', 54 | commit 55 | ) 56 | expect(checkoutInfo.ref).toBe('refs/remotes/pull/123/merge') 57 | expect(checkoutInfo.startPoint).toBeFalsy() 58 | }) 59 | 60 | it('getCheckoutInfo refs/tags/', async () => { 61 | const checkoutInfo = await refHelper.getCheckoutInfo( 62 | git, 63 | 'refs/tags/my-tag', 64 | commit 65 | ) 66 | expect(checkoutInfo.ref).toBe('refs/tags/my-tag') 67 | expect(checkoutInfo.startPoint).toBeFalsy() 68 | }) 69 | 70 | it('getCheckoutInfo unqualified branch only', async () => { 71 | git.branchExists = jest.fn(async (remote: boolean, pattern: string) => { 72 | return true 73 | }) 74 | 75 | const checkoutInfo = await refHelper.getCheckoutInfo(git, 'my/branch', '') 76 | 77 | expect(checkoutInfo.ref).toBe('my/branch') 78 | expect(checkoutInfo.startPoint).toBe('refs/remotes/origin/my/branch') 79 | }) 80 | 81 | it('getCheckoutInfo unqualified tag only', async () => { 82 | git.branchExists = jest.fn(async (remote: boolean, pattern: string) => { 83 | return false 84 | }) 85 | git.tagExists = jest.fn(async (pattern: string) => { 86 | return true 87 | }) 88 | 89 | const checkoutInfo = await refHelper.getCheckoutInfo(git, 'my-tag', '') 90 | 91 | expect(checkoutInfo.ref).toBe('refs/tags/my-tag') 92 | expect(checkoutInfo.startPoint).toBeFalsy() 93 | }) 94 | 95 | it('getCheckoutInfo unqualified ref only, not a branch or tag', async () => { 96 | git.branchExists = jest.fn(async (remote: boolean, pattern: string) => { 97 | return false 98 | }) 99 | git.tagExists = jest.fn(async (pattern: string) => { 100 | return false 101 | }) 102 | 103 | try { 104 | await refHelper.getCheckoutInfo(git, 'my-ref', '') 105 | throw new Error('Should not reach here') 106 | } catch (err) { 107 | expect((err as any)?.message).toBe( 108 | "A branch or tag with the name 'my-ref' could not be found" 109 | ) 110 | } 111 | }) 112 | 113 | it('getRefSpec requires ref or commit', async () => { 114 | assert.throws( 115 | () => refHelper.getRefSpec('', ''), 116 | /Args ref and commit cannot both be empty/ 117 | ) 118 | }) 119 | 120 | it('getRefSpec sha + refs/heads/', async () => { 121 | const refSpec = refHelper.getRefSpec('refs/heads/my/branch', commit) 122 | expect(refSpec.length).toBe(1) 123 | expect(refSpec[0]).toBe(`+${commit}:refs/remotes/origin/my/branch`) 124 | }) 125 | 126 | it('getRefSpec sha + refs/pull/', async () => { 127 | const refSpec = refHelper.getRefSpec('refs/pull/123/merge', commit) 128 | expect(refSpec.length).toBe(1) 129 | expect(refSpec[0]).toBe(`+${commit}:refs/remotes/pull/123/merge`) 130 | }) 131 | 132 | it('getRefSpec sha + refs/tags/', async () => { 133 | const refSpec = refHelper.getRefSpec('refs/tags/my-tag', commit) 134 | expect(refSpec.length).toBe(1) 135 | expect(refSpec[0]).toBe(`+${commit}:refs/tags/my-tag`) 136 | }) 137 | 138 | it('getRefSpec sha only', async () => { 139 | const refSpec = refHelper.getRefSpec('', commit) 140 | expect(refSpec.length).toBe(1) 141 | expect(refSpec[0]).toBe(commit) 142 | }) 143 | 144 | it('getRefSpec unqualified ref only', async () => { 145 | const refSpec = refHelper.getRefSpec('my-ref', '') 146 | expect(refSpec.length).toBe(2) 147 | expect(refSpec[0]).toBe('+refs/heads/my-ref*:refs/remotes/origin/my-ref*') 148 | expect(refSpec[1]).toBe('+refs/tags/my-ref*:refs/tags/my-ref*') 149 | }) 150 | 151 | it('getRefSpec refs/heads/ only', async () => { 152 | const refSpec = refHelper.getRefSpec('refs/heads/my/branch', '') 153 | expect(refSpec.length).toBe(1) 154 | expect(refSpec[0]).toBe( 155 | '+refs/heads/my/branch:refs/remotes/origin/my/branch' 156 | ) 157 | }) 158 | 159 | it('getRefSpec refs/pull/ only', async () => { 160 | const refSpec = refHelper.getRefSpec('refs/pull/123/merge', '') 161 | expect(refSpec.length).toBe(1) 162 | expect(refSpec[0]).toBe('+refs/pull/123/merge:refs/remotes/pull/123/merge') 163 | }) 164 | 165 | it('getRefSpec refs/tags/ only', async () => { 166 | const refSpec = refHelper.getRefSpec('refs/tags/my-tag', '') 167 | expect(refSpec.length).toBe(1) 168 | expect(refSpec[0]).toBe('+refs/tags/my-tag:refs/tags/my-tag') 169 | }) 170 | }) 171 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - releases/* 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 20.x 17 | - uses: actions/checkout@v3 18 | - run: npm ci 19 | - run: npm run build 20 | - run: npm run format-check 21 | - run: npm run lint 22 | - run: npm test 23 | - name: Verify no unstaged changes 24 | run: __test__/verify-no-unstaged-changes.sh 25 | 26 | test: 27 | strategy: 28 | matrix: 29 | runs-on: [ubuntu-latest, macos-latest, windows-latest] 30 | runs-on: ${{ matrix.runs-on }} 31 | 32 | steps: 33 | # Clone this repo 34 | - name: Checkout 35 | uses: actions/checkout@v3 36 | 37 | # Basic checkout 38 | - name: Checkout basic 39 | uses: ./ 40 | with: 41 | ref: test-data/v2/basic 42 | path: basic 43 | - name: Verify basic 44 | shell: bash 45 | run: __test__/verify-basic.sh 46 | 47 | # Clean 48 | - name: Modify work tree 49 | shell: bash 50 | run: __test__/modify-work-tree.sh 51 | - name: Checkout clean 52 | uses: ./ 53 | with: 54 | ref: test-data/v2/basic 55 | path: basic 56 | - name: Verify clean 57 | shell: bash 58 | run: __test__/verify-clean.sh 59 | 60 | # Side by side 61 | - name: Checkout side by side 1 62 | uses: ./ 63 | with: 64 | ref: test-data/v2/side-by-side-1 65 | path: side-by-side-1 66 | - name: Checkout side by side 2 67 | uses: ./ 68 | with: 69 | ref: test-data/v2/side-by-side-2 70 | path: side-by-side-2 71 | - name: Verify side by side 72 | shell: bash 73 | run: __test__/verify-side-by-side.sh 74 | 75 | # Filter 76 | - name: Fetch filter 77 | uses: ./ 78 | with: 79 | filter: 'blob:none' 80 | path: fetch-filter 81 | 82 | - name: Verify fetch filter 83 | run: __test__/verify-fetch-filter.sh 84 | 85 | # Sparse checkout 86 | - name: Sparse checkout 87 | uses: ./ 88 | with: 89 | sparse-checkout: | 90 | __test__ 91 | .github 92 | dist 93 | path: sparse-checkout 94 | 95 | - name: Verify sparse checkout 96 | run: __test__/verify-sparse-checkout.sh 97 | 98 | # Sparse checkout (non-cone mode) 99 | - name: Sparse checkout (non-cone mode) 100 | uses: ./ 101 | with: 102 | sparse-checkout: | 103 | /__test__/ 104 | /.github/ 105 | /dist/ 106 | sparse-checkout-cone-mode: false 107 | path: sparse-checkout-non-cone-mode 108 | 109 | - name: Verify sparse checkout (non-cone mode) 110 | run: __test__/verify-sparse-checkout-non-cone-mode.sh 111 | 112 | # LFS 113 | - name: Checkout LFS 114 | uses: ./ 115 | with: 116 | repository: actions/checkout # hardcoded, otherwise doesn't work from a fork 117 | ref: test-data/v2/lfs 118 | path: lfs 119 | lfs: true 120 | - name: Verify LFS 121 | shell: bash 122 | run: __test__/verify-lfs.sh 123 | 124 | # Submodules false 125 | - name: Checkout submodules false 126 | uses: ./ 127 | with: 128 | ref: test-data/v2/submodule-ssh-url 129 | path: submodules-false 130 | - name: Verify submodules false 131 | run: __test__/verify-submodules-false.sh 132 | 133 | # Submodules one level 134 | - name: Checkout submodules true 135 | uses: ./ 136 | with: 137 | ref: test-data/v2/submodule-ssh-url 138 | path: submodules-true 139 | submodules: true 140 | - name: Verify submodules true 141 | run: __test__/verify-submodules-true.sh 142 | 143 | # Submodules recursive 144 | - name: Checkout submodules recursive 145 | uses: ./ 146 | with: 147 | ref: test-data/v2/submodule-ssh-url 148 | path: submodules-recursive 149 | submodules: recursive 150 | - name: Verify submodules recursive 151 | run: __test__/verify-submodules-recursive.sh 152 | 153 | # Basic checkout using REST API 154 | - name: Remove basic 155 | if: runner.os != 'windows' 156 | run: rm -rf basic 157 | - name: Remove basic (Windows) 158 | if: runner.os == 'windows' 159 | shell: cmd 160 | run: rmdir /s /q basic 161 | - name: Override git version 162 | if: runner.os != 'windows' 163 | run: __test__/override-git-version.sh 164 | - name: Override git version (Windows) 165 | if: runner.os == 'windows' 166 | run: __test__\\override-git-version.cmd 167 | - name: Checkout basic using REST API 168 | uses: ./ 169 | with: 170 | ref: test-data/v2/basic 171 | path: basic 172 | - name: Verify basic 173 | run: __test__/verify-basic.sh --archive 174 | 175 | test-proxy: 176 | runs-on: ubuntu-latest 177 | container: 178 | image: alpine/git:latest 179 | options: --dns 127.0.0.1 180 | services: 181 | squid-proxy: 182 | image: ubuntu/squid:latest 183 | ports: 184 | - 3128:3128 185 | env: 186 | https_proxy: http://squid-proxy:3128 187 | steps: 188 | # Clone this repo 189 | - name: Checkout 190 | uses: actions/checkout@v3 191 | 192 | # Basic checkout using git 193 | - name: Checkout basic 194 | uses: ./ 195 | with: 196 | ref: test-data/v2/basic 197 | path: basic 198 | - name: Verify basic 199 | run: __test__/verify-basic.sh 200 | 201 | # Basic checkout using REST API 202 | - name: Remove basic 203 | run: rm -rf basic 204 | - name: Override git version 205 | run: __test__/override-git-version.sh 206 | - name: Basic checkout using REST API 207 | uses: ./ 208 | with: 209 | ref: test-data/v2/basic 210 | path: basic 211 | - name: Verify basic 212 | run: __test__/verify-basic.sh --archive 213 | 214 | test-bypass-proxy: 215 | runs-on: ubuntu-latest 216 | env: 217 | https_proxy: http://no-such-proxy:3128 218 | no_proxy: api.github.com,github.com 219 | steps: 220 | # Clone this repo 221 | - name: Checkout 222 | uses: actions/checkout@v3 223 | 224 | # Basic checkout using git 225 | - name: Checkout basic 226 | uses: ./ 227 | with: 228 | ref: test-data/v2/basic 229 | path: basic 230 | - name: Verify basic 231 | run: __test__/verify-basic.sh 232 | - name: Remove basic 233 | run: rm -rf basic 234 | 235 | # Basic checkout using REST API 236 | - name: Override git version 237 | run: __test__/override-git-version.sh 238 | - name: Checkout basic using REST API 239 | uses: ./ 240 | with: 241 | ref: test-data/v2/basic 242 | path: basic 243 | - name: Verify basic 244 | run: __test__/verify-basic.sh --archive 245 | 246 | test-git-container: 247 | runs-on: ubuntu-latest 248 | container: bitnami/git:latest 249 | steps: 250 | # Clone this repo 251 | - name: Checkout 252 | uses: actions/checkout@v3 253 | with: 254 | path: v3 255 | 256 | # Basic checkout using git 257 | - name: Checkout basic 258 | uses: ./v3 259 | with: 260 | ref: test-data/v2/basic 261 | - name: Verify basic 262 | run: | 263 | if [ ! -f "./basic-file.txt" ]; then 264 | echo "Expected basic file does not exist" 265 | exit 1 266 | fi 267 | 268 | # Verify .git folder 269 | if [ ! -d "./.git" ]; then 270 | echo "Expected ./.git folder to exist" 271 | exit 1 272 | fi 273 | 274 | # Verify auth token 275 | git config --global --add safe.directory "*" 276 | git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main 277 | 278 | # needed to make checkout post cleanup succeed 279 | - name: Fix Checkout v3 280 | uses: actions/checkout@v3 281 | with: 282 | path: v3 -------------------------------------------------------------------------------- /src/ref-helper.ts: -------------------------------------------------------------------------------- 1 | import {IGitCommandManager} from './git-command-manager' 2 | import * as core from '@actions/core' 3 | import * as github from '@actions/github' 4 | import {getServerApiUrl, isGhes} from './url-helper' 5 | 6 | export const tagsRefSpec = '+refs/tags/*:refs/tags/*' 7 | 8 | export interface ICheckoutInfo { 9 | ref: string 10 | startPoint: string 11 | } 12 | 13 | export async function getCheckoutInfo( 14 | git: IGitCommandManager, 15 | ref: string, 16 | commit: string 17 | ): Promise { 18 | if (!git) { 19 | throw new Error('Arg git cannot be empty') 20 | } 21 | 22 | if (!ref && !commit) { 23 | throw new Error('Args ref and commit cannot both be empty') 24 | } 25 | 26 | const result = ({} as unknown) as ICheckoutInfo 27 | const upperRef = (ref || '').toUpperCase() 28 | 29 | // SHA only 30 | if (!ref) { 31 | result.ref = commit 32 | } 33 | // refs/heads/ 34 | else if (upperRef.startsWith('REFS/HEADS/')) { 35 | const branch = ref.substring('refs/heads/'.length) 36 | result.ref = branch 37 | result.startPoint = `refs/remotes/origin/${branch}` 38 | } 39 | // refs/pull/ 40 | else if (upperRef.startsWith('REFS/PULL/')) { 41 | const branch = ref.substring('refs/pull/'.length) 42 | result.ref = `refs/remotes/pull/${branch}` 43 | } 44 | // refs/tags/ 45 | else if (upperRef.startsWith('REFS/')) { 46 | result.ref = ref 47 | } 48 | // Unqualified ref, check for a matching branch or tag 49 | else { 50 | if (await git.branchExists(true, `origin/${ref}`)) { 51 | result.ref = ref 52 | result.startPoint = `refs/remotes/origin/${ref}` 53 | } else if (await git.tagExists(`${ref}`)) { 54 | result.ref = `refs/tags/${ref}` 55 | } else { 56 | throw new Error( 57 | `A branch or tag with the name '${ref}' could not be found` 58 | ) 59 | } 60 | } 61 | 62 | return result 63 | } 64 | 65 | export function getRefSpecForAllHistory(ref: string, commit: string): string[] { 66 | const result = ['+refs/heads/*:refs/remotes/origin/*', tagsRefSpec] 67 | if (ref && ref.toUpperCase().startsWith('REFS/PULL/')) { 68 | const branch = ref.substring('refs/pull/'.length) 69 | result.push(`+${commit || ref}:refs/remotes/pull/${branch}`) 70 | } 71 | 72 | return result 73 | } 74 | 75 | export function getRefSpec(ref: string, commit: string): string[] { 76 | if (!ref && !commit) { 77 | throw new Error('Args ref and commit cannot both be empty') 78 | } 79 | 80 | const upperRef = (ref || '').toUpperCase() 81 | 82 | // SHA 83 | if (commit) { 84 | // refs/heads 85 | if (upperRef.startsWith('REFS/HEADS/')) { 86 | const branch = ref.substring('refs/heads/'.length) 87 | return [`+${commit}:refs/remotes/origin/${branch}`] 88 | } 89 | // refs/pull/ 90 | else if (upperRef.startsWith('REFS/PULL/')) { 91 | const branch = ref.substring('refs/pull/'.length) 92 | return [`+${commit}:refs/remotes/pull/${branch}`] 93 | } 94 | // refs/tags/ 95 | else if (upperRef.startsWith('REFS/TAGS/')) { 96 | return [`+${commit}:${ref}`] 97 | } 98 | // Otherwise no destination ref 99 | else { 100 | return [commit] 101 | } 102 | } 103 | // Unqualified ref, check for a matching branch or tag 104 | else if (!upperRef.startsWith('REFS/')) { 105 | return [ 106 | `+refs/heads/${ref}*:refs/remotes/origin/${ref}*`, 107 | `+refs/tags/${ref}*:refs/tags/${ref}*` 108 | ] 109 | } 110 | // refs/heads/ 111 | else if (upperRef.startsWith('REFS/HEADS/')) { 112 | const branch = ref.substring('refs/heads/'.length) 113 | return [`+${ref}:refs/remotes/origin/${branch}`] 114 | } 115 | // refs/pull/ 116 | else if (upperRef.startsWith('REFS/PULL/')) { 117 | const branch = ref.substring('refs/pull/'.length) 118 | return [`+${ref}:refs/remotes/pull/${branch}`] 119 | } 120 | // refs/tags/ 121 | else { 122 | return [`+${ref}:${ref}`] 123 | } 124 | } 125 | 126 | /** 127 | * Tests whether the initial fetch created the ref at the expected commit 128 | */ 129 | export async function testRef( 130 | git: IGitCommandManager, 131 | ref: string, 132 | commit: string 133 | ): Promise { 134 | if (!git) { 135 | throw new Error('Arg git cannot be empty') 136 | } 137 | 138 | if (!ref && !commit) { 139 | throw new Error('Args ref and commit cannot both be empty') 140 | } 141 | 142 | // No SHA? Nothing to test 143 | if (!commit) { 144 | return true 145 | } 146 | // SHA only? 147 | else if (!ref) { 148 | return await git.shaExists(commit) 149 | } 150 | 151 | const upperRef = ref.toUpperCase() 152 | 153 | // refs/heads/ 154 | if (upperRef.startsWith('REFS/HEADS/')) { 155 | const branch = ref.substring('refs/heads/'.length) 156 | return ( 157 | (await git.branchExists(true, `origin/${branch}`)) && 158 | commit === (await git.revParse(`refs/remotes/origin/${branch}`)) 159 | ) 160 | } 161 | // refs/pull/ 162 | else if (upperRef.startsWith('REFS/PULL/')) { 163 | // Assume matches because fetched using the commit 164 | return true 165 | } 166 | // refs/tags/ 167 | else if (upperRef.startsWith('REFS/TAGS/')) { 168 | const tagName = ref.substring('refs/tags/'.length) 169 | return ( 170 | (await git.tagExists(tagName)) && commit === (await git.revParse(ref)) 171 | ) 172 | } 173 | // Unexpected 174 | else { 175 | core.debug(`Unexpected ref format '${ref}' when testing ref info`) 176 | return true 177 | } 178 | } 179 | 180 | export async function checkCommitInfo( 181 | token: string, 182 | commitInfo: string, 183 | repositoryOwner: string, 184 | repositoryName: string, 185 | ref: string, 186 | commit: string, 187 | baseUrl?: string 188 | ): Promise { 189 | try { 190 | // GHES? 191 | if (isGhes(baseUrl)) { 192 | return 193 | } 194 | 195 | // Auth token? 196 | if (!token) { 197 | return 198 | } 199 | 200 | // Public PR synchronize, for workflow repo? 201 | if ( 202 | fromPayload('repository.private') !== false || 203 | github.context.eventName !== 'pull_request' || 204 | fromPayload('action') !== 'synchronize' || 205 | repositoryOwner !== github.context.repo.owner || 206 | repositoryName !== github.context.repo.repo || 207 | ref !== github.context.ref || 208 | !ref.startsWith('refs/pull/') || 209 | commit !== github.context.sha 210 | ) { 211 | return 212 | } 213 | 214 | // Head SHA 215 | const expectedHeadSha = fromPayload('after') 216 | if (!expectedHeadSha) { 217 | core.debug('Unable to determine head sha') 218 | return 219 | } 220 | 221 | // Base SHA 222 | const expectedBaseSha = fromPayload('pull_request.base.sha') 223 | if (!expectedBaseSha) { 224 | core.debug('Unable to determine base sha') 225 | return 226 | } 227 | 228 | // Expected message? 229 | const expectedMessage = `Merge ${expectedHeadSha} into ${expectedBaseSha}` 230 | if (commitInfo.indexOf(expectedMessage) >= 0) { 231 | return 232 | } 233 | 234 | // Extract details from message 235 | const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/) 236 | if (!match) { 237 | core.debug('Unexpected message format') 238 | return 239 | } 240 | 241 | // Post telemetry 242 | const actualHeadSha = match[1] 243 | if (actualHeadSha !== expectedHeadSha) { 244 | core.debug( 245 | `Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}` 246 | ) 247 | const octokit = github.getOctokit(token, { 248 | baseUrl: getServerApiUrl(baseUrl), 249 | userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload( 250 | 'number' 251 | )};run_id=${ 252 | process.env['GITHUB_RUN_ID'] 253 | };expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})` 254 | }) 255 | await octokit.rest.repos.get({ 256 | owner: repositoryOwner, 257 | repo: repositoryName 258 | }) 259 | } 260 | } catch (err) { 261 | core.debug( 262 | `Error when validating commit info: ${(err as any)?.stack ?? err}` 263 | ) 264 | } 265 | } 266 | 267 | function fromPayload(path: string): any { 268 | return select(github.context.payload, path) 269 | } 270 | 271 | function select(obj: any, path: string): any { 272 | if (!obj) { 273 | return undefined 274 | } 275 | 276 | const i = path.indexOf('.') 277 | if (i < 0) { 278 | return obj[path] 279 | } 280 | 281 | const key = path.substr(0, i) 282 | return select(obj[key], path.substr(i + 1)) 283 | } 284 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build and Test](https://github.com/actions/checkout/actions/workflows/test.yml/badge.svg)](https://github.com/actions/checkout/actions/workflows/test.yml) 2 | 3 | # Checkout V4 4 | 5 | This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it. 6 | 7 | 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://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events. 8 | 9 | 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. 10 | 11 | When Git 2.18 or higher is not in your PATH, falls back to the REST API to download the files. 12 | 13 | # What's new 14 | 15 | - Updated default runtime to node20 16 | - This requires a minimum Actions Runner version of [v2.308.0](https://github.com/actions/runner/releases/tag/v2.308.0). 17 | - Added support for fetching without the `--progress` option 18 | 19 | # Usage 20 | 21 | 22 | ```yaml 23 | - uses: actions/checkout@v4 24 | with: 25 | # Repository name with owner. For example, actions/checkout 26 | # Default: ${{ github.repository }} 27 | repository: '' 28 | 29 | # The branch, tag or SHA to checkout. When checking out the repository that 30 | # triggered a workflow, this defaults to the reference or SHA for that event. 31 | # Otherwise, uses the default branch. 32 | ref: '' 33 | 34 | # Personal access token (PAT) used to fetch the repository. The PAT is configured 35 | # with the local git config, which enables your scripts to run authenticated git 36 | # commands. The post-job step removes the PAT. 37 | # 38 | # We recommend using a service account with the least permissions necessary. Also 39 | # when generating a new PAT, select the least scopes necessary. 40 | # 41 | # [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) 42 | # 43 | # Default: ${{ github.token }} 44 | token: '' 45 | 46 | # SSH key used to fetch the repository. The SSH key is configured with the local 47 | # git config, which enables your scripts to run authenticated git commands. The 48 | # post-job step removes the SSH key. 49 | # 50 | # We recommend using a service account with the least permissions necessary. 51 | # 52 | # [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) 53 | ssh-key: '' 54 | 55 | # Known hosts in addition to the user and global host key database. The public SSH 56 | # keys for a host may be obtained using the utility `ssh-keyscan`. For example, 57 | # `ssh-keyscan github.com`. The public key for github.com is always implicitly 58 | # added. 59 | ssh-known-hosts: '' 60 | 61 | # Whether to perform strict host key checking. When true, adds the options 62 | # `StrictHostKeyChecking=yes` and `CheckHostIP=no` to the SSH command line. Use 63 | # the input `ssh-known-hosts` to configure additional hosts. 64 | # Default: true 65 | ssh-strict: '' 66 | 67 | # Whether to configure the token or SSH key with the local git config 68 | # Default: true 69 | persist-credentials: '' 70 | 71 | # Relative path under $GITHUB_WORKSPACE to place the repository 72 | path: '' 73 | 74 | # Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching 75 | # Default: true 76 | clean: '' 77 | 78 | # Partially clone against a given filter. Overrides sparse-checkout if set. 79 | # Default: null 80 | filter: '' 81 | 82 | # Do a sparse checkout on given patterns. Each pattern should be separated with 83 | # new lines. 84 | # Default: null 85 | sparse-checkout: '' 86 | 87 | # Specifies whether to use cone-mode when doing a sparse checkout. 88 | # Default: true 89 | sparse-checkout-cone-mode: '' 90 | 91 | # Number of commits to fetch. 0 indicates all history for all branches and tags. 92 | # Default: 1 93 | fetch-depth: '' 94 | 95 | # Whether to fetch tags, even if fetch-depth > 0. 96 | # Default: false 97 | fetch-tags: '' 98 | 99 | # Whether to show progress status output when fetching. 100 | # Default: true 101 | show-progress: '' 102 | 103 | # Whether to download Git-LFS files 104 | # Default: false 105 | lfs: '' 106 | 107 | # Whether to checkout submodules: `true` to checkout submodules or `recursive` to 108 | # recursively checkout submodules. 109 | # 110 | # When the `ssh-key` input is not provided, SSH URLs beginning with 111 | # `git@github.com:` are converted to HTTPS. 112 | # 113 | # Default: false 114 | submodules: '' 115 | 116 | # Add repository path as safe.directory for Git global config by running `git 117 | # config --global --add safe.directory ` 118 | # Default: true 119 | set-safe-directory: '' 120 | 121 | # The base URL for the GitHub instance that you are trying to clone from, will use 122 | # environment defaults to fetch from the same instance that the workflow is 123 | # running from unless specified. Example URLs are https://github.com or 124 | # https://my-ghes-server.example.com 125 | github-server-url: '' 126 | ``` 127 | 128 | 129 | # Scenarios 130 | 131 | - [Fetch only the root files](#Fetch-only-the-root-files) 132 | - [Fetch only the root files and `.github` and `src` folder](#Fetch-only-the-root-files-and-github-and-src-folder) 133 | - [Fetch only a single file](#Fetch-only-a-single-file) 134 | - [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches) 135 | - [Checkout a different branch](#Checkout-a-different-branch) 136 | - [Checkout HEAD^](#Checkout-HEAD) 137 | - [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side) 138 | - [Checkout multiple repos (nested)](#Checkout-multiple-repos-nested) 139 | - [Checkout multiple repos (private)](#Checkout-multiple-repos-private) 140 | - [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit) 141 | - [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event) 142 | - [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token) 143 | 144 | ## Fetch only the root files 145 | 146 | ```yaml 147 | - uses: actions/checkout@v4 148 | with: 149 | sparse-checkout: . 150 | ``` 151 | 152 | ## Fetch only the root files and `.github` and `src` folder 153 | 154 | ```yaml 155 | - uses: actions/checkout@v4 156 | with: 157 | sparse-checkout: | 158 | .github 159 | src 160 | ``` 161 | 162 | ## Fetch only a single file 163 | 164 | ```yaml 165 | - uses: actions/checkout@v4 166 | with: 167 | sparse-checkout: | 168 | README.md 169 | sparse-checkout-cone-mode: false 170 | ``` 171 | 172 | ## Fetch all history for all tags and branches 173 | 174 | ```yaml 175 | - uses: actions/checkout@v4 176 | with: 177 | fetch-depth: 0 178 | ``` 179 | 180 | ## Checkout a different branch 181 | 182 | ```yaml 183 | - uses: actions/checkout@v4 184 | with: 185 | ref: my-branch 186 | ``` 187 | 188 | ## Checkout HEAD^ 189 | 190 | ```yaml 191 | - uses: actions/checkout@v4 192 | with: 193 | fetch-depth: 2 194 | - run: git checkout HEAD^ 195 | ``` 196 | 197 | ## Checkout multiple repos (side by side) 198 | 199 | ```yaml 200 | - name: Checkout 201 | uses: actions/checkout@v4 202 | with: 203 | path: main 204 | 205 | - name: Checkout tools repo 206 | uses: actions/checkout@v4 207 | with: 208 | repository: my-org/my-tools 209 | path: my-tools 210 | ``` 211 | > - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private) 212 | 213 | ## Checkout multiple repos (nested) 214 | 215 | ```yaml 216 | - name: Checkout 217 | uses: actions/checkout@v4 218 | 219 | - name: Checkout tools repo 220 | uses: actions/checkout@v4 221 | with: 222 | repository: my-org/my-tools 223 | path: my-tools 224 | ``` 225 | > - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private) 226 | 227 | ## Checkout multiple repos (private) 228 | 229 | ```yaml 230 | - name: Checkout 231 | uses: actions/checkout@v4 232 | with: 233 | path: main 234 | 235 | - name: Checkout private tools 236 | uses: actions/checkout@v4 237 | with: 238 | repository: my-org/my-private-tools 239 | token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT 240 | path: my-tools 241 | ``` 242 | 243 | > - `${{ github.token }}` is scoped to the current repository, so if you want to checkout a different repository that is private you will need to provide your own [PAT](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line). 244 | 245 | 246 | ## Checkout pull request HEAD commit instead of merge commit 247 | 248 | ```yaml 249 | - uses: actions/checkout@v4 250 | with: 251 | ref: ${{ github.event.pull_request.head.sha }} 252 | ``` 253 | 254 | ## Checkout pull request on closed event 255 | 256 | ```yaml 257 | on: 258 | pull_request: 259 | branches: [main] 260 | types: [opened, synchronize, closed] 261 | jobs: 262 | build: 263 | runs-on: ubuntu-latest 264 | steps: 265 | - uses: actions/checkout@v4 266 | ``` 267 | 268 | ## Push a commit using the built-in token 269 | 270 | ```yaml 271 | on: push 272 | jobs: 273 | build: 274 | runs-on: ubuntu-latest 275 | steps: 276 | - uses: actions/checkout@v4 277 | - run: | 278 | date > generated.txt 279 | git config user.name github-actions 280 | git config user.email github-actions@github.com 281 | git add . 282 | git commit -m "generated" 283 | git push 284 | ``` 285 | 286 | # License 287 | 288 | The scripts and documentation in this project are released under the [MIT License](LICENSE) 289 | -------------------------------------------------------------------------------- /__test__/git-command-manager.test.ts: -------------------------------------------------------------------------------- 1 | import * as exec from '@actions/exec' 2 | import * as fshelper from '../lib/fs-helper' 3 | import * as commandManager from '../lib/git-command-manager' 4 | 5 | let git: commandManager.IGitCommandManager 6 | let mockExec = jest.fn() 7 | 8 | describe('git-auth-helper tests', () => { 9 | beforeAll(async () => {}) 10 | 11 | beforeEach(async () => { 12 | jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) 13 | jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn()) 14 | }) 15 | 16 | afterEach(() => { 17 | jest.restoreAllMocks() 18 | }) 19 | 20 | afterAll(() => {}) 21 | 22 | it('branch list matches', async () => { 23 | mockExec.mockImplementation((path, args, options) => { 24 | console.log(args, options.listeners.stdout) 25 | 26 | if (args.includes('version')) { 27 | options.listeners.stdout(Buffer.from('2.18')) 28 | return 0 29 | } 30 | 31 | if (args.includes('rev-parse')) { 32 | options.listeners.stdline(Buffer.from('refs/heads/foo')) 33 | options.listeners.stdline(Buffer.from('refs/heads/bar')) 34 | return 0 35 | } 36 | 37 | return 1 38 | }) 39 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 40 | const workingDirectory = 'test' 41 | const lfs = false 42 | const doSparseCheckout = false 43 | git = await commandManager.createCommandManager( 44 | workingDirectory, 45 | lfs, 46 | doSparseCheckout 47 | ) 48 | 49 | let branches = await git.branchList(false) 50 | 51 | expect(branches).toHaveLength(2) 52 | expect(branches.sort()).toEqual(['foo', 'bar'].sort()) 53 | }) 54 | 55 | it('ambiguous ref name output is captured', async () => { 56 | mockExec.mockImplementation((path, args, options) => { 57 | console.log(args, options.listeners.stdout) 58 | 59 | if (args.includes('version')) { 60 | options.listeners.stdout(Buffer.from('2.18')) 61 | return 0 62 | } 63 | 64 | if (args.includes('rev-parse')) { 65 | options.listeners.stdline(Buffer.from('refs/heads/foo')) 66 | // If refs/tags/v1 and refs/heads/tags/v1 existed on this repository 67 | options.listeners.errline( 68 | Buffer.from("error: refname 'tags/v1' is ambiguous") 69 | ) 70 | return 0 71 | } 72 | 73 | return 1 74 | }) 75 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 76 | const workingDirectory = 'test' 77 | const lfs = false 78 | const doSparseCheckout = false 79 | git = await commandManager.createCommandManager( 80 | workingDirectory, 81 | lfs, 82 | doSparseCheckout 83 | ) 84 | 85 | let branches = await git.branchList(false) 86 | 87 | expect(branches).toHaveLength(1) 88 | expect(branches.sort()).toEqual(['foo'].sort()) 89 | }) 90 | }) 91 | 92 | describe('Test fetchDepth and fetchTags options', () => { 93 | beforeEach(async () => { 94 | jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) 95 | jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn()) 96 | mockExec.mockImplementation((path, args, options) => { 97 | console.log(args, options.listeners.stdout) 98 | 99 | if (args.includes('version')) { 100 | options.listeners.stdout(Buffer.from('2.18')) 101 | } 102 | 103 | return 0 104 | }) 105 | }) 106 | 107 | afterEach(() => { 108 | jest.restoreAllMocks() 109 | }) 110 | 111 | it('should call execGit with the correct arguments when fetchDepth is 0 and fetchTags is true', async () => { 112 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 113 | const workingDirectory = 'test' 114 | const lfs = false 115 | const doSparseCheckout = false 116 | git = await commandManager.createCommandManager( 117 | workingDirectory, 118 | lfs, 119 | doSparseCheckout 120 | ) 121 | 122 | const refSpec = ['refspec1', 'refspec2'] 123 | const options = { 124 | filter: 'filterValue', 125 | fetchDepth: 0, 126 | fetchTags: true 127 | } 128 | 129 | await git.fetch(refSpec, options) 130 | 131 | expect(mockExec).toHaveBeenCalledWith( 132 | expect.any(String), 133 | [ 134 | '-c', 135 | 'protocol.version=2', 136 | 'fetch', 137 | '--prune', 138 | '--no-recurse-submodules', 139 | '--filter=filterValue', 140 | 'origin', 141 | 'refspec1', 142 | 'refspec2' 143 | ], 144 | expect.any(Object) 145 | ) 146 | }) 147 | 148 | it('should call execGit with the correct arguments when fetchDepth is 0 and fetchTags is false', async () => { 149 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 150 | 151 | const workingDirectory = 'test' 152 | const lfs = false 153 | const doSparseCheckout = false 154 | git = await commandManager.createCommandManager( 155 | workingDirectory, 156 | lfs, 157 | doSparseCheckout 158 | ) 159 | const refSpec = ['refspec1', 'refspec2'] 160 | const options = { 161 | filter: 'filterValue', 162 | fetchDepth: 0, 163 | fetchTags: false 164 | } 165 | 166 | await git.fetch(refSpec, options) 167 | 168 | expect(mockExec).toHaveBeenCalledWith( 169 | expect.any(String), 170 | [ 171 | '-c', 172 | 'protocol.version=2', 173 | 'fetch', 174 | '--no-tags', 175 | '--prune', 176 | '--no-recurse-submodules', 177 | '--filter=filterValue', 178 | 'origin', 179 | 'refspec1', 180 | 'refspec2' 181 | ], 182 | expect.any(Object) 183 | ) 184 | }) 185 | 186 | it('should call execGit with the correct arguments when fetchDepth is 1 and fetchTags is false', async () => { 187 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 188 | 189 | const workingDirectory = 'test' 190 | const lfs = false 191 | const doSparseCheckout = false 192 | git = await commandManager.createCommandManager( 193 | workingDirectory, 194 | lfs, 195 | doSparseCheckout 196 | ) 197 | const refSpec = ['refspec1', 'refspec2'] 198 | const options = { 199 | filter: 'filterValue', 200 | fetchDepth: 1, 201 | fetchTags: false 202 | } 203 | 204 | await git.fetch(refSpec, options) 205 | 206 | expect(mockExec).toHaveBeenCalledWith( 207 | expect.any(String), 208 | [ 209 | '-c', 210 | 'protocol.version=2', 211 | 'fetch', 212 | '--no-tags', 213 | '--prune', 214 | '--no-recurse-submodules', 215 | '--filter=filterValue', 216 | '--depth=1', 217 | 'origin', 218 | 'refspec1', 219 | 'refspec2' 220 | ], 221 | expect.any(Object) 222 | ) 223 | }) 224 | 225 | it('should call execGit with the correct arguments when fetchDepth is 1 and fetchTags is true', async () => { 226 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 227 | 228 | const workingDirectory = 'test' 229 | const lfs = false 230 | const doSparseCheckout = false 231 | git = await commandManager.createCommandManager( 232 | workingDirectory, 233 | lfs, 234 | doSparseCheckout 235 | ) 236 | const refSpec = ['refspec1', 'refspec2'] 237 | const options = { 238 | filter: 'filterValue', 239 | fetchDepth: 1, 240 | fetchTags: true 241 | } 242 | 243 | await git.fetch(refSpec, options) 244 | 245 | expect(mockExec).toHaveBeenCalledWith( 246 | expect.any(String), 247 | [ 248 | '-c', 249 | 'protocol.version=2', 250 | 'fetch', 251 | '--prune', 252 | '--no-recurse-submodules', 253 | '--filter=filterValue', 254 | '--depth=1', 255 | 'origin', 256 | 'refspec1', 257 | 'refspec2' 258 | ], 259 | expect.any(Object) 260 | ) 261 | }) 262 | 263 | it('should call execGit with the correct arguments when showProgress is true', async () => { 264 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 265 | 266 | const workingDirectory = 'test' 267 | const lfs = false 268 | const doSparseCheckout = false 269 | git = await commandManager.createCommandManager( 270 | workingDirectory, 271 | lfs, 272 | doSparseCheckout 273 | ) 274 | const refSpec = ['refspec1', 'refspec2'] 275 | const options = { 276 | filter: 'filterValue', 277 | showProgress: true 278 | } 279 | 280 | await git.fetch(refSpec, options) 281 | 282 | expect(mockExec).toHaveBeenCalledWith( 283 | expect.any(String), 284 | [ 285 | '-c', 286 | 'protocol.version=2', 287 | 'fetch', 288 | '--no-tags', 289 | '--prune', 290 | '--no-recurse-submodules', 291 | '--progress', 292 | '--filter=filterValue', 293 | 'origin', 294 | 'refspec1', 295 | 'refspec2' 296 | ], 297 | expect.any(Object) 298 | ) 299 | }) 300 | 301 | it('should call execGit with the correct arguments when fetchDepth is 42 and showProgress is true', async () => { 302 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 303 | 304 | const workingDirectory = 'test' 305 | const lfs = false 306 | const doSparseCheckout = false 307 | git = await commandManager.createCommandManager( 308 | workingDirectory, 309 | lfs, 310 | doSparseCheckout 311 | ) 312 | const refSpec = ['refspec1', 'refspec2'] 313 | const options = { 314 | filter: 'filterValue', 315 | fetchDepth: 42, 316 | showProgress: true 317 | } 318 | 319 | await git.fetch(refSpec, options) 320 | 321 | expect(mockExec).toHaveBeenCalledWith( 322 | expect.any(String), 323 | [ 324 | '-c', 325 | 'protocol.version=2', 326 | 'fetch', 327 | '--no-tags', 328 | '--prune', 329 | '--no-recurse-submodules', 330 | '--progress', 331 | '--filter=filterValue', 332 | '--depth=42', 333 | 'origin', 334 | 'refspec1', 335 | 'refspec2' 336 | ], 337 | expect.any(Object) 338 | ) 339 | }) 340 | 341 | it('should call execGit with the correct arguments when fetchTags is true and showProgress is true', async () => { 342 | jest.spyOn(exec, 'exec').mockImplementation(mockExec) 343 | 344 | const workingDirectory = 'test' 345 | const lfs = false 346 | const doSparseCheckout = false 347 | git = await commandManager.createCommandManager( 348 | workingDirectory, 349 | lfs, 350 | doSparseCheckout 351 | ) 352 | const refSpec = ['refspec1', 'refspec2'] 353 | const options = { 354 | filter: 'filterValue', 355 | fetchTags: true, 356 | showProgress: true 357 | } 358 | 359 | await git.fetch(refSpec, options) 360 | 361 | expect(mockExec).toHaveBeenCalledWith( 362 | expect.any(String), 363 | [ 364 | '-c', 365 | 'protocol.version=2', 366 | 'fetch', 367 | '--prune', 368 | '--no-recurse-submodules', 369 | '--progress', 370 | '--filter=filterValue', 371 | 'origin', 372 | 'refspec1', 373 | 'refspec2' 374 | ], 375 | expect.any(Object) 376 | ) 377 | }) 378 | }) 379 | -------------------------------------------------------------------------------- /src/git-source-provider.ts: -------------------------------------------------------------------------------- 1 | import * as core from '@actions/core' 2 | import * as fsHelper from './fs-helper' 3 | import * as gitAuthHelper from './git-auth-helper' 4 | import * as gitCommandManager from './git-command-manager' 5 | import * as gitDirectoryHelper from './git-directory-helper' 6 | import * as githubApiHelper from './github-api-helper' 7 | import * as io from '@actions/io' 8 | import * as path from 'path' 9 | import * as refHelper from './ref-helper' 10 | import * as stateHelper from './state-helper' 11 | import * as urlHelper from './url-helper' 12 | import {IGitCommandManager} from './git-command-manager' 13 | import {IGitSourceSettings} from './git-source-settings' 14 | 15 | export async function getSource(settings: IGitSourceSettings): Promise { 16 | // Repository URL 17 | core.info( 18 | `Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}` 19 | ) 20 | const repositoryUrl = urlHelper.getFetchUrl(settings) 21 | 22 | // Remove conflicting file path 23 | if (fsHelper.fileExistsSync(settings.repositoryPath)) { 24 | await io.rmRF(settings.repositoryPath) 25 | } 26 | 27 | // Create directory 28 | let isExisting = true 29 | if (!fsHelper.directoryExistsSync(settings.repositoryPath)) { 30 | isExisting = false 31 | await io.mkdirP(settings.repositoryPath) 32 | } 33 | 34 | // Git command manager 35 | core.startGroup('Getting Git version info') 36 | const git = await getGitCommandManager(settings) 37 | core.endGroup() 38 | 39 | let authHelper: gitAuthHelper.IGitAuthHelper | null = null 40 | try { 41 | if (git) { 42 | authHelper = gitAuthHelper.createAuthHelper(git, settings) 43 | if (settings.setSafeDirectory) { 44 | // 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 45 | // Otherwise all git commands we run in a container fail 46 | await authHelper.configureTempGlobalConfig() 47 | core.info( 48 | `Adding repository directory to the temporary git global config as a safe directory` 49 | ) 50 | 51 | await git 52 | .config('safe.directory', settings.repositoryPath, true, true) 53 | .catch(error => { 54 | core.info( 55 | `Failed to initialize safe directory with error: ${error}` 56 | ) 57 | }) 58 | 59 | stateHelper.setSafeDirectory() 60 | } 61 | } 62 | 63 | // Prepare existing directory, otherwise recreate 64 | if (isExisting) { 65 | await gitDirectoryHelper.prepareExistingDirectory( 66 | git, 67 | settings.repositoryPath, 68 | repositoryUrl, 69 | settings.clean, 70 | settings.ref 71 | ) 72 | } 73 | 74 | if (!git) { 75 | // Downloading using REST API 76 | core.info(`The repository will be downloaded using the GitHub REST API`) 77 | core.info( 78 | `To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH` 79 | ) 80 | if (settings.submodules) { 81 | throw new Error( 82 | `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.` 83 | ) 84 | } else if (settings.sshKey) { 85 | throw new Error( 86 | `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.` 87 | ) 88 | } 89 | 90 | await githubApiHelper.downloadRepository( 91 | settings.authToken, 92 | settings.repositoryOwner, 93 | settings.repositoryName, 94 | settings.ref, 95 | settings.commit, 96 | settings.repositoryPath, 97 | settings.githubServerUrl 98 | ) 99 | return 100 | } 101 | 102 | // Save state for POST action 103 | stateHelper.setRepositoryPath(settings.repositoryPath) 104 | 105 | // Initialize the repository 106 | if ( 107 | !fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git')) 108 | ) { 109 | core.startGroup('Initializing the repository') 110 | await git.init() 111 | await git.remoteAdd('origin', repositoryUrl) 112 | core.endGroup() 113 | } 114 | 115 | // Disable automatic garbage collection 116 | core.startGroup('Disabling automatic garbage collection') 117 | if (!(await git.tryDisableAutomaticGarbageCollection())) { 118 | core.warning( 119 | `Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.` 120 | ) 121 | } 122 | core.endGroup() 123 | 124 | // If we didn't initialize it above, do it now 125 | if (!authHelper) { 126 | authHelper = gitAuthHelper.createAuthHelper(git, settings) 127 | } 128 | // Configure auth 129 | core.startGroup('Setting up auth') 130 | await authHelper.configureAuth() 131 | core.endGroup() 132 | 133 | // Determine the default branch 134 | if (!settings.ref && !settings.commit) { 135 | core.startGroup('Determining the default branch') 136 | if (settings.sshKey) { 137 | settings.ref = await git.getDefaultBranch(repositoryUrl) 138 | } else { 139 | settings.ref = await githubApiHelper.getDefaultBranch( 140 | settings.authToken, 141 | settings.repositoryOwner, 142 | settings.repositoryName, 143 | settings.githubServerUrl 144 | ) 145 | } 146 | core.endGroup() 147 | } 148 | 149 | // LFS install 150 | if (settings.lfs) { 151 | await git.lfsInstall() 152 | } 153 | 154 | // Fetch 155 | core.startGroup('Fetching the repository') 156 | const fetchOptions: { 157 | filter?: string 158 | fetchDepth?: number 159 | fetchTags?: boolean 160 | showProgress?: boolean 161 | } = {} 162 | 163 | if (settings.filter) { 164 | fetchOptions.filter = settings.filter 165 | } else if (settings.sparseCheckout) { 166 | fetchOptions.filter = 'blob:none' 167 | } 168 | 169 | if (settings.fetchDepth <= 0) { 170 | // Fetch all branches and tags 171 | let refSpec = refHelper.getRefSpecForAllHistory( 172 | settings.ref, 173 | settings.commit 174 | ) 175 | await git.fetch(refSpec, fetchOptions) 176 | 177 | // When all history is fetched, the ref we're interested in may have moved to a different 178 | // commit (push or force push). If so, fetch again with a targeted refspec. 179 | if (!(await refHelper.testRef(git, settings.ref, settings.commit))) { 180 | refSpec = refHelper.getRefSpec(settings.ref, settings.commit) 181 | await git.fetch(refSpec, fetchOptions) 182 | } 183 | } else { 184 | fetchOptions.fetchDepth = settings.fetchDepth 185 | fetchOptions.fetchTags = settings.fetchTags 186 | const refSpec = refHelper.getRefSpec(settings.ref, settings.commit) 187 | await git.fetch(refSpec, fetchOptions) 188 | } 189 | core.endGroup() 190 | 191 | // Checkout info 192 | core.startGroup('Determining the checkout info') 193 | const checkoutInfo = await refHelper.getCheckoutInfo( 194 | git, 195 | settings.ref, 196 | settings.commit 197 | ) 198 | core.endGroup() 199 | 200 | // LFS fetch 201 | // Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time). 202 | // Explicit lfs fetch will fetch lfs objects in parallel. 203 | // For sparse checkouts, let `checkout` fetch the needed objects lazily. 204 | if (settings.lfs && !settings.sparseCheckout) { 205 | core.startGroup('Fetching LFS objects') 206 | await git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref) 207 | core.endGroup() 208 | } 209 | 210 | // Sparse checkout 211 | if (settings.sparseCheckout) { 212 | core.startGroup('Setting up sparse checkout') 213 | if (settings.sparseCheckoutConeMode) { 214 | await git.sparseCheckout(settings.sparseCheckout) 215 | } else { 216 | await git.sparseCheckoutNonConeMode(settings.sparseCheckout) 217 | } 218 | core.endGroup() 219 | } 220 | 221 | // Checkout 222 | core.startGroup('Checking out the ref') 223 | await git.checkout(checkoutInfo.ref, checkoutInfo.startPoint) 224 | core.endGroup() 225 | 226 | // Submodules 227 | if (settings.submodules) { 228 | // Temporarily override global config 229 | core.startGroup('Setting up auth for fetching submodules') 230 | await authHelper.configureGlobalAuth() 231 | core.endGroup() 232 | 233 | // Checkout submodules 234 | core.startGroup('Fetching submodules') 235 | await git.submoduleSync(settings.nestedSubmodules) 236 | await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules) 237 | await git.submoduleForeach( 238 | 'git config --local gc.auto 0', 239 | settings.nestedSubmodules 240 | ) 241 | core.endGroup() 242 | 243 | // Persist credentials 244 | if (settings.persistCredentials) { 245 | core.startGroup('Persisting credentials for submodules') 246 | await authHelper.configureSubmoduleAuth() 247 | core.endGroup() 248 | } 249 | } 250 | 251 | // Get commit information 252 | const commitInfo = await git.log1() 253 | 254 | // Log commit sha 255 | await git.log1("--format='%H'") 256 | 257 | // Check for incorrect pull request merge commit 258 | await refHelper.checkCommitInfo( 259 | settings.authToken, 260 | commitInfo, 261 | settings.repositoryOwner, 262 | settings.repositoryName, 263 | settings.ref, 264 | settings.commit, 265 | settings.githubServerUrl 266 | ) 267 | } finally { 268 | // Remove auth 269 | if (authHelper) { 270 | if (!settings.persistCredentials) { 271 | core.startGroup('Removing auth') 272 | await authHelper.removeAuth() 273 | core.endGroup() 274 | } 275 | authHelper.removeGlobalConfig() 276 | } 277 | } 278 | } 279 | 280 | export async function cleanup(repositoryPath: string): Promise { 281 | // Repo exists? 282 | if ( 283 | !repositoryPath || 284 | !fsHelper.fileExistsSync(path.join(repositoryPath, '.git', 'config')) 285 | ) { 286 | return 287 | } 288 | 289 | let git: IGitCommandManager 290 | try { 291 | git = await gitCommandManager.createCommandManager( 292 | repositoryPath, 293 | false, 294 | false 295 | ) 296 | } catch { 297 | return 298 | } 299 | 300 | // Remove auth 301 | const authHelper = gitAuthHelper.createAuthHelper(git) 302 | try { 303 | if (stateHelper.PostSetSafeDirectory) { 304 | // 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 305 | // Otherwise all git commands we run in a container fail 306 | await authHelper.configureTempGlobalConfig() 307 | core.info( 308 | `Adding repository directory to the temporary git global config as a safe directory` 309 | ) 310 | 311 | await git 312 | .config('safe.directory', repositoryPath, true, true) 313 | .catch(error => { 314 | core.info(`Failed to initialize safe directory with error: ${error}`) 315 | }) 316 | } 317 | 318 | await authHelper.removeAuth() 319 | } finally { 320 | await authHelper.removeGlobalConfig() 321 | } 322 | } 323 | 324 | async function getGitCommandManager( 325 | settings: IGitSourceSettings 326 | ): Promise { 327 | core.info(`Working directory is '${settings.repositoryPath}'`) 328 | try { 329 | return await gitCommandManager.createCommandManager( 330 | settings.repositoryPath, 331 | settings.lfs, 332 | settings.sparseCheckout != null 333 | ) 334 | } catch (err) { 335 | // Git is required for LFS 336 | if (settings.lfs) { 337 | throw err 338 | } 339 | 340 | // Otherwise fallback to REST API 341 | return undefined 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /.licenses/npm/before-after-hook.dep.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: before-after-hook 3 | version: 2.2.3 4 | type: npm 5 | summary: asynchronous before/error/after hooks for internal functionality 6 | homepage: 7 | license: apache-2.0 8 | licenses: 9 | - sources: LICENSE 10 | text: |2 11 | Apache License 12 | Version 2.0, January 2004 13 | http://www.apache.org/licenses/ 14 | 15 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 16 | 17 | 1. Definitions. 18 | 19 | "License" shall mean the terms and conditions for use, reproduction, 20 | and distribution as defined by Sections 1 through 9 of this document. 21 | 22 | "Licensor" shall mean the copyright owner or entity authorized by 23 | the copyright owner that is granting the License. 24 | 25 | "Legal Entity" shall mean the union of the acting entity and all 26 | other entities that control, are controlled by, or are under common 27 | control with that entity. For the purposes of this definition, 28 | "control" means (i) the power, direct or indirect, to cause the 29 | direction or management of such entity, whether by contract or 30 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 31 | outstanding shares, or (iii) beneficial ownership of such entity. 32 | 33 | "You" (or "Your") shall mean an individual or Legal Entity 34 | exercising permissions granted by this License. 35 | 36 | "Source" form shall mean the preferred form for making modifications, 37 | including but not limited to software source code, documentation 38 | source, and configuration files. 39 | 40 | "Object" form shall mean any form resulting from mechanical 41 | transformation or translation of a Source form, including but 42 | not limited to compiled object code, generated documentation, 43 | and conversions to other media types. 44 | 45 | "Work" shall mean the work of authorship, whether in Source or 46 | Object form, made available under the License, as indicated by a 47 | copyright notice that is included in or attached to the work 48 | (an example is provided in the Appendix below). 49 | 50 | "Derivative Works" shall mean any work, whether in Source or Object 51 | form, that is based on (or derived from) the Work and for which the 52 | editorial revisions, annotations, elaborations, or other modifications 53 | represent, as a whole, an original work of authorship. For the purposes 54 | of this License, Derivative Works shall not include works that remain 55 | separable from, or merely link (or bind by name) to the interfaces of, 56 | the Work and Derivative Works thereof. 57 | 58 | "Contribution" shall mean any work of authorship, including 59 | the original version of the Work and any modifications or additions 60 | to that Work or Derivative Works thereof, that is intentionally 61 | submitted to Licensor for inclusion in the Work by the copyright owner 62 | or by an individual or Legal Entity authorized to submit on behalf of 63 | the copyright owner. For the purposes of this definition, "submitted" 64 | means any form of electronic, verbal, or written communication sent 65 | to the Licensor or its representatives, including but not limited to 66 | communication on electronic mailing lists, source code control systems, 67 | and issue tracking systems that are managed by, or on behalf of, the 68 | Licensor for the purpose of discussing and improving the Work, but 69 | excluding communication that is conspicuously marked or otherwise 70 | designated in writing by the copyright owner as "Not a Contribution." 71 | 72 | "Contributor" shall mean Licensor and any individual or Legal Entity 73 | on behalf of whom a Contribution has been received by Licensor and 74 | subsequently incorporated within the Work. 75 | 76 | 2. Grant of Copyright License. Subject to the terms and conditions of 77 | this License, each Contributor hereby grants to You a perpetual, 78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 79 | copyright license to reproduce, prepare Derivative Works of, 80 | publicly display, publicly perform, sublicense, and distribute the 81 | Work and such Derivative Works in Source or Object form. 82 | 83 | 3. Grant of Patent License. Subject to the terms and conditions of 84 | this License, each Contributor hereby grants to You a perpetual, 85 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 86 | (except as stated in this section) patent license to make, have made, 87 | use, offer to sell, sell, import, and otherwise transfer the Work, 88 | where such license applies only to those patent claims licensable 89 | by such Contributor that are necessarily infringed by their 90 | Contribution(s) alone or by combination of their Contribution(s) 91 | with the Work to which such Contribution(s) was submitted. If You 92 | institute patent litigation against any entity (including a 93 | cross-claim or counterclaim in a lawsuit) alleging that the Work 94 | or a Contribution incorporated within the Work constitutes direct 95 | or contributory patent infringement, then any patent licenses 96 | granted to You under this License for that Work shall terminate 97 | as of the date such litigation is filed. 98 | 99 | 4. Redistribution. You may reproduce and distribute copies of the 100 | Work or Derivative Works thereof in any medium, with or without 101 | modifications, and in Source or Object form, provided that You 102 | meet the following conditions: 103 | 104 | (a) You must give any other recipients of the Work or 105 | Derivative Works a copy of this License; and 106 | 107 | (b) You must cause any modified files to carry prominent notices 108 | stating that You changed the files; and 109 | 110 | (c) You must retain, in the Source form of any Derivative Works 111 | that You distribute, all copyright, patent, trademark, and 112 | attribution notices from the Source form of the Work, 113 | excluding those notices that do not pertain to any part of 114 | the Derivative Works; and 115 | 116 | (d) If the Work includes a "NOTICE" text file as part of its 117 | distribution, then any Derivative Works that You distribute must 118 | include a readable copy of the attribution notices contained 119 | within such NOTICE file, excluding those notices that do not 120 | pertain to any part of the Derivative Works, in at least one 121 | of the following places: within a NOTICE text file distributed 122 | as part of the Derivative Works; within the Source form or 123 | documentation, if provided along with the Derivative Works; or, 124 | within a display generated by the Derivative Works, if and 125 | wherever such third-party notices normally appear. The contents 126 | of the NOTICE file are for informational purposes only and 127 | do not modify the License. You may add Your own attribution 128 | notices within Derivative Works that You distribute, alongside 129 | or as an addendum to the NOTICE text from the Work, provided 130 | that such additional attribution notices cannot be construed 131 | as modifying the License. 132 | 133 | You may add Your own copyright statement to Your modifications and 134 | may provide additional or different license terms and conditions 135 | for use, reproduction, or distribution of Your modifications, or 136 | for any such Derivative Works as a whole, provided Your use, 137 | reproduction, and distribution of the Work otherwise complies with 138 | the conditions stated in this License. 139 | 140 | 5. Submission of Contributions. Unless You explicitly state otherwise, 141 | any Contribution intentionally submitted for inclusion in the Work 142 | by You to the Licensor shall be under the terms and conditions of 143 | this License, without any additional terms or conditions. 144 | Notwithstanding the above, nothing herein shall supersede or modify 145 | the terms of any separate license agreement you may have executed 146 | with Licensor regarding such Contributions. 147 | 148 | 6. Trademarks. This License does not grant permission to use the trade 149 | names, trademarks, service marks, or product names of the Licensor, 150 | except as required for reasonable and customary use in describing the 151 | origin of the Work and reproducing the content of the NOTICE file. 152 | 153 | 7. Disclaimer of Warranty. Unless required by applicable law or 154 | agreed to in writing, Licensor provides the Work (and each 155 | Contributor provides its Contributions) on an "AS IS" BASIS, 156 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 157 | implied, including, without limitation, any warranties or conditions 158 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 159 | PARTICULAR PURPOSE. You are solely responsible for determining the 160 | appropriateness of using or redistributing the Work and assume any 161 | risks associated with Your exercise of permissions under this License. 162 | 163 | 8. Limitation of Liability. In no event and under no legal theory, 164 | whether in tort (including negligence), contract, or otherwise, 165 | unless required by applicable law (such as deliberate and grossly 166 | negligent acts) or agreed to in writing, shall any Contributor be 167 | liable to You for damages, including any direct, indirect, special, 168 | incidental, or consequential damages of any character arising as a 169 | result of this License or out of the use or inability to use the 170 | Work (including but not limited to damages for loss of goodwill, 171 | work stoppage, computer failure or malfunction, or any and all 172 | other commercial damages or losses), even if such Contributor 173 | has been advised of the possibility of such damages. 174 | 175 | 9. Accepting Warranty or Additional Liability. While redistributing 176 | the Work or Derivative Works thereof, You may choose to offer, 177 | and charge a fee for, acceptance of support, warranty, indemnity, 178 | or other liability obligations and/or rights consistent with this 179 | License. However, in accepting such obligations, You may act only 180 | on Your own behalf and on Your sole responsibility, not on behalf 181 | of any other Contributor, and only if You agree to indemnify, 182 | defend, and hold each Contributor harmless for any liability 183 | incurred by, or claims asserted against, such Contributor by reason 184 | of your accepting any such warranty or additional liability. 185 | 186 | END OF TERMS AND CONDITIONS 187 | 188 | APPENDIX: How to apply the Apache License to your work. 189 | 190 | To apply the Apache License to your work, attach the following 191 | boilerplate notice, with the fields enclosed by brackets "{}" 192 | replaced with your own identifying information. (Don't include 193 | the brackets!) The text should be enclosed in the appropriate 194 | comment syntax for the file format. We also recommend that a 195 | file or class name and description of purpose be included on the 196 | same "printed page" as the copyright notice for easier 197 | identification within third-party archives. 198 | 199 | Copyright 2018 Gregor Martynus and other contributors. 200 | 201 | Licensed under the Apache License, Version 2.0 (the "License"); 202 | you may not use this file except in compliance with the License. 203 | You may obtain a copy of the License at 204 | 205 | http://www.apache.org/licenses/LICENSE-2.0 206 | 207 | Unless required by applicable law or agreed to in writing, software 208 | distributed under the License is distributed on an "AS IS" BASIS, 209 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 210 | See the License for the specific language governing permissions and 211 | limitations under the License. 212 | - sources: README.md 213 | text: "[Apache 2.0](LICENSE)" 214 | notices: [] 215 | -------------------------------------------------------------------------------- /adrs/0153-checkout-v2.md: -------------------------------------------------------------------------------- 1 | # ADR 0153: Checkout v2 2 | 3 | **Date**: 2019-10-21 4 | 5 | **Status**: Accepted 6 | 7 | ## Context 8 | 9 | This ADR details the behavior for `actions/checkout@v2`. 10 | 11 | The new action will be written in typescript. We are moving away from runner-plugin actions. 12 | 13 | We want to take this opportunity to make behavioral changes, from v1. This document is scoped to those differences. 14 | 15 | ## Decision 16 | 17 | ### Inputs 18 | 19 | ```yaml 20 | repository: 21 | description: 'Repository name with owner. For example, actions/checkout' 22 | default: ${{ github.repository }} 23 | ref: 24 | description: > 25 | The branch, tag or SHA to checkout. When checking out the repository that 26 | triggered a workflow, this defaults to the reference or SHA for that 27 | event. Otherwise, uses the default branch. 28 | token: 29 | description: > 30 | Personal access token (PAT) used to fetch the repository. The PAT is configured 31 | with the local git config, which enables your scripts to run authenticated git 32 | commands. The post-job step removes the PAT. 33 | 34 | 35 | We recommend using a service account with the least permissions necessary. 36 | Also when generating a new PAT, select the least scopes necessary. 37 | 38 | 39 | [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) 40 | default: ${{ github.token }} 41 | ssh-key: 42 | description: > 43 | SSH key used to fetch the repository. The SSH key is configured with the local 44 | git config, which enables your scripts to run authenticated git commands. 45 | The post-job step removes the SSH key. 46 | 47 | 48 | We recommend using a service account with the least permissions necessary. 49 | 50 | 51 | [Learn more about creating and using 52 | encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets) 53 | ssh-known-hosts: 54 | description: > 55 | Known hosts in addition to the user and global host key database. The public 56 | SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example, 57 | `ssh-keyscan github.com`. The public key for github.com is always implicitly added. 58 | ssh-strict: 59 | description: > 60 | Whether to perform strict host key checking. When true, adds the options `StrictHostKeyChecking=yes` 61 | and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to 62 | configure additional hosts. 63 | default: true 64 | persist-credentials: 65 | description: 'Whether to configure the token or SSH key with the local git config' 66 | default: true 67 | path: 68 | description: 'Relative path under $GITHUB_WORKSPACE to place the repository' 69 | clean: 70 | description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching' 71 | default: true 72 | fetch-depth: 73 | description: 'Number of commits to fetch. 0 indicates all history for all tags and branches.' 74 | default: 1 75 | lfs: 76 | description: 'Whether to download Git-LFS files' 77 | default: false 78 | submodules: 79 | description: > 80 | Whether to checkout submodules: `true` to checkout submodules or `recursive` to 81 | recursively checkout submodules. 82 | 83 | 84 | When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are 85 | converted to HTTPS. 86 | default: false 87 | ``` 88 | 89 | Note: 90 | - SSH support is new 91 | - `persist-credentials` is new 92 | - `path` behavior is different (refer [below](#path) for details) 93 | 94 | ### Fallback to GitHub API 95 | 96 | When a sufficient version of git is not in the PATH, fallback to the [web API](https://developer.github.com/v3/repos/contents/#get-archive-link) to download a tarball/zipball. 97 | 98 | Note: 99 | - LFS files are not included in the archive. Therefore fail if LFS is set to true. 100 | - Submodules are also not included in the archive. 101 | 102 | ### Persist credentials 103 | 104 | The credentials will be persisted on disk. This will allow users to script authenticated git commands, like `git fetch`. 105 | 106 | A post script will remove the credentials (cleanup for self-hosted). 107 | 108 | Users may opt-out by specifying `persist-credentials: false` 109 | 110 | Note: 111 | - Users scripting `git commit` may need to set the username and email. The service does not provide any reasonable default value. Users can add `git config user.name ` and `git config user.email `. We will document this guidance. 112 | 113 | #### PAT 114 | 115 | When using the `${{github.token}}` or a PAT, the token will be persisted in the local git config. The config key `http.https://github.com/.extraheader` enables an auth header to be specified on all authenticated commands `AUTHORIZATION: basic `. 116 | 117 | Note: 118 | - The auth header is scoped to all of github `http.https://github.com/.extraheader` 119 | - Additional public remotes also just work. 120 | - If users want to authenticate to an additional private remote, they should provide the `token` input. 121 | 122 | #### SSH key 123 | 124 | The SSH key will be written to disk under the `$RUNNER_TEMP` directory. The SSH key will 125 | be removed by the action's post-job hook. Additionally, RUNNER_TEMP is cleared by the 126 | runner between jobs. 127 | 128 | The SSH key must be written with strict file permissions. The SSH client requires the file 129 | to be read/write for the user, and not accessible by others. 130 | 131 | The user host key database (`~/.ssh/known_hosts`) will be copied to a unique file under 132 | `$RUNNER_TEMP`. And values from the input `ssh-known-hosts` will be added to the file. 133 | 134 | The SSH command will be overridden for the local git config: 135 | 136 | ```sh 137 | git config core.sshCommand 'ssh -i "$RUNNER_TEMP/path-to-ssh-key" -o StrictHostKeyChecking=yes -o CheckHostIP=no -o "UserKnownHostsFile=$RUNNER_TEMP/path-to-known-hosts"' 138 | ``` 139 | 140 | When the input `ssh-strict` is set to `false`, the options `CheckHostIP` and `StrictHostKeyChecking` will not be overridden. 141 | 142 | Note: 143 | - When `ssh-strict` is set to `true` (default), the SSH option `CheckHostIP` can safely be disabled. 144 | Strict host checking verifies the server's public key. Therefore, IP verification is unnecessary 145 | and noisy. For example: 146 | > Warning: Permanently added the RSA host key for IP address '140.82.113.4' to the list of known hosts. 147 | - Since GIT_SSH_COMMAND overrides core.sshCommand, temporarily set the env var when fetching the repo. When creds 148 | are persisted, core.sshCommand is leveraged to avoid multiple checkout steps stomping over each other. 149 | - Modify actions/runner to mount RUNNER_TEMP to enable scripting authenticated git commands from a container action. 150 | - Refer [here](https://linux.die.net/man/5/ssh_config) for SSH config details. 151 | 152 | ### Fetch behavior 153 | 154 | Fetch only the SHA being built and set depth=1. This significantly reduces the fetch time for large repos. 155 | 156 | If a SHA isn't available (e.g. multi repo), then fetch only the specified ref with depth=1. 157 | 158 | The input `fetch-depth` can be used to control the depth. 159 | 160 | Note: 161 | - Fetching a single commit is supported by Git wire protocol version 2. The git client uses protocol version 0 by default. The desired protocol version can be overridden in the git config or on the fetch command line invocation (`-c protocol.version=2`). We will override on the fetch command line, for transparency. 162 | - Git client version 2.18+ (released June 2018) is required for wire protocol version 2. 163 | 164 | ### Checkout behavior 165 | 166 | For CI, checkout will create a local ref with the upstream set. This allows users to script git as they normally would. 167 | 168 | For PR, continue to checkout detached head. The PR branch is special - the branch and merge commit are created by the server. It doesn't match a users' local workflow. 169 | 170 | Note: 171 | - Consider deleting all local refs during cleanup if that helps avoid collisions. More testing required. 172 | 173 | ### Path 174 | 175 | For the mainline scenario, the disk-layout behavior remains the same. 176 | 177 | Remember, given the repo `johndoe/foo`, the mainline disk layout looks like: 178 | 179 | ``` 180 | GITHUB_WORKSPACE=/home/runner/work/foo/foo 181 | RUNNER_WORKSPACE=/home/runner/work/foo 182 | ``` 183 | 184 | 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`. 185 | 186 | V2 no longer changes `github.workspace` to follow wherever the self repo is checked-out. 187 | 188 | These behavioral changes align better with container actions. The [documented filesystem contract](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#docker-container-filesystem) is: 189 | 190 | - `/github/home` 191 | - `/github/workspace` - Note: GitHub Actions must be run by the default Docker user (root). Ensure your Dockerfile does not set the USER instruction, otherwise you will not be able to access `GITHUB_WORKSPACE`. 192 | - `/github/workflow` 193 | 194 | Note: 195 | - The tracking config will not be updated to reflect the path of the workflow repo. 196 | - Any existing workflow repo will not be moved when the checkout path changes. In fact some customers want to checkout the workflow repo twice, side by side against different branches. 197 | - Actions that need to operate only against the root of the self repo, should expose a `path` input. 198 | 199 | #### Default value for `path` input 200 | 201 | The `path` input will default to `./` which is rooted against `github.workspace`. 202 | 203 | This default fits the mainline scenario well: single checkout 204 | 205 | For multi-checkout, users must specify the `path` input for at least one of the repositories. 206 | 207 | Note: 208 | - An alternative is for the self repo to default to `./` and other repos default to ``. However nested layout is an atypical git layout and therefore is not a good default. Users should supply the path info. 209 | 210 | #### Example - Nested layout 211 | 212 | The following example checks-out two repositories and creates a nested layout. 213 | 214 | ```yaml 215 | # Self repo - Checkout to $GITHUB_WORKSPACE 216 | - uses: checkout@v2 217 | 218 | # Other repo - Checkout to $GITHUB_WORKSPACE/myscripts 219 | - uses: checkout@v2 220 | with: 221 | repository: myorg/myscripts 222 | path: myscripts 223 | ``` 224 | 225 | #### Example - Side by side layout 226 | 227 | The following example checks-out two repositories and creates a side-by-side layout. 228 | 229 | ```yaml 230 | # Self repo - Checkout to $GITHUB_WORKSPACE/foo 231 | - uses: checkout@v2 232 | with: 233 | path: foo 234 | 235 | # Other repo - Checkout to $GITHUB_WORKSPACE/myscripts 236 | - uses: checkout@v2 237 | with: 238 | repository: myorg/myscripts 239 | path: myscripts 240 | ``` 241 | 242 | #### Path impact to problem matchers 243 | 244 | Problem matchers associate the source files with annotations. 245 | 246 | Today the runner verifies the source file is under the `github.workspace`. Otherwise the source file property is dropped. 247 | 248 | Multi-checkout complicates the matter. However even today submodules may cause this heuristic to be inaccurate. 249 | 250 | A better solution is: 251 | 252 | Given a source file path, walk up the directories until the first `.git/config` is found. Check if it matches the self repo (`url = https://github.com/OWNER/REPO`). If not, drop the source file path. 253 | 254 | ### Submodules 255 | 256 | With both PAT and SSH key support, we should be able to provide frictionless support for 257 | submodules scenarios: recursive, non-recursive, relative submodule paths. 258 | 259 | When fetching submodules, follow the `fetch-depth` settings. 260 | 261 | Also when fetching submodules, if the `ssh-key` input is not provided then convert SSH URLs to HTTPS: `-c url."https://github.com/".insteadOf "git@github.com:"` 262 | 263 | Credentials will be persisted in the submodules local git config too. 264 | 265 | ### Port to typescript 266 | 267 | The checkout action should be a typescript action on the GitHub graph, for the following reasons: 268 | - Enables customers to fork the checkout repo and modify 269 | - Serves as an example for customers 270 | - Demystifies the checkout action manifest 271 | - Simplifies the runner 272 | - Reduce the amount of runner code to port (if we ever do) 273 | 274 | Note: 275 | - This means job-container images will need git in the PATH, for checkout. 276 | 277 | ### Branching strategy and release tags 278 | 279 | - Create a servicing branch for V1: `releases/v1` 280 | - Merge the changes into the default branch 281 | - Release using a new tag `preview` 282 | - When stable, release using a new tag `v2` 283 | 284 | ## Consequences 285 | 286 | - Update the checkout action and readme 287 | - Update samples to consume `actions/checkout@v2` 288 | - Job containers now require git in the PATH for checkout, otherwise fallback to REST API 289 | - Minimum git version 2.18 290 | - Update problem matcher logic regarding source file verification (runner) 291 | --------------------------------------------------------------------------------