Public Access
1
0
mirror of https://github.com/actions/checkout.git synced 2025-07-13 12:13:51 +00:00

Compare commits

..

4 Commits

Author SHA1 Message Date
c43fdbfb30 update dist 2020-08-21 08:14:48 -04:00
239235dd46 add updated dist 2020-08-21 07:57:25 -04:00
2955f2419d fix package lock file 2020-08-21 07:53:08 -04:00
5aa50f005d change zeit -> vercel
Company name changed and deprecated all packages in favor of the new name
2020-08-21 07:47:54 -04:00
84 changed files with 34556 additions and 45683 deletions

View File

@ -1,6 +1,6 @@
{ {
"plugins": ["jest", "@typescript-eslint"], "plugins": ["jest", "@typescript-eslint"],
"extends": ["plugin:github/recommended"], "extends": ["plugin:github/es6"],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {
"ecmaVersion": 9, "ecmaVersion": 9,
@ -16,19 +16,23 @@
"@typescript-eslint/no-require-imports": "error", "@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error", "@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error", "@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-ignore": "error",
"camelcase": "off", "camelcase": "off",
"@typescript-eslint/camelcase": "error",
"@typescript-eslint/class-name-casing": "error",
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}], "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
"@typescript-eslint/func-call-spacing": ["error", "never"], "@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/generic-type-naming": ["error", "^[A-Z][A-Za-z]*$"],
"@typescript-eslint/no-array-constructor": "error", "@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error", "@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-extraneous-class": "error", "@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-for-in-array": "error", "@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error", "@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error", "@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error", "@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn", "@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-object-literal-type-assertion": "error",
"@typescript-eslint/no-unnecessary-qualifier": "error", "@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error", "@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error", "@typescript-eslint/no-useless-constructor": "error",
@ -36,6 +40,7 @@
"@typescript-eslint/prefer-for-of": "warn", "@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn", "@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error", "@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-interface": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error", "@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error", "@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error", "@typescript-eslint/require-array-sort-compare": "error",

1
.gitattributes vendored
View File

@ -1 +0,0 @@
.licenses/** -diff linguist-generated=true

View File

@ -1,51 +0,0 @@
# `dist/index.js` is a special file in Actions.
# When you reference an action with `uses:` in a workflow,
# `index.js` is the code that will run.
# For our project, we generate this file through a build process
# from other source files.
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
name: Check dist
on:
push:
branches:
- main
paths-ignore:
- '**.md'
pull_request:
paths-ignore:
- '**.md'
workflow_dispatch:
jobs:
check-dist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set Node.js 16.x
uses: actions/setup-node@v1
with:
node-version: 16.x
- name: Install dependencies
run: npm ci
- name: Rebuild the index.js file
run: npm run build
- name: Compare the expected and actual dist/ directories
run: |
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
echo "Detected uncommitted changes after build. See status below:"
git diff
exit 1
fi
# If dist/ was different than expected, upload the expected version as an artifact
- uses: actions/upload-artifact@v2
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
with:
name: dist
path: dist/

View File

@ -1,58 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '28 9 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- run: npm ci
- run: npm run build
- run: rm -rf dist # We want code scanning to analyze lib instead (individual .js files)
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@ -1,14 +0,0 @@
name: Licensed
on:
push: {branches: main}
pull_request: {branches: main}
jobs:
test:
runs-on: ubuntu-latest
name: Check licenses
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run licensed-check

View File

@ -13,8 +13,8 @@ jobs:
steps: steps:
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
with: with:
node-version: 16.x node-version: 12.x
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- run: npm ci - run: npm ci
- run: npm run build - run: npm run build
- run: npm run format-check - run: npm run format-check
@ -32,7 +32,7 @@ jobs:
steps: steps:
# Clone this repo # Clone this repo
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
# Basic checkout # Basic checkout
- name: Checkout basic - name: Checkout basic
@ -142,7 +142,7 @@ jobs:
options: --dns 127.0.0.1 options: --dns 127.0.0.1
services: services:
squid-proxy: squid-proxy:
image: ubuntu/squid:latest image: datadog/squid:latest
ports: ports:
- 3128:3128 - 3128:3128
env: env:
@ -150,7 +150,7 @@ jobs:
steps: steps:
# Clone this repo # Clone this repo
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
# Basic checkout using git # Basic checkout using git
- name: Checkout basic - name: Checkout basic
@ -182,7 +182,7 @@ jobs:
steps: steps:
# Clone this repo # Clone this repo
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
# Basic checkout using git # Basic checkout using git
- name: Checkout basic - name: Checkout basic
@ -205,41 +205,3 @@ jobs:
path: basic path: basic
- name: Verify basic - name: Verify basic
run: __test__/verify-basic.sh --archive run: __test__/verify-basic.sh --archive
test-git-container:
runs-on: ubuntu-latest
container: bitnami/git:latest
steps:
# Clone this repo
- name: Checkout
uses: actions/checkout@v3
with:
path: v3
# Basic checkout using git
- name: Checkout basic
uses: ./v3
with:
ref: test-data/v2/basic
- name: Verify basic
run: |
if [ ! -f "./basic-file.txt" ]; then
echo "Expected basic file does not exist"
exit 1
fi
# Verify .git folder
if [ ! -d "./.git" ]; then
echo "Expected ./.git folder to exist"
exit 1
fi
# Verify auth token
git config --global --add safe.directory "*"
git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main
# needed to make checkout post cleanup succeed
- name: Fix Checkout v3
uses: actions/checkout@v3
with:
path: v3

View File

@ -1,31 +0,0 @@
name: Update Main Version
run-name: Move ${{ github.event.inputs.major_version }} to ${{ github.event.inputs.target }}
on:
workflow_dispatch:
inputs:
target:
description: The tag or reference to use
required: true
major_version:
type: choice
description: The major version to update
options:
- v3
- v2
jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Git config
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Tag new target
run: git tag -f ${{ github.event.inputs.major_version }} ${{ github.event.inputs.target }}
- name: Push new tag
run: git push origin ${{ github.event.inputs.major_version }} --force

4
.gitignore vendored
View File

@ -1,5 +1,3 @@
__test__/_temp __test__/_temp
_temp/
lib/ lib/
node_modules/ node_modules/
.vscode/

View File

@ -1,14 +0,0 @@
sources:
npm: true
allowed:
- apache-2.0
- bsd-2-clause
- bsd-3-clause
- isc
- mit
- cc0-1.0
- unlicense
reviewed:
npm:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,51 +1,10 @@
# Changelog # Changelog
## v3.5.2
- [Fix api endpoint for GHES](https://github.com/actions/checkout/pull/1289)
## v3.5.1
- [Fix slow checkout on Windows](https://github.com/actions/checkout/pull/1246)
## v3.5.0
* [Add new public key for known_hosts](https://github.com/actions/checkout/pull/1237)
## v3.4.0
- [Upgrade codeql actions to v2](https://github.com/actions/checkout/pull/1209)
- [Upgrade dependencies](https://github.com/actions/checkout/pull/1210)
- [Upgrade @actions/io](https://github.com/actions/checkout/pull/1225)
## v3.3.0
- [Implement branch list using callbacks from exec function](https://github.com/actions/checkout/pull/1045)
- [Add in explicit reference to private checkout options](https://github.com/actions/checkout/pull/1050)
- [Fix comment typos (that got added in #770)](https://github.com/actions/checkout/pull/1057)
## v3.2.0
- [Add GitHub Action to perform release](https://github.com/actions/checkout/pull/942)
- [Fix status badge](https://github.com/actions/checkout/pull/967)
- [Replace datadog/squid with ubuntu/squid Docker image](https://github.com/actions/checkout/pull/1002)
- [Wrap pipeline commands for submoduleForeach in quotes](https://github.com/actions/checkout/pull/964)
- [Update @actions/io to 1.1.2](https://github.com/actions/checkout/pull/1029)
- [Upgrading version to 3.2.0](https://github.com/actions/checkout/pull/1039)
## v3.1.0
- [Use @actions/core `saveState` and `getState`](https://github.com/actions/checkout/pull/939)
- [Add `github-server-url` input](https://github.com/actions/checkout/pull/922)
## v3.0.2
- [Add input `set-safe-directory`](https://github.com/actions/checkout/pull/770)
## v3.0.1
- [Fixed an issue where checkout failed to run in container jobs due to the new git setting `safe.directory`](https://github.com/actions/checkout/pull/762)
- [Bumped various npm package versions](https://github.com/actions/checkout/pull/744)
## v3.0.0
- [Update to node 16](https://github.com/actions/checkout/pull/689)
## v2.3.1 ## v2.3.1
- [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284) - [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284)
## v2.3.0 ## v2.3.0
- [Fallback to the default branch](https://github.com/actions/checkout/pull/278) - [Fallback to the default branch](https://github.com/actions/checkout/pull/278)

View File

@ -1 +0,0 @@
* @actions/actions-runtime

View File

@ -1,28 +0,0 @@
# Contributing
## Submitting a pull request
1. Fork and clone the repository
1. Configure and install the dependencies: `npm install`
1. Create a new branch: `git checkout -b my-branch-name`
1. Make your change, add tests, and make sure the tests still pass: `npm run test`
1. Make sure your code is correctly formatted: `npm run format`
1. Update `dist/index.js` using `npm run build`. This creates a single javascript file that is used as an entrypoint for the action
1. Push to your fork and submit a pull request
1. Pat yourself on the back and wait for your pull request to be reviewed and merged
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
- Write tests.
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
## Resources
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)
- [Writing good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
Thanks! :heart: :heart: :heart:
GitHub Actions Team :octocat:

View File

@ -1,6 +1,8 @@
[![Build and Test](https://github.com/actions/checkout/actions/workflows/test.yml/badge.svg)](https://github.com/actions/checkout/actions/workflows/test.yml) <p align="center">
<a href="https://github.com/actions/checkout"><img alt="GitHub Actions status" src="https://github.com/actions/checkout/workflows/test-local/badge.svg"></a>
</p>
# Checkout V3 # Checkout V2
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it. This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
@ -12,14 +14,27 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
# What's new # What's new
- Updated to the node16 runtime by default - Improved performance
- This requires a minimum [Actions Runner](https://github.com/actions/runner/releases/tag/v2.285.0) version of v2.285.0 to run, which is by default available in GHES 3.4 or later. - Fetches only a single commit by default
- Script authenticated git commands
- Auth token persisted in the local git config
- Supports SSH
- Creates a local branch
- No longer detached HEAD when checking out a branch
- Improved layout
- The input `path` is always relative to $GITHUB_WORKSPACE
- Aligns better with container actions, where $GITHUB_WORKSPACE gets mapped in
- Fallback to REST API download
- When Git 2.18 or higher is not in the PATH, the REST API will be used to download the files
- When using a job container, the container's PATH is used
Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions.
# Usage # Usage
<!-- start usage --> <!-- start usage -->
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
# Repository name with owner. For example, actions/checkout # Repository name with owner. For example, actions/checkout
# Default: ${{ github.repository }} # Default: ${{ github.repository }}
@ -90,17 +105,6 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
# #
# Default: false # Default: false
submodules: '' submodules: ''
# Add repository path as safe.directory for Git global config by running `git
# config --global --add safe.directory <path>`
# Default: true
set-safe-directory: ''
# The base URL for the GitHub instance that you are trying to clone from, will use
# environment defaults to fetch from the same instance that the workflow is
# running from unless specified. Example URLs are https://github.com or
# https://my-ghes-server.example.com
github-server-url: ''
``` ```
<!-- end usage --> <!-- end usage -->
@ -119,7 +123,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Fetch all history for all tags and branches ## Fetch all history for all tags and branches
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
``` ```
@ -127,7 +131,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Checkout a different branch ## Checkout a different branch
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
ref: my-branch ref: my-branch
``` ```
@ -135,7 +139,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Checkout HEAD^ ## Checkout HEAD^
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
fetch-depth: 2 fetch-depth: 2
- run: git checkout HEAD^ - run: git checkout HEAD^
@ -145,45 +149,43 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
```yaml ```yaml
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
path: main path: main
- name: Checkout tools repo - name: Checkout tools repo
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
repository: my-org/my-tools repository: my-org/my-tools
path: my-tools path: my-tools
``` ```
> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
## Checkout multiple repos (nested) ## Checkout multiple repos (nested)
```yaml ```yaml
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
- name: Checkout tools repo - name: Checkout tools repo
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
repository: my-org/my-tools repository: my-org/my-tools
path: my-tools path: my-tools
``` ```
> - If your secondary repository is private you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
## Checkout multiple repos (private) ## Checkout multiple repos (private)
```yaml ```yaml
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
path: main path: main
- name: Checkout private tools - name: Checkout private tools
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
repository: my-org/my-private-tools repository: my-org/my-private-tools
token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT token: ${{ secrets.GitHub_PAT }} # `GitHub_PAT` is a secret that contains your PAT
path: my-tools path: my-tools
``` ```
@ -193,7 +195,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
## Checkout pull request HEAD commit instead of merge commit ## Checkout pull request HEAD commit instead of merge commit
```yaml ```yaml
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
``` ```
@ -209,7 +211,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
``` ```
## Push a commit using the built-in token ## Push a commit using the built-in token
@ -220,7 +222,7 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- run: | - run: |
date > generated.txt date > generated.txt
git config user.name github-actions git config user.name github-actions

View File

@ -20,7 +20,6 @@ let tempHomedir: string
let git: IGitCommandManager & {env: {[key: string]: string}} let git: IGitCommandManager & {env: {[key: string]: string}}
let settings: IGitSourceSettings let settings: IGitSourceSettings
let sshPath: string let sshPath: string
let githubServerUrl: string
describe('git-auth-helper tests', () => { describe('git-auth-helper tests', () => {
beforeAll(async () => { beforeAll(async () => {
@ -68,18 +67,11 @@ describe('git-auth-helper tests', () => {
} }
}) })
async function testAuthHeader( const configureAuth_configuresAuthHeader =
testName: string, 'configureAuth configures auth header'
serverUrl: string | undefined = undefined it(configureAuth_configuresAuthHeader, async () => {
) {
// Arrange // Arrange
let expectedServerUrl = 'https://github.com' await setup(configureAuth_configuresAuthHeader)
if (serverUrl) {
githubServerUrl = serverUrl
expectedServerUrl = githubServerUrl
}
await setup(testName)
expect(settings.authToken).toBeTruthy() // sanity check expect(settings.authToken).toBeTruthy() // sanity check
const authHelper = gitAuthHelper.createAuthHelper(git, settings) const authHelper = gitAuthHelper.createAuthHelper(git, settings)
@ -96,33 +88,9 @@ describe('git-auth-helper tests', () => {
).toString('base64') ).toString('base64')
expect( expect(
configContent.indexOf( configContent.indexOf(
`http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}` `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
) )
).toBeGreaterThanOrEqual(0) ).toBeGreaterThanOrEqual(0)
}
const configureAuth_configuresAuthHeader =
'configureAuth configures auth header'
it(configureAuth_configuresAuthHeader, async () => {
await testAuthHeader(configureAuth_configuresAuthHeader)
})
const configureAuth_AcceptsGitHubServerUrl =
'inject https://my-ghes-server.com as github server url'
it(configureAuth_AcceptsGitHubServerUrl, async () => {
await testAuthHeader(
configureAuth_AcceptsGitHubServerUrl,
'https://my-ghes-server.com'
)
})
const configureAuth_AcceptsGitHubServerUrlSetToGHEC =
'inject https://github.com as github server url'
it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => {
await testAuthHeader(
configureAuth_AcceptsGitHubServerUrl,
'https://github.com'
)
}) })
const configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse = const configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse =
@ -449,7 +417,7 @@ describe('git-auth-helper tests', () => {
`Did not expect file to exist: '${globalGitConfigPath}'` `Did not expect file to exist: '${globalGitConfigPath}'`
) )
} catch (err) { } catch (err) {
if ((err as any)?.code !== 'ENOENT') { if (err.code !== 'ENOENT') {
throw err throw err
} }
} }
@ -550,17 +518,12 @@ describe('git-auth-helper tests', () => {
await authHelper.configureSubmoduleAuth() await authHelper.configureSubmoduleAuth()
// Assert // Assert
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4) expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
/unset-all.*insteadOf/ /unset-all.*insteadOf/
) )
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/) expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/url.*insteadOf/)
/url.*insteadOf.*git@github.com:/
)
expect(mockSubmoduleForeach.mock.calls[3][0]).toMatch(
/url.*insteadOf.*org-123456@github.com:/
)
} }
) )
@ -638,7 +601,7 @@ describe('git-auth-helper tests', () => {
await fs.promises.stat(actualKeyPath) await fs.promises.stat(actualKeyPath)
throw new Error('SSH key should have been deleted') throw new Error('SSH key should have been deleted')
} catch (err) { } catch (err) {
if ((err as any)?.code !== 'ENOENT') { if (err.code !== 'ENOENT') {
throw err throw err
} }
} }
@ -648,7 +611,7 @@ describe('git-auth-helper tests', () => {
await fs.promises.stat(actualKnownHostsPath) await fs.promises.stat(actualKnownHostsPath)
throw new Error('SSH known hosts should have been deleted') throw new Error('SSH known hosts should have been deleted')
} catch (err) { } catch (err) {
if ((err as any)?.code !== 'ENOENT') { if (err.code !== 'ENOENT') {
throw err throw err
} }
} }
@ -675,11 +638,10 @@ describe('git-auth-helper tests', () => {
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0) expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
}) })
const removeGlobalConfig_removesOverride = const removeGlobalAuth_removesOverride = 'removeGlobalAuth removes override'
'removeGlobalConfig removes override' it(removeGlobalAuth_removesOverride, async () => {
it(removeGlobalConfig_removesOverride, async () => {
// Arrange // Arrange
await setup(removeGlobalConfig_removesOverride) await setup(removeGlobalAuth_removesOverride)
const authHelper = gitAuthHelper.createAuthHelper(git, settings) const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth() await authHelper.configureAuth()
await authHelper.configureGlobalAuth() await authHelper.configureGlobalAuth()
@ -688,7 +650,7 @@ describe('git-auth-helper tests', () => {
await fs.promises.stat(path.join(git.env['HOME'], '.gitconfig')) await fs.promises.stat(path.join(git.env['HOME'], '.gitconfig'))
// Act // Act
await authHelper.removeGlobalConfig() await authHelper.removeGlobalAuth()
// Assert // Assert
expect(git.env['HOME']).toBeUndefined() expect(git.env['HOME']).toBeUndefined()
@ -696,7 +658,7 @@ describe('git-auth-helper tests', () => {
await fs.promises.stat(homeOverride) await fs.promises.stat(homeOverride)
throw new Error(`Should have been deleted '${homeOverride}'`) throw new Error(`Should have been deleted '${homeOverride}'`)
} catch (err) { } catch (err) {
if ((err as any)?.code !== 'ENOENT') { if (err.code !== 'ENOENT') {
throw err throw err
} }
} }
@ -808,10 +770,7 @@ async function setup(testName: string): Promise<void> {
repositoryPath: '', repositoryPath: '',
sshKey: sshPath ? 'some ssh private key' : '', sshKey: sshPath ? 'some ssh private key' : '',
sshKnownHosts: '', sshKnownHosts: '',
sshStrict: true, sshStrict: true
workflowOrganizationId: 123456,
setSafeDirectory: true,
githubServerUrl: githubServerUrl
} }
} }

View File

@ -1,80 +0,0 @@
import * as exec from '@actions/exec'
import * as fshelper from '../lib/fs-helper'
import * as commandManager from '../lib/git-command-manager'
let git: commandManager.IGitCommandManager
let mockExec = jest.fn()
describe('git-auth-helper tests', () => {
beforeAll(async () => {})
beforeEach(async () => {
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
})
afterEach(() => {
jest.restoreAllMocks()
})
afterAll(() => {})
it('branch list matches', async () => {
mockExec.mockImplementation((path, args, options) => {
console.log(args, options.listeners.stdout)
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('2.18'))
return 0
}
if (args.includes('rev-parse')) {
options.listeners.stdline(Buffer.from('refs/heads/foo'))
options.listeners.stdline(Buffer.from('refs/heads/bar'))
return 0
}
return 1
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
const workingDirectory = 'test'
const lfs = false
git = await commandManager.createCommandManager(workingDirectory, lfs)
let branches = await git.branchList(false)
expect(branches).toHaveLength(2)
expect(branches.sort()).toEqual(['foo', 'bar'].sort())
})
it('ambiguous ref name output is captured', async () => {
mockExec.mockImplementation((path, args, options) => {
console.log(args, options.listeners.stdout)
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('2.18'))
return 0
}
if (args.includes('rev-parse')) {
options.listeners.stdline(Buffer.from('refs/heads/foo'))
// If refs/tags/v1 and refs/heads/tags/v1 existed on this repository
options.listeners.errline(
Buffer.from("error: refname 'tags/v1' is ambiguous")
)
return 0
}
return 1
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
const workingDirectory = 'test'
const lfs = false
git = await commandManager.createCommandManager(workingDirectory, lfs)
let branches = await git.branchList(false)
expect(branches).toHaveLength(1)
expect(branches.sort()).toEqual(['foo'].sort())
})
})

View File

@ -1,9 +1,9 @@
import * as assert from 'assert'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as fsHelper from '../lib/fs-helper' import * as fsHelper from '../lib/fs-helper'
import * as github from '@actions/github' import * as github from '@actions/github'
import * as inputHelper from '../lib/input-helper' import * as inputHelper from '../lib/input-helper'
import * as path from 'path' import * as path from 'path'
import * as workflowContextHelper from '../lib/workflow-context-helper'
import {IGitSourceSettings} from '../lib/git-source-settings' import {IGitSourceSettings} from '../lib/git-source-settings'
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE'] const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
@ -43,11 +43,6 @@ describe('input-helper tests', () => {
.spyOn(fsHelper, 'directoryExistsSync') .spyOn(fsHelper, 'directoryExistsSync')
.mockImplementation((path: string) => path == gitHubWorkspace) .mockImplementation((path: string) => path == gitHubWorkspace)
// Mock ./workflowContextHelper getOrganizationId()
jest
.spyOn(workflowContextHelper, 'getOrganizationId')
.mockImplementation(() => Promise.resolve(123456))
// GitHub workspace // GitHub workspace
process.env['GITHUB_WORKSPACE'] = gitHubWorkspace process.env['GITHUB_WORKSPACE'] = gitHubWorkspace
}) })
@ -72,8 +67,8 @@ describe('input-helper tests', () => {
jest.restoreAllMocks() jest.restoreAllMocks()
}) })
it('sets defaults', async () => { it('sets defaults', () => {
const settings: IGitSourceSettings = await inputHelper.getInputs() const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings).toBeTruthy() expect(settings).toBeTruthy()
expect(settings.authToken).toBeFalsy() expect(settings.authToken).toBeFalsy()
expect(settings.clean).toBe(true) expect(settings.clean).toBe(true)
@ -85,14 +80,13 @@ describe('input-helper tests', () => {
expect(settings.repositoryName).toBe('some-repo') expect(settings.repositoryName).toBe('some-repo')
expect(settings.repositoryOwner).toBe('some-owner') expect(settings.repositoryOwner).toBe('some-owner')
expect(settings.repositoryPath).toBe(gitHubWorkspace) expect(settings.repositoryPath).toBe(gitHubWorkspace)
expect(settings.setSafeDirectory).toBe(true)
}) })
it('qualifies ref', async () => { it('qualifies ref', () => {
let originalRef = github.context.ref let originalRef = github.context.ref
try { try {
github.context.ref = 'some-unqualified-ref' github.context.ref = 'some-unqualified-ref'
const settings: IGitSourceSettings = await inputHelper.getInputs() const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings).toBeTruthy() expect(settings).toBeTruthy()
expect(settings.commit).toBe('1234567890123456789012345678901234567890') expect(settings.commit).toBe('1234567890123456789012345678901234567890')
expect(settings.ref).toBe('refs/heads/some-unqualified-ref') expect(settings.ref).toBe('refs/heads/some-unqualified-ref')
@ -101,42 +95,32 @@ describe('input-helper tests', () => {
} }
}) })
it('requires qualified repo', async () => { it('requires qualified repo', () => {
inputs.repository = 'some-unqualified-repo' inputs.repository = 'some-unqualified-repo'
try { assert.throws(() => {
await inputHelper.getInputs() inputHelper.getInputs()
throw 'should not reach here' }, /Invalid repository 'some-unqualified-repo'/)
} catch (err) {
expect(`(${(err as any).message}`).toMatch(
"Invalid repository 'some-unqualified-repo'"
)
}
}) })
it('roots path', async () => { it('roots path', () => {
inputs.path = 'some-directory/some-subdirectory' inputs.path = 'some-directory/some-subdirectory'
const settings: IGitSourceSettings = await inputHelper.getInputs() const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.repositoryPath).toBe( expect(settings.repositoryPath).toBe(
path.join(gitHubWorkspace, 'some-directory', 'some-subdirectory') path.join(gitHubWorkspace, 'some-directory', 'some-subdirectory')
) )
}) })
it('sets ref to empty when explicit sha', async () => { it('sets ref to empty when explicit sha', () => {
inputs.ref = '1111111111222222222233333333334444444444' inputs.ref = '1111111111222222222233333333334444444444'
const settings: IGitSourceSettings = await inputHelper.getInputs() const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.ref).toBeFalsy() expect(settings.ref).toBeFalsy()
expect(settings.commit).toBe('1111111111222222222233333333334444444444') expect(settings.commit).toBe('1111111111222222222233333333334444444444')
}) })
it('sets sha to empty when explicit ref', async () => { it('sets sha to empty when explicit ref', () => {
inputs.ref = 'refs/heads/some-other-ref' inputs.ref = 'refs/heads/some-other-ref'
const settings: IGitSourceSettings = await inputHelper.getInputs() const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.ref).toBe('refs/heads/some-other-ref') expect(settings.ref).toBe('refs/heads/some-other-ref')
expect(settings.commit).toBeFalsy() expect(settings.commit).toBeFalsy()
}) })
it('sets workflow organization ID', async () => {
const settings: IGitSourceSettings = await inputHelper.getInputs()
expect(settings.workflowOrganizationId).toBe(123456)
})
}) })

View File

@ -2,5 +2,5 @@
mkdir override-git-version mkdir override-git-version
cd override-git-version cd override-git-version
echo @echo override git version 1.2.3 > git.cmd echo @echo override git version 1.2.3 > git.cmd
echo "%CD%" >> $GITHUB_PATH echo ::add-path::%CD%
cd .. cd ..

View File

@ -5,5 +5,5 @@ cd override-git-version
echo "#!/bin/sh" > git echo "#!/bin/sh" > git
echo "echo override git version 1.2.3" >> git echo "echo override git version 1.2.3" >> git
chmod +x git chmod +x git
echo "$(pwd)" >> $GITHUB_PATH echo "::add-path::$(pwd)"
cd .. cd ..

View File

@ -16,7 +16,7 @@ describe('ref-helper tests', () => {
await refHelper.getCheckoutInfo(git, 'refs/heads/my/branch', commit) await refHelper.getCheckoutInfo(git, 'refs/heads/my/branch', commit)
throw new Error('Should not reach here') throw new Error('Should not reach here')
} catch (err) { } catch (err) {
expect((err as any)?.message).toBe('Arg git cannot be empty') expect(err.message).toBe('Arg git cannot be empty')
} }
}) })
@ -25,9 +25,7 @@ describe('ref-helper tests', () => {
await refHelper.getCheckoutInfo(git, '', '') await refHelper.getCheckoutInfo(git, '', '')
throw new Error('Should not reach here') throw new Error('Should not reach here')
} catch (err) { } catch (err) {
expect((err as any)?.message).toBe( expect(err.message).toBe('Args ref and commit cannot both be empty')
'Args ref and commit cannot both be empty'
)
} }
}) })
@ -104,7 +102,7 @@ describe('ref-helper tests', () => {
await refHelper.getCheckoutInfo(git, 'my-ref', '') await refHelper.getCheckoutInfo(git, 'my-ref', '')
throw new Error('Should not reach here') throw new Error('Should not reach here')
} catch (err) { } catch (err) {
expect((err as any)?.message).toBe( expect(err.message).toBe(
"A branch or tag with the name 'my-ref' could not be found" "A branch or tag with the name 'my-ref' could not be found"
) )
} }

View File

@ -74,7 +74,7 @@ describe('retry-helper tests', () => {
throw new Error(`some error ${++attempts}`) throw new Error(`some error ${++attempts}`)
}) })
} catch (err) { } catch (err) {
error = err as Error error = err
} }
expect(error.message).toBe('some error 3') expect(error.message).toBe('some error 3')
expect(attempts).toBe(3) expect(attempts).toBe(3)

View File

@ -68,13 +68,7 @@ inputs:
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
converted to HTTPS. converted to HTTPS.
default: false default: false
set-safe-directory:
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
default: true
github-server-url:
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
required: false
runs: runs:
using: node16 using: node12
main: dist/index.js main: dist/index.js
post: dist/index.js post: dist/index.js

42760
dist/index.js vendored

File diff suppressed because one or more lines are too long

1328
dist/licenses.txt vendored Normal file

File diff suppressed because it is too large Load Diff

34832
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "checkout", "name": "checkout",
"version": "3.5.2", "version": "2.0.2",
"description": "checkout action", "description": "checkout action",
"main": "lib/main.js", "main": "lib/main.js",
"scripts": { "scripts": {
@ -8,9 +8,7 @@
"format": "prettier --write '**/*.ts'", "format": "prettier --write '**/*.ts'",
"format-check": "prettier --check '**/*.ts'", "format-check": "prettier --check '**/*.ts'",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"test": "jest", "test": "jest"
"licensed-check": "src/misc/licensed-check.sh",
"licensed-generate": "src/misc/licensed-generate.sh"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -28,28 +26,27 @@
}, },
"homepage": "https://github.com/actions/checkout#readme", "homepage": "https://github.com/actions/checkout#readme",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.1.3",
"@actions/exec": "^1.0.1", "@actions/exec": "^1.0.1",
"@actions/github": "^5.0.0", "@actions/github": "^2.2.0",
"@actions/io": "^1.1.3", "@actions/io": "^1.0.1",
"@actions/tool-cache": "^1.1.2", "@actions/tool-cache": "^1.1.2",
"uuid": "^3.3.3" "uuid": "^3.3.3"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^27.0.2", "@types/jest": "^24.0.23",
"@types/node": "^12.7.12", "@types/node": "^12.7.12",
"@types/uuid": "^3.4.6", "@types/uuid": "^3.4.6",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^2.8.0",
"@typescript-eslint/parser": "^5.45.0", "@vercel/ncc": "^0.23.0",
"@vercel/ncc": "^0.36.1", "eslint": "^5.16.0",
"eslint": "^7.32.0", "eslint-plugin-github": "^2.0.0",
"eslint-plugin-github": "^4.3.2", "eslint-plugin-jest": "^22.21.0",
"eslint-plugin-jest": "^25.7.0", "jest": "^24.9.0",
"jest": "^27.3.0", "jest-circus": "^24.9.0",
"jest-circus": "^27.3.0",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"ts-jest": "^27.0.7", "ts-jest": "^24.2.0",
"typescript": "^4.4.4" "typescript": "^3.6.4"
} }
} }

View File

@ -9,7 +9,7 @@ export function directoryExistsSync(path: string, required?: boolean): boolean {
try { try {
stats = fs.statSync(path) stats = fs.statSync(path)
} catch (error) { } catch (error) {
if ((error as any)?.code === 'ENOENT') { if (error.code === 'ENOENT') {
if (!required) { if (!required) {
return false return false
} }
@ -18,8 +18,7 @@ export function directoryExistsSync(path: string, required?: boolean): boolean {
} }
throw new Error( throw new Error(
`Encountered an error when checking whether path '${path}' exists: ${(error as any) `Encountered an error when checking whether path '${path}' exists: ${error.message}`
?.message ?? error}`
) )
} }
@ -40,13 +39,12 @@ export function existsSync(path: string): boolean {
try { try {
fs.statSync(path) fs.statSync(path)
} catch (error) { } catch (error) {
if ((error as any)?.code === 'ENOENT') { if (error.code === 'ENOENT') {
return false return false
} }
throw new Error( throw new Error(
`Encountered an error when checking whether path '${path}' exists: ${(error as any) `Encountered an error when checking whether path '${path}' exists: ${error.message}`
?.message ?? error}`
) )
} }
@ -62,13 +60,12 @@ export function fileExistsSync(path: string): boolean {
try { try {
stats = fs.statSync(path) stats = fs.statSync(path)
} catch (error) { } catch (error) {
if ((error as any)?.code === 'ENOENT') { if (error.code === 'ENOENT') {
return false return false
} }
throw new Error( throw new Error(
`Encountered an error when checking whether path '${path}' exists: ${(error as any) `Encountered an error when checking whether path '${path}' exists: ${error.message}`
?.message ?? error}`
) )
} }

View File

@ -19,9 +19,8 @@ export interface IGitAuthHelper {
configureAuth(): Promise<void> configureAuth(): Promise<void>
configureGlobalAuth(): Promise<void> configureGlobalAuth(): Promise<void>
configureSubmoduleAuth(): Promise<void> configureSubmoduleAuth(): Promise<void>
configureTempGlobalConfig(): Promise<string>
removeAuth(): Promise<void> removeAuth(): Promise<void>
removeGlobalConfig(): Promise<void> removeGlobalAuth(): Promise<void>
} }
export function createAuthHelper( export function createAuthHelper(
@ -38,7 +37,7 @@ class GitAuthHelper {
private readonly tokenConfigValue: string private readonly tokenConfigValue: string
private readonly tokenPlaceholderConfigValue: string private readonly tokenPlaceholderConfigValue: string
private readonly insteadOfKey: string private readonly insteadOfKey: string
private readonly insteadOfValues: string[] = [] private readonly insteadOfValue: string
private sshCommand = '' private sshCommand = ''
private sshKeyPath = '' private sshKeyPath = ''
private sshKnownHostsPath = '' private sshKnownHostsPath = ''
@ -46,13 +45,13 @@ class GitAuthHelper {
constructor( constructor(
gitCommandManager: IGitCommandManager, gitCommandManager: IGitCommandManager,
gitSourceSettings: IGitSourceSettings | undefined gitSourceSettings?: IGitSourceSettings
) { ) {
this.git = gitCommandManager this.git = gitCommandManager
this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings) this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings)
// Token auth header // Token auth header
const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl) const serverUrl = urlHelper.getServerUrl()
this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT] this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT]
const basicCredential = Buffer.from( const basicCredential = Buffer.from(
`x-access-token:${this.settings.authToken}`, `x-access-token:${this.settings.authToken}`,
@ -64,12 +63,7 @@ class GitAuthHelper {
// Instead of SSH URL // Instead of SSH URL
this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf` // "origin" is SCHEME://HOSTNAME[:PORT] this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf` // "origin" is SCHEME://HOSTNAME[:PORT]
this.insteadOfValues.push(`git@${serverUrl.hostname}:`) this.insteadOfValue = `git@${serverUrl.hostname}:`
if (this.settings.workflowOrganizationId) {
this.insteadOfValues.push(
`org-${this.settings.workflowOrganizationId}@github.com:`
)
}
} }
async configureAuth(): Promise<void> { async configureAuth(): Promise<void> {
@ -81,11 +75,7 @@ class GitAuthHelper {
await this.configureToken() await this.configureToken()
} }
async configureTempGlobalConfig(): Promise<string> { async configureGlobalAuth(): Promise<void> {
// Already setup global config
if (this.temporaryHomePath?.length > 0) {
return path.join(this.temporaryHomePath, '.gitconfig')
}
// Create a temp home directory // Create a temp home directory
const runnerTemp = process.env['RUNNER_TEMP'] || '' const runnerTemp = process.env['RUNNER_TEMP'] || ''
assert.ok(runnerTemp, 'RUNNER_TEMP is not defined') assert.ok(runnerTemp, 'RUNNER_TEMP is not defined')
@ -104,7 +94,7 @@ class GitAuthHelper {
await fs.promises.stat(gitConfigPath) await fs.promises.stat(gitConfigPath)
configExists = true configExists = true
} catch (err) { } catch (err) {
if ((err as any)?.code !== 'ENOENT') { if (err.code !== 'ENOENT') {
throw err throw err
} }
} }
@ -115,28 +105,20 @@ class GitAuthHelper {
await fs.promises.writeFile(newGitConfigPath, '') await fs.promises.writeFile(newGitConfigPath, '')
} }
// Override HOME
core.info(
`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`
)
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
return newGitConfigPath
}
async configureGlobalAuth(): Promise<void> {
// 'configureTempGlobalConfig' noops if already set, just returns the path
const newGitConfigPath = await this.configureTempGlobalConfig()
try { try {
// Override HOME
core.info(
`Temporarily overriding HOME='${this.temporaryHomePath}' before making global git config changes`
)
this.git.setEnvironmentVariable('HOME', this.temporaryHomePath)
// Configure the token // Configure the token
await this.configureToken(newGitConfigPath, true) await this.configureToken(newGitConfigPath, true)
// Configure HTTPS instead of SSH // Configure HTTPS instead of SSH
await this.git.tryConfigUnset(this.insteadOfKey, true) await this.git.tryConfigUnset(this.insteadOfKey, true)
if (!this.settings.sshKey) { if (!this.settings.sshKey) {
for (const insteadOfValue of this.insteadOfValues) { await this.git.config(this.insteadOfKey, this.insteadOfValue, true)
await this.git.config(this.insteadOfKey, insteadOfValue, true, true)
}
} }
} catch (err) { } catch (err) {
// Unset in case somehow written to the real global config // Unset in case somehow written to the real global config
@ -157,8 +139,7 @@ class GitAuthHelper {
// by process creation audit events, which are commonly logged. For more information, // by process creation audit events, which are commonly logged. For more information,
// refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
const output = await this.git.submoduleForeach( const output = await this.git.submoduleForeach(
// wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline `git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url`,
`sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`,
this.settings.nestedSubmodules this.settings.nestedSubmodules
) )
@ -167,7 +148,7 @@ class GitAuthHelper {
output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []
for (const configPath of configPaths) { for (const configPath of configPaths) {
core.debug(`Replacing token placeholder in '${configPath}'`) core.debug(`Replacing token placeholder in '${configPath}'`)
await this.replaceTokenPlaceholder(configPath) this.replaceTokenPlaceholder(configPath)
} }
if (this.settings.sshKey) { if (this.settings.sshKey) {
@ -178,12 +159,10 @@ class GitAuthHelper {
) )
} else { } else {
// Configure HTTPS instead of SSH // Configure HTTPS instead of SSH
for (const insteadOfValue of this.insteadOfValues) { await this.git.submoduleForeach(
await this.git.submoduleForeach( `git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`,
`git config --local --add '${this.insteadOfKey}' '${insteadOfValue}'`, this.settings.nestedSubmodules
this.settings.nestedSubmodules )
)
}
} }
} }
} }
@ -193,12 +172,10 @@ class GitAuthHelper {
await this.removeToken() await this.removeToken()
} }
async removeGlobalConfig(): Promise<void> { async removeGlobalAuth(): Promise<void> {
if (this.temporaryHomePath?.length > 0) { core.debug(`Unsetting HOME override`)
core.debug(`Unsetting HOME override`) this.git.removeEnvironmentVariable('HOME')
this.git.removeEnvironmentVariable('HOME') await io.rmRF(this.temporaryHomePath)
await io.rmRF(this.temporaryHomePath)
}
} }
private async configureSsh(): Promise<void> { private async configureSsh(): Promise<void> {
@ -236,7 +213,7 @@ class GitAuthHelper {
await fs.promises.readFile(userKnownHostsPath) await fs.promises.readFile(userKnownHostsPath)
).toString() ).toString()
} catch (err) { } catch (err) {
if ((err as any)?.code !== 'ENOENT') { if (err.code !== 'ENOENT') {
throw err throw err
} }
} }
@ -247,7 +224,7 @@ class GitAuthHelper {
if (this.settings.sshKnownHosts) { if (this.settings.sshKnownHosts) {
knownHosts += `# Begin from input known hosts\n${this.settings.sshKnownHosts}\n# end from input known hosts\n` knownHosts += `# Begin from input known hosts\n${this.settings.sshKnownHosts}\n# end from input known hosts\n`
} }
knownHosts += `# Begin implicitly added github.com\ngithub.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=\n# End implicitly added github.com\n` knownHosts += `# Begin implicitly added github.com\ngithub.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\n# End implicitly added github.com\n`
this.sshKnownHostsPath = path.join(runnerTemp, `${uniqueId}_known_hosts`) this.sshKnownHostsPath = path.join(runnerTemp, `${uniqueId}_known_hosts`)
stateHelper.setSshKnownHostsPath(this.sshKnownHostsPath) stateHelper.setSshKnownHostsPath(this.sshKnownHostsPath)
await fs.promises.writeFile(this.sshKnownHostsPath, knownHosts) await fs.promises.writeFile(this.sshKnownHostsPath, knownHosts)
@ -325,7 +302,7 @@ class GitAuthHelper {
try { try {
await io.rmRF(keyPath) await io.rmRF(keyPath)
} catch (err) { } catch (err) {
core.debug(`${(err as any)?.message ?? err}`) core.debug(err.message)
core.warning(`Failed to remove SSH key '${keyPath}'`) core.warning(`Failed to remove SSH key '${keyPath}'`)
} }
} }
@ -366,8 +343,7 @@ class GitAuthHelper {
const pattern = regexpHelper.escape(configKey) const pattern = regexpHelper.escape(configKey)
await this.git.submoduleForeach( await this.git.submoduleForeach(
// wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline `git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :`,
`sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`,
true true
) )
} }

View File

@ -21,8 +21,7 @@ export interface IGitCommandManager {
config( config(
configKey: string, configKey: string,
configValue: string, configValue: string,
globalConfig?: boolean, globalConfig?: boolean
add?: boolean
): Promise<void> ): Promise<void>
configExists(configKey: string, globalConfig?: boolean): Promise<boolean> configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
fetch(refSpec: string[], fetchDepth?: number): Promise<void> fetch(refSpec: string[], fetchDepth?: number): Promise<void>
@ -32,7 +31,7 @@ export interface IGitCommandManager {
isDetached(): Promise<boolean> isDetached(): Promise<boolean>
lfsFetch(ref: string): Promise<void> lfsFetch(ref: string): Promise<void>
lfsInstall(): Promise<void> lfsInstall(): Promise<void>
log1(format?: string): Promise<string> log1(): Promise<string>
remoteAdd(remoteName: string, remoteUrl: string): Promise<void> remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
removeEnvironmentVariable(name: string): void removeEnvironmentVariable(name: string): void
revParse(ref: string): Promise<string> revParse(ref: string): Promise<string>
@ -94,11 +93,8 @@ class GitCommandManager {
// Note, this implementation uses "rev-parse --symbolic-full-name" because the output from // Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
// "branch --list" is more difficult when in a detached HEAD state. // "branch --list" is more difficult when in a detached HEAD state.
// Note, this implementation uses "rev-parse --symbolic-full-name" because there is a bug
// TODO(https://github.com/actions/checkout/issues/786): this implementation uses // in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names.
// "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names. When
// 2.18 is no longer supported, we can switch back to --symbolic.
const args = ['rev-parse', '--symbolic-full-name'] const args = ['rev-parse', '--symbolic-full-name']
if (remote) { if (remote) {
@ -107,47 +103,19 @@ class GitCommandManager {
args.push('--branches') args.push('--branches')
} }
const stderr: string[] = [] const output = await this.execGit(args)
const errline: string[] = []
const stdout: string[] = []
const stdline: string[] = []
const listeners = { for (let branch of output.stdout.trim().split('\n')) {
stderr: (data: Buffer) => {
stderr.push(data.toString())
},
errline: (data: Buffer) => {
errline.push(data.toString())
},
stdout: (data: Buffer) => {
stdout.push(data.toString())
},
stdline: (data: Buffer) => {
stdline.push(data.toString())
}
}
// Suppress the output in order to avoid flooding annotations with innocuous errors.
await this.execGit(args, false, true, listeners)
core.debug(`stderr callback is: ${stderr}`)
core.debug(`errline callback is: ${errline}`)
core.debug(`stdout callback is: ${stdout}`)
core.debug(`stdline callback is: ${stdline}`)
for (let branch of stdline) {
branch = branch.trim() branch = branch.trim()
if (!branch) { if (branch) {
continue if (branch.startsWith('refs/heads/')) {
} branch = branch.substr('refs/heads/'.length)
} else if (branch.startsWith('refs/remotes/')) {
branch = branch.substr('refs/remotes/'.length)
}
if (branch.startsWith('refs/heads/')) { result.push(branch)
branch = branch.substring('refs/heads/'.length)
} else if (branch.startsWith('refs/remotes/')) {
branch = branch.substring('refs/remotes/'.length)
} }
result.push(branch)
} }
return result return result
@ -172,15 +140,14 @@ class GitCommandManager {
async config( async config(
configKey: string, configKey: string,
configValue: string, configValue: string,
globalConfig?: boolean, globalConfig?: boolean
add?: boolean
): Promise<void> { ): Promise<void> {
const args: string[] = ['config', globalConfig ? '--global' : '--local'] await this.execGit([
if (add) { 'config',
args.push('--add') globalConfig ? '--global' : '--local',
} configKey,
args.push(...[configKey, configValue]) configValue
await this.execGit(args) ])
} }
async configExists( async configExists(
@ -287,10 +254,8 @@ class GitCommandManager {
await this.execGit(['lfs', 'install', '--local']) await this.execGit(['lfs', 'install', '--local'])
} }
async log1(format?: string): Promise<string> { async log1(): Promise<string> {
var args = format ? ['log', '-1', format] : ['log', '-1'] const output = await this.execGit(['log', '-1'])
var silent = format ? false : true
const output = await this.execGit(args, false, silent)
return output.stdout return output.stdout
} }
@ -425,9 +390,7 @@ class GitCommandManager {
private async execGit( private async execGit(
args: string[], args: string[],
allowAllExitCodes = false, allowAllExitCodes = false
silent = false,
customListeners = {}
): Promise<GitOutput> { ): Promise<GitOutput> {
fshelper.directoryExistsSync(this.workingDirectory, true) fshelper.directoryExistsSync(this.workingDirectory, true)
@ -441,29 +404,21 @@ class GitCommandManager {
env[key] = this.gitEnv[key] env[key] = this.gitEnv[key]
} }
const defaultListener = {
stdout: (data: Buffer) => {
stdout.push(data.toString())
}
}
const mergedListeners = {...defaultListener, ...customListeners}
const stdout: string[] = [] const stdout: string[] = []
const options = { const options = {
cwd: this.workingDirectory, cwd: this.workingDirectory,
env, env,
silent,
ignoreReturnCode: allowAllExitCodes, ignoreReturnCode: allowAllExitCodes,
listeners: mergedListeners listeners: {
stdout: (data: Buffer) => {
stdout.push(data.toString())
}
}
} }
result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options) result.exitCode = await exec.exec(`"${this.gitPath}"`, args, options)
result.stdout = stdout.join('') result.stdout = stdout.join('')
core.debug(result.exitCode.toString())
core.debug(result.stdout)
return result return result
} }

View File

@ -39,9 +39,7 @@ export async function prepareExistingDirectory(
try { try {
await io.rmRF(lockPath) await io.rmRF(lockPath)
} catch (error) { } catch (error) {
core.debug( core.debug(`Unable to delete '${lockPath}'. ${error.message}`)
`Unable to delete '${lockPath}'. ${(error as any)?.message ?? error}`
)
} }
} }

View File

@ -36,95 +36,68 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
const git = await getGitCommandManager(settings) const git = await getGitCommandManager(settings)
core.endGroup() core.endGroup()
let authHelper: gitAuthHelper.IGitAuthHelper | null = null // Prepare existing directory, otherwise recreate
try { if (isExisting) {
if (git) { await gitDirectoryHelper.prepareExistingDirectory(
authHelper = gitAuthHelper.createAuthHelper(git, settings) git,
if (settings.setSafeDirectory) { settings.repositoryPath,
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail repositoryUrl,
// Otherwise all git commands we run in a container fail settings.clean,
await authHelper.configureTempGlobalConfig() settings.ref
core.info( )
`Adding repository directory to the temporary git global config as a safe directory` }
)
await git if (!git) {
.config('safe.directory', settings.repositoryPath, true, true) // Downloading using REST API
.catch(error => { core.info(`The repository will be downloaded using the GitHub REST API`)
core.info( core.info(
`Failed to initialize safe directory with error: ${error}` `To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
) )
}) if (settings.submodules) {
throw new Error(
stateHelper.setSafeDirectory() `Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
} )
} } else if (settings.sshKey) {
throw new Error(
// Prepare existing directory, otherwise recreate `Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
if (isExisting) {
await gitDirectoryHelper.prepareExistingDirectory(
git,
settings.repositoryPath,
repositoryUrl,
settings.clean,
settings.ref
) )
} }
if (!git) { await githubApiHelper.downloadRepository(
// Downloading using REST API settings.authToken,
core.info(`The repository will be downloaded using the GitHub REST API`) settings.repositoryOwner,
core.info( settings.repositoryName,
`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH` settings.ref,
) settings.commit,
if (settings.submodules) { settings.repositoryPath
throw new Error( )
`Input 'submodules' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.` return
) }
} else if (settings.sshKey) {
throw new Error(
`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`
)
}
await githubApiHelper.downloadRepository( // Save state for POST action
settings.authToken, stateHelper.setRepositoryPath(settings.repositoryPath)
settings.repositoryOwner,
settings.repositoryName,
settings.ref,
settings.commit,
settings.repositoryPath,
settings.githubServerUrl
)
return
}
// Save state for POST action // Initialize the repository
stateHelper.setRepositoryPath(settings.repositoryPath) if (
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
// Initialize the repository ) {
if ( core.startGroup('Initializing the repository')
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git')) await git.init()
) { await git.remoteAdd('origin', repositoryUrl)
core.startGroup('Initializing the repository')
await git.init()
await git.remoteAdd('origin', repositoryUrl)
core.endGroup()
}
// Disable automatic garbage collection
core.startGroup('Disabling automatic garbage collection')
if (!(await git.tryDisableAutomaticGarbageCollection())) {
core.warning(
`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`
)
}
core.endGroup() core.endGroup()
}
// If we didn't initialize it above, do it now // Disable automatic garbage collection
if (!authHelper) { core.startGroup('Disabling automatic garbage collection')
authHelper = gitAuthHelper.createAuthHelper(git, settings) if (!(await git.tryDisableAutomaticGarbageCollection())) {
} core.warning(
`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`
)
}
core.endGroup()
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
try {
// Configure auth // Configure auth
core.startGroup('Setting up auth') core.startGroup('Setting up auth')
await authHelper.configureAuth() await authHelper.configureAuth()
@ -139,8 +112,7 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
settings.ref = await githubApiHelper.getDefaultBranch( settings.ref = await githubApiHelper.getDefaultBranch(
settings.authToken, settings.authToken,
settings.repositoryOwner, settings.repositoryOwner,
settings.repositoryName, settings.repositoryName
settings.githubServerUrl
) )
} }
core.endGroup() core.endGroup()
@ -198,35 +170,40 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Submodules // Submodules
if (settings.submodules) { if (settings.submodules) {
// Temporarily override global config try {
core.startGroup('Setting up auth for fetching submodules') // Temporarily override global config
await authHelper.configureGlobalAuth() core.startGroup('Setting up auth for fetching submodules')
core.endGroup() await authHelper.configureGlobalAuth()
// Checkout submodules
core.startGroup('Fetching submodules')
await git.submoduleSync(settings.nestedSubmodules)
await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
await git.submoduleForeach(
'git config --local gc.auto 0',
settings.nestedSubmodules
)
core.endGroup()
// Persist credentials
if (settings.persistCredentials) {
core.startGroup('Persisting credentials for submodules')
await authHelper.configureSubmoduleAuth()
core.endGroup() core.endGroup()
// Checkout submodules
core.startGroup('Fetching submodules')
await git.submoduleSync(settings.nestedSubmodules)
await git.submoduleUpdate(
settings.fetchDepth,
settings.nestedSubmodules
)
await git.submoduleForeach(
'git config --local gc.auto 0',
settings.nestedSubmodules
)
core.endGroup()
// Persist credentials
if (settings.persistCredentials) {
core.startGroup('Persisting credentials for submodules')
await authHelper.configureSubmoduleAuth()
core.endGroup()
}
} finally {
// Remove temporary global config override
await authHelper.removeGlobalAuth()
} }
} }
// Get commit information // Dump some info about the checked out commit
const commitInfo = await git.log1() const commitInfo = await git.log1()
// Log commit sha
await git.log1("--format='%H'")
// Check for incorrect pull request merge commit // Check for incorrect pull request merge commit
await refHelper.checkCommitInfo( await refHelper.checkCommitInfo(
settings.authToken, settings.authToken,
@ -234,18 +211,14 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
settings.repositoryOwner, settings.repositoryOwner,
settings.repositoryName, settings.repositoryName,
settings.ref, settings.ref,
settings.commit, settings.commit
settings.githubServerUrl
) )
} finally { } finally {
// Remove auth // Remove auth
if (authHelper) { if (!settings.persistCredentials) {
if (!settings.persistCredentials) { core.startGroup('Removing auth')
core.startGroup('Removing auth') await authHelper.removeAuth()
await authHelper.removeAuth() core.endGroup()
core.endGroup()
}
authHelper.removeGlobalConfig()
} }
} }
} }
@ -268,26 +241,7 @@ export async function cleanup(repositoryPath: string): Promise<void> {
// Remove auth // Remove auth
const authHelper = gitAuthHelper.createAuthHelper(git) const authHelper = gitAuthHelper.createAuthHelper(git)
try { await authHelper.removeAuth()
if (stateHelper.PostSetSafeDirectory) {
// Setup the repository path as a safe directory, so if we pass this into a container job with a different user it doesn't fail
// Otherwise all git commands we run in a container fail
await authHelper.configureTempGlobalConfig()
core.info(
`Adding repository directory to the temporary git global config as a safe directory`
)
await git
.config('safe.directory', repositoryPath, true, true)
.catch(error => {
core.info(`Failed to initialize safe directory with error: ${error}`)
})
}
await authHelper.removeAuth()
} finally {
await authHelper.removeGlobalConfig()
}
} }
async function getGitCommandManager( async function getGitCommandManager(

View File

@ -73,19 +73,4 @@ export interface IGitSourceSettings {
* Indicates whether to persist the credentials on disk to enable scripting authenticated git commands * Indicates whether to persist the credentials on disk to enable scripting authenticated git commands
*/ */
persistCredentials: boolean persistCredentials: boolean
/**
* Organization ID for the currently running workflow (used for auth settings)
*/
workflowOrganizationId: number | undefined
/**
* Indicates whether to add repositoryPath as safe.directory in git global config
*/
setSafeDirectory: boolean
/**
* User override on the GitHub Server/Host URL that hosts the repository to be cloned
*/
githubServerUrl: string | undefined
} }

View File

@ -7,7 +7,7 @@ import * as path from 'path'
import * as retryHelper from './retry-helper' import * as retryHelper from './retry-helper'
import * as toolCache from '@actions/tool-cache' import * as toolCache from '@actions/tool-cache'
import {default as uuid} from 'uuid/v4' import {default as uuid} from 'uuid/v4'
import {getServerApiUrl} from './url-helper' import {Octokit} from '@octokit/rest'
const IS_WINDOWS = process.platform === 'win32' const IS_WINDOWS = process.platform === 'win32'
@ -17,19 +17,18 @@ export async function downloadRepository(
repo: string, repo: string,
ref: string, ref: string,
commit: string, commit: string,
repositoryPath: string, repositoryPath: string
baseUrl?: string
): Promise<void> { ): Promise<void> {
// Determine the default branch // Determine the default branch
if (!ref && !commit) { if (!ref && !commit) {
core.info('Determining the default branch') core.info('Determining the default branch')
ref = await getDefaultBranch(authToken, owner, repo, baseUrl) ref = await getDefaultBranch(authToken, owner, repo)
} }
// Download the archive // Download the archive
let archiveData = await retryHelper.execute(async () => { let archiveData = await retryHelper.execute(async () => {
core.info('Downloading the archive') core.info('Downloading the archive')
return await downloadArchive(authToken, owner, repo, ref, commit, baseUrl) return await downloadArchive(authToken, owner, repo, ref, commit)
}) })
// Write archive to disk // Write archive to disk
@ -48,7 +47,7 @@ export async function downloadRepository(
} else { } else {
await toolCache.extractTar(archivePath, extractPath) await toolCache.extractTar(archivePath, extractPath)
} }
await io.rmRF(archivePath) io.rmRF(archivePath)
// Determine the path of the repository content. The archive contains // Determine the path of the repository content. The archive contains
// a top-level folder and the repository content is inside. // a top-level folder and the repository content is inside.
@ -71,7 +70,7 @@ export async function downloadRepository(
await io.mv(sourcePath, targetPath) await io.mv(sourcePath, targetPath)
} }
} }
await io.rmRF(extractPath) io.rmRF(extractPath)
} }
/** /**
@ -80,26 +79,20 @@ export async function downloadRepository(
export async function getDefaultBranch( export async function getDefaultBranch(
authToken: string, authToken: string,
owner: string, owner: string,
repo: string, repo: string
baseUrl?: string
): Promise<string> { ): Promise<string> {
return await retryHelper.execute(async () => { return await retryHelper.execute(async () => {
core.info('Retrieving the default branch name') core.info('Retrieving the default branch name')
const octokit = github.getOctokit(authToken, { const octokit = new github.GitHub(authToken)
baseUrl: getServerApiUrl(baseUrl)
})
let result: string let result: string
try { try {
// Get the default branch from the repo info // Get the default branch from the repo info
const response = await octokit.rest.repos.get({owner, repo}) const response = await octokit.repos.get({owner, repo})
result = response.data.default_branch result = response.data.default_branch
assert.ok(result, 'default_branch cannot be empty') assert.ok(result, 'default_branch cannot be empty')
} catch (err) { } catch (err) {
// Handle .wiki repo // Handle .wiki repo
if ( if (err['status'] === 404 && repo.toUpperCase().endsWith('.WIKI')) {
(err as any)?.status === 404 &&
repo.toUpperCase().endsWith('.WIKI')
) {
result = 'master' result = 'master'
} }
// Otherwise error // Otherwise error
@ -125,19 +118,21 @@ async function downloadArchive(
owner: string, owner: string,
repo: string, repo: string,
ref: string, ref: string,
commit: string, commit: string
baseUrl?: string
): Promise<Buffer> { ): Promise<Buffer> {
const octokit = github.getOctokit(authToken, { const octokit = new github.GitHub(authToken)
baseUrl: getServerApiUrl(baseUrl) const params: Octokit.ReposGetArchiveLinkParams = {
})
const download = IS_WINDOWS
? octokit.rest.repos.downloadZipballArchive
: octokit.rest.repos.downloadTarballArchive
const response = await download({
owner: owner, owner: owner,
repo: repo, repo: repo,
archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
ref: commit || ref ref: commit || ref
}) }
return Buffer.from(response.data as ArrayBuffer) // response.data is ArrayBuffer const response = await octokit.repos.getArchiveLink(params)
if (response.status != 200) {
throw new Error(
`Unexpected response from GitHub API. Status: ${response.status}, Data: ${response.data}`
)
}
return Buffer.from(response.data) // response.data is ArrayBuffer
} }

View File

@ -2,10 +2,9 @@ import * as core from '@actions/core'
import * as fsHelper from './fs-helper' import * as fsHelper from './fs-helper'
import * as github from '@actions/github' import * as github from '@actions/github'
import * as path from 'path' import * as path from 'path'
import * as workflowContextHelper from './workflow-context-helper'
import {IGitSourceSettings} from './git-source-settings' import {IGitSourceSettings} from './git-source-settings'
export async function getInputs(): Promise<IGitSourceSettings> { export function getInputs(): IGitSourceSettings {
const result = ({} as unknown) as IGitSourceSettings const result = ({} as unknown) as IGitSourceSettings
// GitHub workspace // GitHub workspace
@ -119,16 +118,5 @@ export async function getInputs(): Promise<IGitSourceSettings> {
result.persistCredentials = result.persistCredentials =
(core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE' (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'
// Workflow organization ID
result.workflowOrganizationId = await workflowContextHelper.getOrganizationId()
// Set safe.directory in git global config.
result.setSafeDirectory =
(core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE'
// Determine the GitHub URL that the repository is being hosted from
result.githubServerUrl = core.getInput('github-server-url')
core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
return result return result
} }

View File

@ -7,7 +7,7 @@ import * as stateHelper from './state-helper'
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const sourceSettings = await inputHelper.getInputs() const sourceSettings = inputHelper.getInputs()
try { try {
// Register problem matcher // Register problem matcher
@ -24,7 +24,7 @@ async function run(): Promise<void> {
coreCommand.issueCommand('remove-matcher', {owner: 'checkout-git'}, '') coreCommand.issueCommand('remove-matcher', {owner: 'checkout-git'}, '')
} }
} catch (error) { } catch (error) {
core.setFailed(`${(error as any)?.message ?? error}`) core.setFailed(error.message)
} }
} }
@ -32,7 +32,7 @@ async function cleanup(): Promise<void> {
try { try {
await gitSourceProvider.cleanup(stateHelper.RepositoryPath) await gitSourceProvider.cleanup(stateHelper.RepositoryPath)
} catch (error) { } catch (error) {
core.warning(`${(error as any)?.message ?? error}`) core.warning(error.message)
} }
} }

View File

@ -10,10 +10,10 @@ import * as yaml from 'js-yaml'
function updateUsage( function updateUsage(
actionReference: string, actionReference: string,
actionYamlPath = 'action.yml', actionYamlPath: string = 'action.yml',
readmePath = 'README.md', readmePath: string = 'README.md',
startToken = '<!-- start usage -->', startToken: string = '<!-- start usage -->',
endToken = '<!-- end usage -->' endToken: string = '<!-- end usage -->'
): void { ): void {
if (!actionReference) { if (!actionReference) {
throw new Error('Parameter actionReference must not be empty') throw new Error('Parameter actionReference must not be empty')
@ -120,7 +120,7 @@ function updateUsage(
} }
updateUsage( updateUsage(
'actions/checkout@v3', 'actions/checkout@v2',
path.join(__dirname, '..', '..', 'action.yml'), path.join(__dirname, '..', '..', 'action.yml'),
path.join(__dirname, '..', '..', 'README.md') path.join(__dirname, '..', '..', 'README.md')
) )

View File

@ -1,8 +0,0 @@
#!/bin/bash
set -e
src/misc/licensed-download.sh
echo 'Running: licensed cached'
_temp/licensed-3.6.0/licensed status

View File

@ -1,24 +0,0 @@
#!/bin/bash
set -e
if [ ! -f _temp/licensed-3.6.0.done ]; then
echo 'Clearing temp'
rm -rf _temp/licensed-3.6.0 || true
echo 'Downloading licensed'
mkdir -p _temp/licensed-3.6.0
pushd _temp/licensed-3.6.0
if [[ "$OSTYPE" == "darwin"* ]]; then
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.6.0/licensed-3.6.0-darwin-x64.tar.gz
else
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/3.6.0/licensed-3.6.0-linux-x64.tar.gz
fi
echo 'Extracting licenesed'
tar -xzf licensed.tar.gz
popd
touch _temp/licensed-3.6.0.done
else
echo 'Licensed already downloaded'
fi

View File

@ -1,8 +0,0 @@
#!/bin/bash
set -e
src/misc/licensed-download.sh
echo 'Running: licensed cached'
_temp/licensed-3.6.0/licensed cache

View File

@ -1,7 +1,7 @@
import {URL} from 'url'
import {IGitCommandManager} from './git-command-manager' import {IGitCommandManager} from './git-command-manager'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as github from '@actions/github' import * as github from '@actions/github'
import {getServerApiUrl, isGhes} from './url-helper'
export const tagsRefSpec = '+refs/tags/*:refs/tags/*' export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
@ -183,12 +183,11 @@ export async function checkCommitInfo(
repositoryOwner: string, repositoryOwner: string,
repositoryName: string, repositoryName: string,
ref: string, ref: string,
commit: string, commit: string
baseUrl?: string
): Promise<void> { ): Promise<void> {
try { try {
// GHES? // GHES?
if (isGhes(baseUrl)) { if (isGhes()) {
return return
} }
@ -244,23 +243,17 @@ export async function checkCommitInfo(
core.debug( core.debug(
`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}` `Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`
) )
const octokit = github.getOctokit(token, { const octokit = new github.GitHub(token, {
baseUrl: getServerApiUrl(baseUrl),
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload( userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload(
'number' 'number'
)};run_id=${ )};run_id=${
process.env['GITHUB_RUN_ID'] process.env['GITHUB_RUN_ID']
};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})` };expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
}) })
await octokit.rest.repos.get({ await octokit.repos.get({owner: repositoryOwner, repo: repositoryName})
owner: repositoryOwner,
repo: repositoryName
})
} }
} catch (err) { } catch (err) {
core.debug( core.debug(`Error when validating commit info: ${err.stack}`)
`Error when validating commit info: ${(err as any)?.stack ?? err}`
)
} }
} }
@ -281,3 +274,10 @@ function select(obj: any, path: string): any {
const key = path.substr(0, i) const key = path.substr(0, i)
return select(obj[key], path.substr(i + 1)) return select(obj[key], path.substr(i + 1))
} }
function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
)
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
}

View File

@ -29,7 +29,7 @@ export class RetryHelper {
try { try {
return await action() return await action()
} catch (err) { } catch (err) {
core.info((err as any)?.message) core.info(err.message)
} }
// Sleep // Sleep

View File

@ -1,60 +1,58 @@
import * as core from '@actions/core' import * as coreCommand from '@actions/core/lib/command'
/** /**
* Indicates whether the POST action is running * Indicates whether the POST action is running
*/ */
export const IsPost = !!core.getState('isPost') export const IsPost = !!process.env['STATE_isPost']
/** /**
* The repository path for the POST action. The value is empty during the MAIN action. * The repository path for the POST action. The value is empty during the MAIN action.
*/ */
export const RepositoryPath = core.getState('repositoryPath') export const RepositoryPath =
(process.env['STATE_repositoryPath'] as string) || ''
/**
* The set-safe-directory for the POST action. The value is set if input: 'safe-directory' is set during the MAIN action.
*/
export const PostSetSafeDirectory = core.getState('setSafeDirectory') === 'true'
/** /**
* The SSH key path for the POST action. The value is empty during the MAIN action. * The SSH key path for the POST action. The value is empty during the MAIN action.
*/ */
export const SshKeyPath = core.getState('sshKeyPath') export const SshKeyPath = (process.env['STATE_sshKeyPath'] as string) || ''
/** /**
* The SSH known hosts path for the POST action. The value is empty during the MAIN action. * The SSH known hosts path for the POST action. The value is empty during the MAIN action.
*/ */
export const SshKnownHostsPath = core.getState('sshKnownHostsPath') export const SshKnownHostsPath =
(process.env['STATE_sshKnownHostsPath'] as string) || ''
/** /**
* Save the repository path so the POST action can retrieve the value. * Save the repository path so the POST action can retrieve the value.
*/ */
export function setRepositoryPath(repositoryPath: string) { export function setRepositoryPath(repositoryPath: string) {
core.saveState('repositoryPath', repositoryPath) coreCommand.issueCommand(
'save-state',
{name: 'repositoryPath'},
repositoryPath
)
} }
/** /**
* Save the SSH key path so the POST action can retrieve the value. * Save the SSH key path so the POST action can retrieve the value.
*/ */
export function setSshKeyPath(sshKeyPath: string) { export function setSshKeyPath(sshKeyPath: string) {
core.saveState('sshKeyPath', sshKeyPath) coreCommand.issueCommand('save-state', {name: 'sshKeyPath'}, sshKeyPath)
} }
/** /**
* Save the SSH known hosts path so the POST action can retrieve the value. * Save the SSH known hosts path so the POST action can retrieve the value.
*/ */
export function setSshKnownHostsPath(sshKnownHostsPath: string) { export function setSshKnownHostsPath(sshKnownHostsPath: string) {
core.saveState('sshKnownHostsPath', sshKnownHostsPath) coreCommand.issueCommand(
} 'save-state',
{name: 'sshKnownHostsPath'},
/** sshKnownHostsPath
* Save the set-safe-directory input so the POST action can retrieve the value. )
*/
export function setSafeDirectory() {
core.saveState('setSafeDirectory', 'true')
} }
// Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic. // Publish a variable so that when the POST action runs, it can determine it should run the cleanup logic.
// This is necessary since we don't have a separate entry point. // This is necessary since we don't have a separate entry point.
if (!IsPost) { if (!IsPost) {
core.saveState('isPost', 'true') coreCommand.issueCommand('save-state', {name: 'isPost'}, 'true')
} }

View File

@ -1,6 +1,6 @@
import * as assert from 'assert' import * as assert from 'assert'
import {URL} from 'url'
import {IGitSourceSettings} from './git-source-settings' import {IGitSourceSettings} from './git-source-settings'
import {URL} from 'url'
export function getFetchUrl(settings: IGitSourceSettings): string { export function getFetchUrl(settings: IGitSourceSettings): string {
assert.ok( assert.ok(
@ -8,7 +8,7 @@ export function getFetchUrl(settings: IGitSourceSettings): string {
'settings.repositoryOwner must be defined' 'settings.repositoryOwner must be defined'
) )
assert.ok(settings.repositoryName, 'settings.repositoryName must be defined') assert.ok(settings.repositoryName, 'settings.repositoryName must be defined')
const serviceUrl = getServerUrl(settings.githubServerUrl) const serviceUrl = getServerUrl()
const encodedOwner = encodeURIComponent(settings.repositoryOwner) const encodedOwner = encodeURIComponent(settings.repositoryOwner)
const encodedName = encodeURIComponent(settings.repositoryName) const encodedName = encodeURIComponent(settings.repositoryName)
if (settings.sshKey) { if (settings.sshKey) {
@ -19,27 +19,11 @@ export function getFetchUrl(settings: IGitSourceSettings): string {
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}` return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`
} }
export function getServerUrl(url?: string): URL { export function getServerUrl(): URL {
let urlValue = // todo: remove GITHUB_URL after support for GHES Alpha is no longer needed
url && url.trim().length > 0 return new URL(
? url process.env['GITHUB_SERVER_URL'] ||
: process.env['GITHUB_SERVER_URL'] || 'https://github.com' process.env['GITHUB_URL'] ||
return new URL(urlValue) 'https://github.com'
} )
export function getServerApiUrl(url?: string): string {
let apiUrl = 'https://api.github.com'
if (isGhes(url)) {
const serverUrl = getServerUrl(url)
apiUrl = new URL(`${serverUrl.origin}/api/v3`).toString()
}
return apiUrl
}
export function isGhes(url?: string): boolean {
const ghUrl = getServerUrl(url)
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
} }

View File

@ -1,30 +0,0 @@
import * as core from '@actions/core'
import * as fs from 'fs'
/**
* Gets the organization ID of the running workflow or undefined if the value cannot be loaded from the GITHUB_EVENT_PATH
*/
export async function getOrganizationId(): Promise<number | undefined> {
try {
const eventPath = process.env.GITHUB_EVENT_PATH
if (!eventPath) {
core.debug(`GITHUB_EVENT_PATH is not defined`)
return
}
const content = await fs.promises.readFile(eventPath, {encoding: 'utf8'})
const event = JSON.parse(content)
const id = event?.repository?.owner?.id
if (typeof id !== 'number') {
core.debug('Repository owner ID not found within GITHUB event info')
return
}
return id as number
} catch (err) {
core.debug(
`Unable to load organization ID from GITHUB_EVENT_PATH: ${(err as any)
.message || err}`
)
}
}

View File

@ -10,8 +10,7 @@
"declaration": true, "declaration": true,
"strict": true, "strict": true,
"noImplicitAny": false, "noImplicitAny": false,
"esModuleInterop": true, "esModuleInterop": true
"skipLibCheck": true
}, },
"exclude": ["__test__", "lib", "node_modules"] "exclude": ["__test__", "lib", "node_modules"]
} }