Compare commits

..

3 Commits

Author SHA1 Message Date
eric sciple 95471c29b6 fail when submodules or ssh-key and fallback to REST API 2020-03-12 11:29:02 -04:00
eric sciple 2a15e11d5a update verbiage; add comments 2020-03-12 00:59:09 -04:00
eric sciple 518a867127 persist core.sshCommand for submodules 2020-03-11 19:18:07 -04:00
20 changed files with 676 additions and 14901 deletions

View File

@ -1,40 +1,5 @@
# Changelog
## v2.3.1
- [Fix default branch resolution for .wiki and when using SSH](https://github.com/actions/checkout/pull/284)
## v2.3.0
- [Fallback to the default branch](https://github.com/actions/checkout/pull/278)
## v2.2.0
- [Fetch all history for all tags and branches when fetch-depth=0](https://github.com/actions/checkout/pull/258)
## v2.1.1
- Changes to support GHES ([here](https://github.com/actions/checkout/pull/236) and [here](https://github.com/actions/checkout/pull/248))
## v2.1.0
- [Group output](https://github.com/actions/checkout/pull/191)
- [Changes to support GHES alpha release](https://github.com/actions/checkout/pull/199)
- [Persist core.sshCommand for submodules](https://github.com/actions/checkout/pull/184)
- [Add support ssh](https://github.com/actions/checkout/pull/163)
- [Convert submodule SSH URL to HTTPS, when not using SSH](https://github.com/actions/checkout/pull/179)
- [Add submodule support](https://github.com/actions/checkout/pull/157)
- [Follow proxy settings](https://github.com/actions/checkout/pull/144)
- [Fix ref for pr closed event when a pr is merged](https://github.com/actions/checkout/pull/141)
- [Fix issue checking detached when git less than 2.22](https://github.com/actions/checkout/pull/128)
## v2.0.0
- [Do not pass cred on command line](https://github.com/actions/checkout/pull/108)
- [Add input persist-credentials](https://github.com/actions/checkout/pull/107)
- [Fallback to REST API to download repo](https://github.com/actions/checkout/pull/104)
## v2 (beta)
- Improved fetch performance

View File

@ -6,7 +6,7 @@
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth: 0` to fetch all history for all branches and tags. Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth` to fetch more history. Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out.
@ -18,7 +18,6 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
- 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
@ -27,6 +26,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
- Fallback to REST API download
- When Git 2.18 or higher is not in the PATH, the REST API will be used to download the files
- When using a job container, the container's PATH is used
- Removed input `submodules`
Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous versions.
@ -42,7 +42,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, uses the default branch.
# Otherwise, defaults to `master`.
ref: ''
# Personal access token (PAT) used to fetch the repository. The PAT is configured
@ -89,7 +89,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# Default: true
clean: ''
# Number of commits to fetch. 0 indicates all history for all branches and tags.
# Number of commits to fetch. 0 indicates all history.
# Default: 1
fetch-depth: ''
@ -110,7 +110,6 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# Scenarios
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
- [Checkout a different branch](#Checkout-a-different-branch)
- [Checkout HEAD^](#Checkout-HEAD)
- [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
@ -118,14 +117,10 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
- [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
- [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
## Fetch all history for all tags and branches
```yaml
- uses: actions/checkout@v2
with:
fetch-depth: 0
```
- [Checkout submodules](#Checkout-submodules)
- [Fetch all tags](#Fetch-all-tags)
- [Fetch all branches](#Fetch-all-branches)
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
## Checkout a different branch
@ -213,6 +208,43 @@ jobs:
- uses: actions/checkout@v2
```
## Checkout submodules
```yaml
- uses: actions/checkout@v2
- name: Checkout submodules
shell: bash
run: |
# If your submodules are configured to use SSH instead of HTTPS please uncomment the following line
# git config --global url."https://github.com/".insteadOf "git@github.com:"
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
```
## Fetch all tags
```yaml
- uses: actions/checkout@v2
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
```
## Fetch all branches
```yaml
- uses: actions/checkout@v2
- run: |
git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
```
## Fetch all history for all tags and branches
```yaml
- uses: actions/checkout@v2
- run: |
git fetch --prune --unshallow
```
# License
The scripts and documentation in this project are released under the [MIT License](LICENSE)

View File

@ -714,7 +714,6 @@ async function setup(testName: string): Promise<void> {
),
env: {},
fetch: jest.fn(),
getDefaultBranch: jest.fn(),
getWorkingDirectory: jest.fn(() => workspace),
init: jest.fn(),
isDetached: jest.fn(),
@ -723,11 +722,9 @@ async function setup(testName: string): Promise<void> {
log1: jest.fn(),
remoteAdd: jest.fn(),
removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]),
revParse: jest.fn(),
setEnvironmentVariable: jest.fn((name: string, value: string) => {
git.env[name] = value
}),
shaExists: jest.fn(),
submoduleForeach: jest.fn(async () => {
return ''
}),

View File

@ -9,7 +9,6 @@ const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
let repositoryPath: string
let repositoryUrl: string
let clean: boolean
let ref: string
let git: IGitCommandManager
describe('git-directory-helper tests', () => {
@ -42,8 +41,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -65,8 +63,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -91,8 +88,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -113,8 +109,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -142,8 +137,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -169,8 +163,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
differentRepositoryUrl,
clean,
ref
clean
)
// Assert
@ -194,8 +187,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -220,8 +212,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -245,8 +236,7 @@ describe('git-directory-helper tests', () => {
undefined,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -270,8 +260,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -301,8 +290,7 @@ describe('git-directory-helper tests', () => {
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
@ -317,66 +305,29 @@ describe('git-directory-helper tests', () => {
expect(git.tryReset).not.toHaveBeenCalled()
})
const removesAncestorRemoteBranch = 'removes ancestor remote branch'
it(removesAncestorRemoteBranch, async () => {
const removesRemoteBranches = 'removes local branches'
it(removesRemoteBranches, async () => {
// Arrange
await setup(removesAncestorRemoteBranch)
await setup(removesRemoteBranches)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const mockBranchList = git.branchList as jest.Mock<any, any>
mockBranchList.mockImplementation(async (remote: boolean) => {
return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
return remote ? ['remote-branch-1', 'remote-branch-2'] : []
})
ref = 'remote-branch-1/conflict'
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean,
ref
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.branchDelete).toHaveBeenCalledTimes(1)
expect(git.branchDelete).toHaveBeenCalledWith(
true,
'origin/remote-branch-1'
)
})
const removesDescendantRemoteBranches = 'removes descendant remote branch'
it(removesDescendantRemoteBranches, async () => {
// Arrange
await setup(removesDescendantRemoteBranches)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const mockBranchList = git.branchList as jest.Mock<any, any>
mockBranchList.mockImplementation(async (remote: boolean) => {
return remote
? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2']
: []
})
ref = 'remote-branch-1'
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
clean,
ref
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.branchDelete).toHaveBeenCalledTimes(1)
expect(git.branchDelete).toHaveBeenCalledWith(
true,
'origin/remote-branch-1/conflict'
)
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-1')
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-2')
})
})
@ -393,9 +344,6 @@ async function setup(testName: string): Promise<void> {
// Clean
clean = true
// Ref
ref = ''
// Git command manager
git = {
branchDelete: jest.fn(),
@ -408,7 +356,6 @@ async function setup(testName: string): Promise<void> {
config: jest.fn(),
configExists: jest.fn(),
fetch: jest.fn(),
getDefaultBranch: jest.fn(),
getWorkingDirectory: jest.fn(() => repositoryPath),
init: jest.fn(),
isDetached: jest.fn(),
@ -417,9 +364,7 @@ async function setup(testName: string): Promise<void> {
log1: jest.fn(),
remoteAdd: jest.fn(),
removeEnvironmentVariable: jest.fn(),
revParse: jest.fn(),
setEnvironmentVariable: jest.fn(),
shaExists: jest.fn(),
submoduleForeach: jest.fn(),
submoduleSync: jest.fn(),
submoduleUpdate: jest.fn(),

View File

@ -110,6 +110,13 @@ describe('input-helper tests', () => {
)
})
it('sets correct default ref/sha for other repo', () => {
inputs.repository = 'some-owner/some-other-repo'
const settings: IGitSourceSettings = inputHelper.getInputs()
expect(settings.ref).toBe('refs/heads/master')
expect(settings.commit).toBeFalsy()
})
it('sets ref to empty when explicit sha', () => {
inputs.ref = '1111111111222222222233333333334444444444'
const settings: IGitSourceSettings = inputHelper.getInputs()

View File

@ -12,6 +12,6 @@ if [[ "$(git status --porcelain)" != "" ]]; then
echo ----------------------------------------
echo Troubleshooting
echo ----------------------------------------
echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run format && npm run build"
echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run all"
exit 1
fi

View File

@ -8,7 +8,7 @@ inputs:
description: >
The branch, tag or SHA to checkout. When checking out the repository that
triggered a workflow, this defaults to the reference or SHA for that
event. Otherwise, uses the default branch.
event. Otherwise, defaults to `master`.
token:
description: >
Personal access token (PAT) used to fetch the repository. The PAT is configured
@ -54,7 +54,7 @@ inputs:
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
default: true
fetch-depth:
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
description: 'Number of commits to fetch. 0 indicates all history.'
default: 1
lfs:
description: 'Whether to download Git-LFS files'

View File

@ -29,26 +29,14 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: >
Personal access token (PAT) used to fetch the repository. The PAT is configured
with the local git config, which enables your scripts to run authenticated git
commands. The post-job step removes the PAT.
We recommend using a service account with the least permissions necessary.
Also when generating a new PAT, select the least scopes necessary.
[Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
commands. The post-job step removes the PAT. [Learn more about creating and using
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
default: ${{ github.token }}
ssh-key:
description: >
SSH key used to fetch the repository. The SSH key is configured with the local
SSH key used to fetch the repository. SSH key is configured with the local
git config, which enables your scripts to run authenticated git commands.
The post-job step removes the SSH key.
We recommend using a service account with the least permissions necessary.
[Learn more about creating and using
The post-job step removes the SSH key. [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)
ssh-known-hosts:
description: >
@ -56,10 +44,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example,
`ssh-keyscan github.com`. The public key for github.com is always implicitly added.
ssh-strict:
description: >
Whether to perform strict host key checking. When true, adds the options `StrictHostKeyChecking=yes`
and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to
configure additional hosts.
description: 'Whether to perform strict host key checking'
default: true
persist-credentials:
description: 'Whether to configure the token or SSH key with the local git config'
@ -70,7 +55,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
default: true
fetch-depth:
description: 'Number of commits to fetch. 0 indicates all history for all tags and branches.'
description: 'Number of commits to fetch. 0 indicates all history.'
default: 1
lfs:
description: 'Whether to download Git-LFS files'
@ -79,11 +64,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: >
Whether to checkout submodules: `true` to checkout submodules or `recursive` to
recursively checkout submodules.
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
converted to HTTPS.
default: false
default: 'false'
```
Note:

14679
dist/index.js vendored

File diff suppressed because one or more lines are too long

125
package-lock.json generated
View File

@ -15,19 +15,19 @@
"integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ=="
},
"@actions/github": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-2.2.0.tgz",
"integrity": "sha512-9UAZqn8ywdR70n3GwVle4N8ALosQs4z50N7XMXrSTUVOmVpaBC5kE3TRTT7qQdi3OaQV24mjGuJZsHUmhD+ZXw==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-2.1.0.tgz",
"integrity": "sha512-G4ncMlh4pLLAvNgHUYUtpWQ1zPf/VYqmRH9oshxLabdaOOnp7i1hgSgzr2xne2YUaSND3uqemd3YYTIsm2f/KQ==",
"requires": {
"@actions/http-client": "^1.0.3",
"@octokit/graphql": "^4.3.1",
"@octokit/rest": "^16.43.1"
"@octokit/rest": "^16.15.0"
}
},
"@actions/http-client": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
"integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.3.tgz",
"integrity": "sha512-wFwh1U4adB/Zsk4cc9kVqaBOHoknhp/pJQk+aWTocbAZWpIl4Zx/At83WFRLXvxB+5HVTWOACM6qjULMZfQSfw==",
"requires": {
"tunnel": "0.0.6"
},
@ -622,23 +622,13 @@
}
},
"@octokit/endpoint": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.1.tgz",
"integrity": "sha512-pOPHaSz57SFT/m3R5P8MUu4wLPszokn5pXcB/pzavLTQf2jbU+6iayTvzaY6/BiotuRS0qyEUkx3QglT4U958A==",
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz",
"integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==",
"requires": {
"@octokit/types": "^2.11.1",
"@octokit/types": "^2.0.0",
"is-plain-object": "^3.0.0",
"universal-user-agent": "^5.0.0"
},
"dependencies": {
"universal-user-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz",
"integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==",
"requires": {
"os-name": "^3.1.0"
}
}
"universal-user-agent": "^4.0.0"
}
},
"@octokit/graphql": {
@ -651,57 +641,25 @@
"universal-user-agent": "^4.0.0"
}
},
"@octokit/plugin-paginate-rest": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz",
"integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==",
"requires": {
"@octokit/types": "^2.0.1"
}
},
"@octokit/plugin-request-log": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz",
"integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw=="
},
"@octokit/plugin-rest-endpoint-methods": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz",
"integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==",
"requires": {
"@octokit/types": "^2.0.1",
"deprecation": "^2.3.1"
}
},
"@octokit/request": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.2.tgz",
"integrity": "sha512-zKdnGuQ2TQ2vFk9VU8awFT4+EYf92Z/v3OlzRaSh4RIP0H6cvW1BFPXq4XYvNez+TPQjqN+0uSkCYnMFFhcFrw==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz",
"integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==",
"requires": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.0.0",
"@octokit/types": "^2.11.1",
"@octokit/endpoint": "^5.5.0",
"@octokit/request-error": "^1.0.1",
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
"is-plain-object": "^3.0.0",
"node-fetch": "^2.3.0",
"once": "^1.4.0",
"universal-user-agent": "^5.0.0"
},
"dependencies": {
"universal-user-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz",
"integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==",
"requires": {
"os-name": "^3.1.0"
}
}
"universal-user-agent": "^4.0.0"
}
},
"@octokit/request-error": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz",
"integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz",
"integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==",
"requires": {
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
@ -709,14 +667,11 @@
}
},
"@octokit/rest": {
"version": "16.43.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz",
"integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==",
"version": "16.38.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.38.1.tgz",
"integrity": "sha512-zyNFx+/Bd1EXt7LQjfrc6H4wryBQ/oDuZeZhGMBSFr1eMPFDmpEweFQR3R25zjKwBQpDY7L5GQO6A3XSaOfV1w==",
"requires": {
"@octokit/auth-token": "^2.4.0",
"@octokit/plugin-paginate-rest": "^1.1.1",
"@octokit/plugin-request-log": "^1.0.0",
"@octokit/plugin-rest-endpoint-methods": "2.4.0",
"@octokit/request": "^5.2.0",
"@octokit/request-error": "^1.0.2",
"atob-lite": "^2.0.0",
@ -729,24 +684,12 @@
"octokit-pagination-methods": "^1.1.0",
"once": "^1.4.0",
"universal-user-agent": "^4.0.0"
},
"dependencies": {
"@octokit/request-error": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz",
"integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==",
"requires": {
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
}
}
},
"@octokit/types": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.14.0.tgz",
"integrity": "sha512-1w2wxpN45rEXPDFeB7rGain7wcJ/aTRg8bdILITVnS0O7a4zEGELa3JmIe+jeLdekQjvZRbVfNPqS+mi5fKCKQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.1.tgz",
"integrity": "sha512-89LOYH+d/vsbDX785NOfLxTW88GjNd0lWRz1DVPVsZgg9Yett5O+3MOvwo7iHgvUwbFz0mf/yPIjBkUbs4kxoQ==",
"requires": {
"@types/node": ">= 8"
}
@ -6777,9 +6720,9 @@
}
},
"universal-user-agent": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz",
"integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz",
"integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==",
"requires": {
"os-name": "^3.1.0"
}
@ -6958,9 +6901,9 @@
"dev": true
},
"windows-release": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz",
"integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz",
"integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==",
"requires": {
"execa": "^1.0.0"
}

View File

@ -5,8 +5,8 @@
"main": "lib/main.js",
"scripts": {
"build": "tsc && ncc build && node lib/misc/generate-docs.js",
"format": "prettier --write '**/*.ts'",
"format-check": "prettier --check '**/*.ts'",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts",
"test": "jest"
},
@ -28,7 +28,7 @@
"dependencies": {
"@actions/core": "^1.1.3",
"@actions/exec": "^1.0.1",
"@actions/github": "^2.2.0",
"@actions/github": "^2.0.2",
"@actions/io": "^1.0.1",
"@actions/tool-cache": "^1.1.2",
"uuid": "^3.3.3"

View File

@ -7,12 +7,12 @@ import * as os from 'os'
import * as path from 'path'
import * as regexpHelper from './regexp-helper'
import * as stateHelper from './state-helper'
import * as urlHelper from './url-helper'
import {default as uuid} from 'uuid/v4'
import {IGitCommandManager} from './git-command-manager'
import {IGitSourceSettings} from './git-source-settings'
const IS_WINDOWS = process.platform === 'win32'
const HOSTNAME = 'github.com'
const SSH_COMMAND_KEY = 'core.sshCommand'
export interface IGitAuthHelper {
@ -33,15 +33,15 @@ export function createAuthHelper(
class GitAuthHelper {
private readonly git: IGitCommandManager
private readonly settings: IGitSourceSettings
private readonly tokenConfigKey: string
private readonly tokenConfigValue: string
private readonly tokenConfigKey: string = `http.https://${HOSTNAME}/.extraheader`
private readonly tokenPlaceholderConfigValue: string
private readonly insteadOfKey: string
private readonly insteadOfValue: string
private readonly insteadOfKey: string = `url.https://${HOSTNAME}/.insteadOf`
private readonly insteadOfValue: string = `git@${HOSTNAME}:`
private sshCommand = ''
private sshKeyPath = ''
private sshKnownHostsPath = ''
private temporaryHomePath = ''
private tokenConfigValue: string
constructor(
gitCommandManager: IGitCommandManager,
@ -51,8 +51,6 @@ class GitAuthHelper {
this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings)
// Token auth header
const serverUrl = urlHelper.getServerUrl()
this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT]
const basicCredential = Buffer.from(
`x-access-token:${this.settings.authToken}`,
'utf8'
@ -60,10 +58,6 @@ class GitAuthHelper {
core.setSecret(basicCredential)
this.tokenPlaceholderConfigValue = `AUTHORIZATION: basic ***`
this.tokenConfigValue = `AUTHORIZATION: basic ${basicCredential}`
// Instead of SSH URL
this.insteadOfKey = `url.${serverUrl.origin}/.insteadOf` // "origin" is SCHEME://HOSTNAME[:PORT]
this.insteadOfValue = `git@${serverUrl.hostname}:`
}
async configureAuth(): Promise<void> {
@ -173,7 +167,7 @@ class GitAuthHelper {
}
async removeGlobalAuth(): Promise<void> {
core.debug(`Unsetting HOME override`)
core.info(`Unsetting HOME override`)
this.git.removeEnvironmentVariable('HOME')
await io.rmRF(this.temporaryHomePath)
}

View File

@ -3,7 +3,6 @@ import * as exec from '@actions/exec'
import * as fshelper from './fs-helper'
import * as io from '@actions/io'
import * as path from 'path'
import * as refHelper from './ref-helper'
import * as regexpHelper from './regexp-helper'
import * as retryHelper from './retry-helper'
import {GitVersion} from './git-version'
@ -24,19 +23,16 @@ export interface IGitCommandManager {
globalConfig?: boolean
): Promise<void>
configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
fetch(refSpec: string[], fetchDepth?: number): Promise<void>
getDefaultBranch(repositoryUrl: string): Promise<string>
fetch(fetchDepth: number, refSpec: string[]): Promise<void>
getWorkingDirectory(): string
init(): Promise<void>
isDetached(): Promise<boolean>
lfsFetch(ref: string): Promise<void>
lfsInstall(): Promise<void>
log1(): Promise<string>
log1(): Promise<void>
remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
removeEnvironmentVariable(name: string): void
revParse(ref: string): Promise<string>
setEnvironmentVariable(name: string, value: string): void
shaExists(sha: string): Promise<boolean>
submoduleForeach(command: string, recursive: boolean): Promise<string>
submoduleSync(recursive: boolean): Promise<void>
submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
@ -168,14 +164,17 @@ class GitCommandManager {
return output.exitCode === 0
}
async fetch(refSpec: string[], fetchDepth?: number): Promise<void> {
const args = ['-c', 'protocol.version=2', 'fetch']
if (!refSpec.some(x => x === refHelper.tagsRefSpec)) {
args.push('--no-tags')
}
args.push('--prune', '--progress', '--no-recurse-submodules')
if (fetchDepth && fetchDepth > 0) {
async fetch(fetchDepth: number, refSpec: string[]): Promise<void> {
const args = [
'-c',
'protocol.version=2',
'fetch',
'--no-tags',
'--prune',
'--progress',
'--no-recurse-submodules'
]
if (fetchDepth > 0) {
args.push(`--depth=${fetchDepth}`)
} else if (
fshelper.fileExistsSync(
@ -196,34 +195,6 @@ class GitCommandManager {
})
}
async getDefaultBranch(repositoryUrl: string): Promise<string> {
let output: GitOutput | undefined
await retryHelper.execute(async () => {
output = await this.execGit([
'ls-remote',
'--quiet',
'--exit-code',
'--symref',
repositoryUrl,
'HEAD'
])
})
if (output) {
// Satisfy compiler, will always be set
for (let line of output.stdout.trim().split('\n')) {
line = line.trim()
if (line.startsWith('ref:') || line.endsWith('HEAD')) {
return line
.substr('ref:'.length, line.length - 'ref:'.length - 'HEAD'.length)
.trim()
}
}
}
throw new Error('Unexpected output when retrieving default branch')
}
getWorkingDirectory(): string {
return this.workingDirectory
}
@ -254,9 +225,8 @@ class GitCommandManager {
await this.execGit(['lfs', 'install', '--local'])
}
async log1(): Promise<string> {
const output = await this.execGit(['log', '-1'])
return output.stdout
async log1(): Promise<void> {
await this.execGit(['log', '-1'])
}
async remoteAdd(remoteName: string, remoteUrl: string): Promise<void> {
@ -267,27 +237,10 @@ class GitCommandManager {
delete this.gitEnv[name]
}
/**
* Resolves a ref to a SHA. For a branch or lightweight tag, the commit SHA is returned.
* For an annotated tag, the tag SHA is returned.
* @param {string} ref For example: 'refs/heads/master' or '/refs/tags/v1'
* @returns {Promise<string>}
*/
async revParse(ref: string): Promise<string> {
const output = await this.execGit(['rev-parse', ref])
return output.stdout.trim()
}
setEnvironmentVariable(name: string, value: string): void {
this.gitEnv[name] = value
}
async shaExists(sha: string): Promise<boolean> {
const args = ['rev-parse', '--verify', '--quiet', `${sha}^{object}`]
const output = await this.execGit(args, true)
return output.exitCode === 0
}
async submoduleForeach(command: string, recursive: boolean): Promise<string> {
const args = ['submodule', 'foreach']
if (recursive) {

View File

@ -1,4 +1,3 @@
import * as assert from 'assert'
import * as core from '@actions/core'
import * as fs from 'fs'
import * as fsHelper from './fs-helper'
@ -10,13 +9,8 @@ export async function prepareExistingDirectory(
git: IGitCommandManager | undefined,
repositoryPath: string,
repositoryUrl: string,
clean: boolean,
ref: string
clean: boolean
): Promise<void> {
assert.ok(repositoryPath, 'Expected repositoryPath to be defined')
assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined')
// Indicates whether to delete the directory contents
let remove = false
// Check whether using git or REST API
@ -44,7 +38,6 @@ export async function prepareExistingDirectory(
}
try {
core.startGroup('Removing previously created refs, to avoid conflicts')
// Checkout detached HEAD
if (!(await git.isDetached())) {
await git.checkoutDetach()
@ -56,32 +49,14 @@ export async function prepareExistingDirectory(
await git.branchDelete(false, branch)
}
// Remove any conflicting refs/remotes/origin/*
// Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar
// Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo
if (ref) {
ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`
if (ref.startsWith('refs/heads/')) {
const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length)
const upperName1Slash = `${upperName1}/`
branches = await git.branchList(true)
for (const branch of branches) {
const upperName2 = branch.substr('origin/'.length).toUpperCase()
const upperName2Slash = `${upperName2}/`
if (
upperName1.startsWith(upperName2Slash) ||
upperName2.startsWith(upperName1Slash)
) {
await git.branchDelete(true, branch)
}
}
}
// Remove all refs/remotes/origin/* to avoid conflicts
branches = await git.branchList(true)
for (const branch of branches) {
await git.branchDelete(true, branch)
}
core.endGroup()
// Clean
if (clean) {
core.startGroup('Cleaning the repository')
if (!(await git.tryClean())) {
core.debug(
`The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`
@ -90,7 +65,6 @@ export async function prepareExistingDirectory(
} else if (!(await git.tryReset())) {
remove = true
}
core.endGroup()
if (remove) {
core.warning(

View File

@ -8,16 +8,23 @@ import * as io from '@actions/io'
import * as path from 'path'
import * as refHelper from './ref-helper'
import * as stateHelper from './state-helper'
import * as urlHelper from './url-helper'
import {IGitCommandManager} from './git-command-manager'
import {IGitSourceSettings} from './git-source-settings'
const hostname = 'github.com'
export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Repository URL
core.info(
`Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}`
)
const repositoryUrl = urlHelper.getFetchUrl(settings)
const repositoryUrl = settings.sshKey
? `git@${hostname}:${encodeURIComponent(
settings.repositoryOwner
)}/${encodeURIComponent(settings.repositoryName)}.git`
: `https://${hostname}/${encodeURIComponent(
settings.repositoryOwner
)}/${encodeURIComponent(settings.repositoryName)}`
// Remove conflicting file path
if (fsHelper.fileExistsSync(settings.repositoryPath)) {
@ -32,9 +39,7 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
}
// Git command manager
core.startGroup('Getting Git version info')
const git = await getGitCommandManager(settings)
core.endGroup()
// Prepare existing directory, otherwise recreate
if (isExisting) {
@ -42,8 +47,7 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
git,
settings.repositoryPath,
repositoryUrl,
settings.clean,
settings.ref
settings.clean
)
}
@ -81,42 +85,21 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
if (
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
) {
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()
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
try {
// Configure auth
core.startGroup('Setting up auth')
await authHelper.configureAuth()
core.endGroup()
// Determine the default branch
if (!settings.ref && !settings.commit) {
core.startGroup('Determining the default branch')
if (settings.sshKey) {
settings.ref = await git.getDefaultBranch(repositoryUrl)
} else {
settings.ref = await githubApiHelper.getDefaultBranch(
settings.authToken,
settings.repositoryOwner,
settings.repositoryName
)
}
core.endGroup()
}
// LFS install
if (settings.lfs) {
@ -124,60 +107,33 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
}
// Fetch
core.startGroup('Fetching the repository')
if (settings.fetchDepth <= 0) {
// Fetch all branches and tags
let refSpec = refHelper.getRefSpecForAllHistory(
settings.ref,
settings.commit
)
await git.fetch(refSpec)
// When all history is fetched, the ref we're interested in may have moved to a different
// commit (push or force push). If so, fetch again with a targeted refspec.
if (!(await refHelper.testRef(git, settings.ref, settings.commit))) {
refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
await git.fetch(refSpec)
}
} else {
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
await git.fetch(refSpec, settings.fetchDepth)
}
core.endGroup()
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
await git.fetch(settings.fetchDepth, refSpec)
// Checkout info
core.startGroup('Determining the checkout info')
const checkoutInfo = await refHelper.getCheckoutInfo(
git,
settings.ref,
settings.commit
)
core.endGroup()
// LFS fetch
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
// Explicit lfs fetch will fetch lfs objects in parallel.
if (settings.lfs) {
core.startGroup('Fetching LFS objects')
await git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref)
core.endGroup()
}
// Checkout
core.startGroup('Checking out the ref')
await git.checkout(checkoutInfo.ref, checkoutInfo.startPoint)
core.endGroup()
// Submodules
if (settings.submodules) {
try {
// Temporarily override global config
core.startGroup('Setting up auth for fetching submodules')
await authHelper.configureGlobalAuth()
core.endGroup()
// Checkout submodules
core.startGroup('Fetching submodules')
await git.submoduleSync(settings.nestedSubmodules)
await git.submoduleUpdate(
settings.fetchDepth,
@ -187,13 +143,10 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
'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
@ -202,23 +155,11 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
}
// Dump some info about the checked out commit
const commitInfo = await git.log1()
// Check for incorrect pull request merge commit
await refHelper.checkCommitInfo(
settings.authToken,
commitInfo,
settings.repositoryOwner,
settings.repositoryName,
settings.ref,
settings.commit
)
await git.log1()
} finally {
// Remove auth
if (!settings.persistCredentials) {
core.startGroup('Removing auth')
await authHelper.removeAuth()
core.endGroup()
}
}
}

View File

@ -1,76 +1,17 @@
export interface IGitSourceSettings {
/**
* The location on disk where the repository will be placed
*/
repositoryPath: string
/**
* The repository owner
*/
repositoryOwner: string
/**
* The repository name
*/
repositoryName: string
/**
* The ref to fetch
*/
ref: string
/**
* The commit to checkout
*/
commit: string
/**
* Indicates whether to clean the repository
*/
clean: boolean
/**
* The depth when fetching
*/
fetchDepth: number
/**
* Indicates whether to fetch LFS objects
*/
lfs: boolean
/**
* Indicates whether to checkout submodules
*/
submodules: boolean
/**
* Indicates whether to recursively checkout submodules
*/
nestedSubmodules: boolean
/**
* The auth token to use when fetching the repository
*/
authToken: string
/**
* The SSH key to configure
*/
sshKey: string
/**
* Additional SSH known hosts
*/
sshKnownHosts: string
/**
* Indicates whether the server must be a known host
*/
sshStrict: boolean
/**
* Indicates whether to persist the credentials on disk to enable scripting authenticated git commands
*/
persistCredentials: boolean
}

View File

@ -7,7 +7,7 @@ import * as path from 'path'
import * as retryHelper from './retry-helper'
import * as toolCache from '@actions/tool-cache'
import {default as uuid} from 'uuid/v4'
import {Octokit} from '@octokit/rest'
import {ReposGetArchiveLinkParams} from '@octokit/rest'
const IS_WINDOWS = process.platform === 'win32'
@ -19,12 +19,6 @@ export async function downloadRepository(
commit: string,
repositoryPath: string
): Promise<void> {
// Determine the default branch
if (!ref && !commit) {
core.info('Determining the default branch')
ref = await getDefaultBranch(authToken, owner, repo)
}
// Download the archive
let archiveData = await retryHelper.execute(async () => {
core.info('Downloading the archive')
@ -73,46 +67,6 @@ export async function downloadRepository(
io.rmRF(extractPath)
}
/**
* Looks up the default branch name
*/
export async function getDefaultBranch(
authToken: string,
owner: string,
repo: string
): Promise<string> {
return await retryHelper.execute(async () => {
core.info('Retrieving the default branch name')
const octokit = new github.GitHub(authToken)
let result: string
try {
// Get the default branch from the repo info
const response = await octokit.repos.get({owner, repo})
result = response.data.default_branch
assert.ok(result, 'default_branch cannot be empty')
} catch (err) {
// Handle .wiki repo
if (err['status'] === 404 && repo.toUpperCase().endsWith('.WIKI')) {
result = 'master'
}
// Otherwise error
else {
throw err
}
}
// Print the default branch
core.info(`Default branch '${result}'`)
// Prefix with 'refs/heads'
if (!result.startsWith('refs/')) {
result = `refs/heads/${result}`
}
return result
})
}
async function downloadArchive(
authToken: string,
owner: string,
@ -121,7 +75,7 @@ async function downloadArchive(
commit: string
): Promise<Buffer> {
const octokit = new github.GitHub(authToken)
const params: Octokit.ReposGetArchiveLinkParams = {
const params: ReposGetArchiveLinkParams = {
owner: owner,
repo: repo,
archive_format: IS_WINDOWS ? 'zipball' : 'tarball',

View File

@ -68,6 +68,10 @@ export function getInputs(): IGitSourceSettings {
result.ref = `refs/heads/${result.ref}`
}
}
if (!result.ref && !result.commit) {
result.ref = 'refs/heads/master'
}
}
// SHA?
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) {
@ -106,7 +110,7 @@ export function getInputs(): IGitSourceSettings {
core.debug(`recursive submodules = ${result.nestedSubmodules}`)
// Auth token
result.authToken = core.getInput('token', {required: true})
result.authToken = core.getInput('token')
// SSH
result.sshKey = core.getInput('ssh-key')

View File

@ -1,9 +1,4 @@
import {URL} from 'url'
import {IGitCommandManager} from './git-command-manager'
import * as core from '@actions/core'
import * as github from '@actions/github'
export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
export interface ICheckoutInfo {
ref: string
@ -62,16 +57,6 @@ export async function getCheckoutInfo(
return result
}
export function getRefSpecForAllHistory(ref: string, commit: string): string[] {
const result = ['+refs/heads/*:refs/remotes/origin/*', tagsRefSpec]
if (ref && ref.toUpperCase().startsWith('REFS/PULL/')) {
const branch = ref.substring('refs/pull/'.length)
result.push(`+${commit || ref}:refs/remotes/pull/${branch}`)
}
return result
}
export function getRefSpec(ref: string, commit: string): string[] {
if (!ref && !commit) {
throw new Error('Args ref and commit cannot both be empty')
@ -122,162 +107,3 @@ export function getRefSpec(ref: string, commit: string): string[] {
return [`+${ref}:${ref}`]
}
}
/**
* Tests whether the initial fetch created the ref at the expected commit
*/
export async function testRef(
git: IGitCommandManager,
ref: string,
commit: string
): Promise<boolean> {
if (!git) {
throw new Error('Arg git cannot be empty')
}
if (!ref && !commit) {
throw new Error('Args ref and commit cannot both be empty')
}
// No SHA? Nothing to test
if (!commit) {
return true
}
// SHA only?
else if (!ref) {
return await git.shaExists(commit)
}
const upperRef = ref.toUpperCase()
// refs/heads/
if (upperRef.startsWith('REFS/HEADS/')) {
const branch = ref.substring('refs/heads/'.length)
return (
(await git.branchExists(true, `origin/${branch}`)) &&
commit === (await git.revParse(`refs/remotes/origin/${branch}`))
)
}
// refs/pull/
else if (upperRef.startsWith('REFS/PULL/')) {
// Assume matches because fetched using the commit
return true
}
// refs/tags/
else if (upperRef.startsWith('REFS/TAGS/')) {
const tagName = ref.substring('refs/tags/'.length)
return (
(await git.tagExists(tagName)) && commit === (await git.revParse(ref))
)
}
// Unexpected
else {
core.debug(`Unexpected ref format '${ref}' when testing ref info`)
return true
}
}
export async function checkCommitInfo(
token: string,
commitInfo: string,
repositoryOwner: string,
repositoryName: string,
ref: string,
commit: string
): Promise<void> {
try {
// GHES?
if (isGhes()) {
return
}
// Auth token?
if (!token) {
return
}
// Public PR synchronize, for workflow repo?
if (
fromPayload('repository.private') !== false ||
github.context.eventName !== 'pull_request' ||
fromPayload('action') !== 'synchronize' ||
repositoryOwner !== github.context.repo.owner ||
repositoryName !== github.context.repo.repo ||
ref !== github.context.ref ||
!ref.startsWith('refs/pull/') ||
commit !== github.context.sha
) {
return
}
// Head SHA
const expectedHeadSha = fromPayload('after')
if (!expectedHeadSha) {
core.debug('Unable to determine head sha')
return
}
// Base SHA
const expectedBaseSha = fromPayload('pull_request.base.sha')
if (!expectedBaseSha) {
core.debug('Unable to determine base sha')
return
}
// Expected message?
const expectedMessage = `Merge ${expectedHeadSha} into ${expectedBaseSha}`
if (commitInfo.indexOf(expectedMessage) >= 0) {
return
}
// Extract details from message
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/)
if (!match) {
core.debug('Unexpected message format')
return
}
// Post telemetry
const actualHeadSha = match[1]
if (actualHeadSha !== expectedHeadSha) {
core.debug(
`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`
)
const octokit = new github.GitHub(token, {
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload(
'number'
)};run_id=${
process.env['GITHUB_RUN_ID']
};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
})
await octokit.repos.get({owner: repositoryOwner, repo: repositoryName})
}
} catch (err) {
core.debug(`Error when validating commit info: ${err.stack}`)
}
}
function fromPayload(path: string): any {
return select(github.context.payload, path)
}
function select(obj: any, path: string): any {
if (!obj) {
return undefined
}
const i = path.indexOf('.')
if (i < 0) {
return obj[path]
}
const key = path.substr(0, i)
return select(obj[key], path.substr(i + 1))
}
function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
)
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
}

View File

@ -1,29 +0,0 @@
import * as assert from 'assert'
import {IGitSourceSettings} from './git-source-settings'
import {URL} from 'url'
export function getFetchUrl(settings: IGitSourceSettings): string {
assert.ok(
settings.repositoryOwner,
'settings.repositoryOwner must be defined'
)
assert.ok(settings.repositoryName, 'settings.repositoryName must be defined')
const serviceUrl = getServerUrl()
const encodedOwner = encodeURIComponent(settings.repositoryOwner)
const encodedName = encodeURIComponent(settings.repositoryName)
if (settings.sshKey) {
return `git@${serviceUrl.hostname}:${encodedOwner}/${encodedName}.git`
}
// "origin" is SCHEME://HOSTNAME[:PORT]
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`
}
export function getServerUrl(): URL {
// todo: remove GITHUB_URL after support for GHES Alpha is no longer needed
return new URL(
process.env['GITHUB_SERVER_URL'] ||
process.env['GITHUB_URL'] ||
'https://github.com'
)
}