1
0
mirror of https://github.com/actions/checkout.git synced 2026-06-22 20:01:21 +08:00

Compare commits

..

29 Commits

Author SHA1 Message Date
eric sciple d668097b24 . 2021-07-12 11:09:36 -05:00
eric sciple 4edfe9f331 Update main.ts 2021-07-12 11:06:24 -05:00
Ross Brodbeck 25a956c84d Create CODEOWNERS 2021-02-04 12:25:41 -05:00
Johannes Schindelin 5a4ac9002d Add missing awaits (#379)
* auth-helper: properly await replacement of the token value in the config

After writing the `.extraheader` config, we manually replace the token
with the actual value. This is done in an `async` function, but we were
not `await`ing the result.

In our tests, this commit fixes a flakiness we observed where
`remote.origin.url` sometimes (very rarely, actually) is not set for
submodules. Our interpretation is that the configs are in the process of
being rewritten with the correct token value _while_ another `git
config` that wants to set the `insteadOf` value is reading the config,
which is currently empty.

A more idiomatic way to fix this in Typescript would use
`Promise.all()`, like this:

      await Promise.all(
        configPaths.map(async configPath => {
          core.debug(`Replacing token placeholder in '${configPath}'`)
          await this.replaceTokenPlaceholder(configPath)
        })
      )

However, during review of https://github.com/actions/checkout/pull/379
it was decided to keep the `for` loop in the interest of simplicity.

Reported by Ian Lynagh.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

* downloadRepository(): await the result of recursive deletions

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

* Ask ESLint to report floating Promises

This rule is quite helpful in avoiding hard-to-debug missing `await`s.

Note: there are two locations in `src/main.ts` that trigger warnings:
the `run()` and the `cleanup()` function are called without `await` and
without any `.catch()` clause.

In the initial version of https://github.com/actions/checkout/pull/379,
this was addressed by adding `.catch()` clauses. However, it was
determined that this is boilerplate code that will need to be fixed in a
broader way.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

* Rebuild

This trick was brought to you by `npm ci && npm run build`. Needed to
get the PR build to pass.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2020-11-03 09:44:09 -05:00
Thomas Boop c952173edf Swap to Environment Files (#360) 2020-09-30 11:41:09 -04:00
Thomas Boop a81bbbf829 Remove unneeded commit information from build logs (#345)
* Remove unneeded commit information from stdout
2020-09-23 09:41:47 -04:00
Thomas Boop 21dc310f19 Add Licensed To Help Verify Prod Licenses (#326)
* Add Licensed file and workflow

* manual updates of dependencies

* Delete licenses.txt

* Ignore Generated Files in Git PR's
2020-09-10 09:24:29 -04:00
Thomas Boop be6c44d969 Revert "Delete licenses.txt" 2020-08-11 19:41:01 -04:00
Thomas Boop dac8cc78a1 Delete licenses.txt 2020-08-11 19:36:35 -04:00
Thomas Boop 2036a08e25 Add Third Party License Information to Dist Files (#320) 2020-08-07 09:22:39 -04:00
eric sciple 592cf69a22 Update README.md 2020-07-14 16:30:57 -04:00
eric sciple a4b69b4886 Update README.md 2020-07-14 13:08:52 -04:00
eric sciple 1433f62caa update default branch (#305) 2020-07-14 09:23:30 -04:00
eric sciple 61b9e3751b improve description for fetch-depth (#301) 2020-07-12 21:02:24 -04:00
eric sciple 28c7f3d2b5 changelog 2020-06-18 10:27:39 -04:00
eric sciple fb6f360df2 fix default branch for .wiki and when using ssh (#284) 2020-06-18 10:20:33 -04:00
eric sciple b4483adec3 changelog 2020-06-16 13:48:53 -04:00
eric sciple 00a3be8934 determine default branch (#278) 2020-06-16 13:41:01 -04:00
eric sciple 453ee27fca update troubleshooting instructions to include 'npm run format' 2020-05-31 17:48:51 -04:00
Daniel Hwang 65865e15a1 build because all is no more (#264) 2020-05-31 17:46:53 -04:00
eric sciple aabbfeb2ce changelog 2020-05-27 12:37:40 -04:00
eric sciple e52d022eb5 Fetch all history for all tags and branches when fetch-depth=0 (#258) 2020-05-27 09:54:28 -04:00
eric sciple 2ff2fbdea4 telemetry for incorrect merge commit (#253) 2020-05-21 11:09:16 -04:00
eric sciple df86c829eb fix readme (#251) 2020-05-20 10:20:52 -04:00
Peter Evans 97b30c411c fix prettier glob pattern (#247) 2020-05-19 12:34:05 -04:00
eric sciple 86f86b36ef changelog 2020-05-19 10:27:02 -04:00
eric sciple 7523e23789 switch GITHUB_URL to GITHUB_SERVER_URL (#248) 2020-05-18 13:05:15 -04:00
eric sciple ac455590d1 consume new @actions/github for GHES support (#236) 2020-05-07 12:11:11 -04:00
eric sciple 94c2de77cc Update changelog 2020-04-02 16:04:37 -04:00
88 changed files with 14761 additions and 663 deletions
+1
View File
@@ -27,6 +27,7 @@
"@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",
+1
View File
@@ -0,0 +1 @@
.licenses/** -diff linguist-generated=true
+20
View File
@@ -0,0 +1,20 @@
name: Licensed
on:
push: {branches: main}
pull_request: {branches: main}
jobs:
test:
runs-on: ubuntu-latest
name: Check licenses
steps:
- uses: actions/checkout@v2
- run: npm ci
- name: Install licensed
run: |
cd $RUNNER_TEMP
curl -Lfs -o licensed.tar.gz https://github.com/github/licensed/releases/download/2.12.2/licensed-2.12.2-linux-x64.tar.gz
sudo tar -xzf licensed.tar.gz
sudo mv licensed /usr/local/bin/licensed
- run: licensed status
+1 -1
View File
@@ -4,7 +4,7 @@ on:
pull_request: pull_request:
push: push:
branches: branches:
- master - main
- releases/* - releases/*
jobs: jobs:
+14
View File
@@ -0,0 +1,14 @@
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.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+35
View File
@@ -1,5 +1,40 @@
# Changelog # 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) ## v2 (beta)
- Improved fetch performance - Improved fetch performance
+1
View File
@@ -0,0 +1 @@
* @actions/actions-runtime
+28 -26
View File
@@ -6,7 +6,7 @@
This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it. This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth` 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. 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.
The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out. The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out.
@@ -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 # The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event. # triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, defaults to `master`. # Otherwise, uses the default branch.
ref: '' ref: ''
# Personal access token (PAT) used to fetch the repository. The PAT is configured # Personal access token (PAT) used to fetch the repository. The PAT is configured
@@ -89,7 +89,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# Default: true # Default: true
clean: '' clean: ''
# Number of commits to fetch. 0 indicates all history. # Number of commits to fetch. 0 indicates all history for all branches and tags.
# Default: 1 # Default: 1
fetch-depth: '' fetch-depth: ''
@@ -110,6 +110,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# Scenarios # 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 a different branch](#Checkout-a-different-branch)
- [Checkout HEAD^](#Checkout-HEAD) - [Checkout HEAD^](#Checkout-HEAD)
- [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side) - [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
@@ -117,9 +118,15 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
- [Checkout multiple repos (private)](#Checkout-multiple-repos-private) - [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
- [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit) - [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
- [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event) - [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
- [Fetch all tags](#Fetch-all-tags) - [Push a commit using the built-in token](#Push-a-commit-using-the-built-in-token)
- [Fetch all branches](#Fetch-all-branches)
- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches) ## Fetch all history for all tags and branches
```yaml
- uses: actions/checkout@v2
with:
fetch-depth: 0
```
## Checkout a different branch ## Checkout a different branch
@@ -198,7 +205,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
```yaml ```yaml
on: on:
pull_request: pull_request:
branches: [master] branches: [main]
types: [opened, synchronize, closed] types: [opened, synchronize, closed]
jobs: jobs:
build: build:
@@ -207,27 +214,22 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
``` ```
## Fetch all tags ## Push a commit using the built-in token
```yaml ```yaml
- uses: actions/checkout@v2 on: push
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* jobs:
``` build:
runs-on: ubuntu-latest
## Fetch all branches steps:
- uses: actions/checkout@v2
```yaml - run: |
- uses: actions/checkout@v2 date > generated.txt
- run: | git config user.name github-actions
git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* git config user.email github-actions@github.com
``` git add .
git commit -m "generated"
## Fetch all history for all tags and branches git push
```yaml
- uses: actions/checkout@v2
- run: |
git fetch --prune --unshallow
``` ```
# License # License
+4 -1
View File
@@ -714,6 +714,7 @@ async function setup(testName: string): Promise<void> {
), ),
env: {}, env: {},
fetch: jest.fn(), fetch: jest.fn(),
getDefaultBranch: jest.fn(),
getWorkingDirectory: jest.fn(() => workspace), getWorkingDirectory: jest.fn(() => workspace),
init: jest.fn(), init: jest.fn(),
isDetached: jest.fn(), isDetached: jest.fn(),
@@ -722,9 +723,11 @@ async function setup(testName: string): Promise<void> {
log1: jest.fn(), log1: jest.fn(),
remoteAdd: jest.fn(), remoteAdd: jest.fn(),
removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]), removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]),
revParse: jest.fn(),
setEnvironmentVariable: jest.fn((name: string, value: string) => { setEnvironmentVariable: jest.fn((name: string, value: string) => {
git.env[name] = value git.env[name] = value
}), }),
shaExists: jest.fn(),
submoduleForeach: jest.fn(async () => { submoduleForeach: jest.fn(async () => {
return '' return ''
}), }),
@@ -761,7 +764,7 @@ async function setup(testName: string): Promise<void> {
submodules: false, submodules: false,
nestedSubmodules: false, nestedSubmodules: false,
persistCredentials: true, persistCredentials: true,
ref: 'refs/heads/master', ref: 'refs/heads/main',
repositoryName: 'my-repo', repositoryName: 'my-repo',
repositoryOwner: 'my-org', repositoryOwner: 'my-org',
repositoryPath: '', repositoryPath: '',
+73 -18
View File
@@ -9,6 +9,7 @@ const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
let repositoryPath: string let repositoryPath: string
let repositoryUrl: string let repositoryUrl: string
let clean: boolean let clean: boolean
let ref: string
let git: IGitCommandManager let git: IGitCommandManager
describe('git-directory-helper tests', () => { describe('git-directory-helper tests', () => {
@@ -41,7 +42,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -63,7 +65,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -88,7 +91,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -109,7 +113,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -137,7 +142,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -163,7 +169,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
differentRepositoryUrl, differentRepositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -187,7 +194,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -212,7 +220,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -236,7 +245,8 @@ describe('git-directory-helper tests', () => {
undefined, undefined,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -260,7 +270,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -290,7 +301,8 @@ describe('git-directory-helper tests', () => {
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
@@ -305,29 +317,66 @@ describe('git-directory-helper tests', () => {
expect(git.tryReset).not.toHaveBeenCalled() expect(git.tryReset).not.toHaveBeenCalled()
}) })
const removesRemoteBranches = 'removes local branches' const removesAncestorRemoteBranch = 'removes ancestor remote branch'
it(removesRemoteBranches, async () => { it(removesAncestorRemoteBranch, async () => {
// Arrange // Arrange
await setup(removesRemoteBranches) await setup(removesAncestorRemoteBranch)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const mockBranchList = git.branchList as jest.Mock<any, any> const mockBranchList = git.branchList as jest.Mock<any, any>
mockBranchList.mockImplementation(async (remote: boolean) => { mockBranchList.mockImplementation(async (remote: boolean) => {
return remote ? ['remote-branch-1', 'remote-branch-2'] : [] return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
}) })
ref = 'remote-branch-1/conflict'
// Act // Act
await gitDirectoryHelper.prepareExistingDirectory( await gitDirectoryHelper.prepareExistingDirectory(
git, git,
repositoryPath, repositoryPath,
repositoryUrl, repositoryUrl,
clean clean,
ref
) )
// Assert // Assert
const files = await fs.promises.readdir(repositoryPath) const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file']) expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-1') expect(git.branchDelete).toHaveBeenCalledTimes(1)
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-2') 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'
)
}) })
}) })
@@ -344,6 +393,9 @@ async function setup(testName: string): Promise<void> {
// Clean // Clean
clean = true clean = true
// Ref
ref = ''
// Git command manager // Git command manager
git = { git = {
branchDelete: jest.fn(), branchDelete: jest.fn(),
@@ -356,6 +408,7 @@ async function setup(testName: string): Promise<void> {
config: jest.fn(), config: jest.fn(),
configExists: jest.fn(), configExists: jest.fn(),
fetch: jest.fn(), fetch: jest.fn(),
getDefaultBranch: jest.fn(),
getWorkingDirectory: jest.fn(() => repositoryPath), getWorkingDirectory: jest.fn(() => repositoryPath),
init: jest.fn(), init: jest.fn(),
isDetached: jest.fn(), isDetached: jest.fn(),
@@ -364,7 +417,9 @@ async function setup(testName: string): Promise<void> {
log1: jest.fn(), log1: jest.fn(),
remoteAdd: jest.fn(), remoteAdd: jest.fn(),
removeEnvironmentVariable: jest.fn(), removeEnvironmentVariable: jest.fn(),
revParse: jest.fn(),
setEnvironmentVariable: jest.fn(), setEnvironmentVariable: jest.fn(),
shaExists: jest.fn(),
submoduleForeach: jest.fn(), submoduleForeach: jest.fn(),
submoduleSync: jest.fn(), submoduleSync: jest.fn(),
submoduleUpdate: jest.fn(), submoduleUpdate: jest.fn(),
-7
View File
@@ -110,13 +110,6 @@ 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', () => { it('sets ref to empty when explicit sha', () => {
inputs.ref = '1111111111222222222233333333334444444444' inputs.ref = '1111111111222222222233333333334444444444'
const settings: IGitSourceSettings = inputHelper.getInputs() const settings: IGitSourceSettings = inputHelper.getInputs()
+1 -1
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 ::add-path::%CD% echo "%CD%" >> $GITHUB_PATH
cd .. cd ..
+1 -1
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 "::add-path::$(pwd)" echo "$(pwd)" >> $GITHUB_PATH
cd .. cd ..
+1 -1
View File
@@ -20,5 +20,5 @@ else
# Verify auth token # Verify auth token
cd basic cd basic
git fetch --no-tags --depth=1 origin +refs/heads/master:refs/remotes/origin/master git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main
fi fi
+1 -1
View File
@@ -12,6 +12,6 @@ if [[ "$(git status --porcelain)" != "" ]]; then
echo ---------------------------------------- echo ----------------------------------------
echo Troubleshooting echo Troubleshooting
echo ---------------------------------------- echo ----------------------------------------
echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run all" echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run format && npm run build"
exit 1 exit 1
fi fi
+2 -2
View File
@@ -8,7 +8,7 @@ inputs:
description: > description: >
The branch, tag or SHA to checkout. When checking out the repository that The branch, tag or SHA to checkout. When checking out the repository that
triggered a workflow, this defaults to the reference or SHA for that triggered a workflow, this defaults to the reference or SHA for that
event. Otherwise, defaults to `master`. event. Otherwise, uses the default branch.
token: token:
description: > description: >
Personal access token (PAT) used to fetch the repository. The PAT is configured Personal access token (PAT) used to fetch the repository. The PAT is configured
@@ -54,7 +54,7 @@ inputs:
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching' description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
default: true default: true
fetch-depth: fetch-depth:
description: 'Number of commits to fetch. 0 indicates all history.' description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
default: 1 default: 1
lfs: lfs:
description: 'Whether to download Git-LFS files' description: 'Whether to download Git-LFS files'
+3 -3
View File
@@ -24,7 +24,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: > description: >
The branch, tag or SHA to checkout. When checking out the repository that The branch, tag or SHA to checkout. When checking out the repository that
triggered a workflow, this defaults to the reference or SHA for that triggered a workflow, this defaults to the reference or SHA for that
event. Otherwise, defaults to `master`. event. Otherwise, uses the default branch.
token: token:
description: > description: >
Personal access token (PAT) used to fetch the repository. The PAT is configured Personal access token (PAT) used to fetch the repository. The PAT is configured
@@ -70,7 +70,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching' description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
default: true default: true
fetch-depth: fetch-depth:
description: 'Number of commits to fetch. 0 indicates all history.' description: 'Number of commits to fetch. 0 indicates all history for all tags and branches.'
default: 1 default: 1
lfs: lfs:
description: 'Whether to download Git-LFS files' description: 'Whether to download Git-LFS files'
@@ -277,7 +277,7 @@ Note:
### Branching strategy and release tags ### Branching strategy and release tags
- Create a servicing branch for V1: `releases/v1` - Create a servicing branch for V1: `releases/v1`
- Merge the changes into `master` - Merge the changes into the default branch
- Release using a new tag `preview` - Release using a new tag `preview`
- When stable, release using a new tag `v2` - When stable, release using a new tag `v2`
+14104 -518
View File
File diff suppressed because one or more lines are too long
+92 -35
View File
@@ -15,19 +15,19 @@
"integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ==" "integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ=="
}, },
"@actions/github": { "@actions/github": {
"version": "2.1.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@actions/github/-/github-2.2.0.tgz",
"integrity": "sha512-G4ncMlh4pLLAvNgHUYUtpWQ1zPf/VYqmRH9oshxLabdaOOnp7i1hgSgzr2xne2YUaSND3uqemd3YYTIsm2f/KQ==", "integrity": "sha512-9UAZqn8ywdR70n3GwVle4N8ALosQs4z50N7XMXrSTUVOmVpaBC5kE3TRTT7qQdi3OaQV24mjGuJZsHUmhD+ZXw==",
"requires": { "requires": {
"@actions/http-client": "^1.0.3", "@actions/http-client": "^1.0.3",
"@octokit/graphql": "^4.3.1", "@octokit/graphql": "^4.3.1",
"@octokit/rest": "^16.15.0" "@octokit/rest": "^16.43.1"
} }
}, },
"@actions/http-client": { "@actions/http-client": {
"version": "1.0.3", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
"integrity": "sha512-wFwh1U4adB/Zsk4cc9kVqaBOHoknhp/pJQk+aWTocbAZWpIl4Zx/At83WFRLXvxB+5HVTWOACM6qjULMZfQSfw==", "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
"requires": { "requires": {
"tunnel": "0.0.6" "tunnel": "0.0.6"
}, },
@@ -622,13 +622,23 @@
} }
}, },
"@octokit/endpoint": { "@octokit/endpoint": {
"version": "5.5.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.1.tgz",
"integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==", "integrity": "sha512-pOPHaSz57SFT/m3R5P8MUu4wLPszokn5pXcB/pzavLTQf2jbU+6iayTvzaY6/BiotuRS0qyEUkx3QglT4U958A==",
"requires": { "requires": {
"@octokit/types": "^2.0.0", "@octokit/types": "^2.11.1",
"is-plain-object": "^3.0.0", "is-plain-object": "^3.0.0",
"universal-user-agent": "^4.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"
}
}
} }
}, },
"@octokit/graphql": { "@octokit/graphql": {
@@ -641,25 +651,57 @@
"universal-user-agent": "^4.0.0" "universal-user-agent": "^4.0.0"
} }
}, },
"@octokit/request": { "@octokit/plugin-paginate-rest": {
"version": "5.3.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz",
"integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==", "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==",
"requires": { "requires": {
"@octokit/endpoint": "^5.5.0", "@octokit/types": "^2.0.1"
"@octokit/request-error": "^1.0.1", }
"@octokit/types": "^2.0.0", },
"@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==",
"requires": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.0.0",
"@octokit/types": "^2.11.1",
"deprecation": "^2.0.0", "deprecation": "^2.0.0",
"is-plain-object": "^3.0.0", "is-plain-object": "^3.0.0",
"node-fetch": "^2.3.0", "node-fetch": "^2.3.0",
"once": "^1.4.0", "once": "^1.4.0",
"universal-user-agent": "^4.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"
}
}
} }
}, },
"@octokit/request-error": { "@octokit/request-error": {
"version": "1.2.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz",
"integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==", "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==",
"requires": { "requires": {
"@octokit/types": "^2.0.0", "@octokit/types": "^2.0.0",
"deprecation": "^2.0.0", "deprecation": "^2.0.0",
@@ -667,11 +709,14 @@
} }
}, },
"@octokit/rest": { "@octokit/rest": {
"version": "16.38.1", "version": "16.43.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.38.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz",
"integrity": "sha512-zyNFx+/Bd1EXt7LQjfrc6H4wryBQ/oDuZeZhGMBSFr1eMPFDmpEweFQR3R25zjKwBQpDY7L5GQO6A3XSaOfV1w==", "integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==",
"requires": { "requires": {
"@octokit/auth-token": "^2.4.0", "@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": "^5.2.0",
"@octokit/request-error": "^1.0.2", "@octokit/request-error": "^1.0.2",
"atob-lite": "^2.0.0", "atob-lite": "^2.0.0",
@@ -684,12 +729,24 @@
"octokit-pagination-methods": "^1.1.0", "octokit-pagination-methods": "^1.1.0",
"once": "^1.4.0", "once": "^1.4.0",
"universal-user-agent": "^4.0.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": { "@octokit/types": {
"version": "2.1.1", "version": "2.14.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.14.0.tgz",
"integrity": "sha512-89LOYH+d/vsbDX785NOfLxTW88GjNd0lWRz1DVPVsZgg9Yett5O+3MOvwo7iHgvUwbFz0mf/yPIjBkUbs4kxoQ==", "integrity": "sha512-1w2wxpN45rEXPDFeB7rGain7wcJ/aTRg8bdILITVnS0O7a4zEGELa3JmIe+jeLdekQjvZRbVfNPqS+mi5fKCKQ==",
"requires": { "requires": {
"@types/node": ">= 8" "@types/node": ">= 8"
} }
@@ -6720,9 +6777,9 @@
} }
}, },
"universal-user-agent": { "universal-user-agent": {
"version": "4.0.0", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz",
"integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==",
"requires": { "requires": {
"os-name": "^3.1.0" "os-name": "^3.1.0"
} }
@@ -6901,9 +6958,9 @@
"dev": true "dev": true
}, },
"windows-release": { "windows-release": {
"version": "3.2.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz",
"integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", "integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==",
"requires": { "requires": {
"execa": "^1.0.0" "execa": "^1.0.0"
} }
+3 -3
View File
@@ -5,8 +5,8 @@
"main": "lib/main.js", "main": "lib/main.js",
"scripts": { "scripts": {
"build": "tsc && ncc build && node lib/misc/generate-docs.js", "build": "tsc && ncc build && node lib/misc/generate-docs.js",
"format": "prettier --write **/*.ts", "format": "prettier --write '**/*.ts'",
"format-check": "prettier --check **/*.ts", "format-check": "prettier --check '**/*.ts'",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"test": "jest" "test": "jest"
}, },
@@ -28,7 +28,7 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.1.3", "@actions/core": "^1.1.3",
"@actions/exec": "^1.0.1", "@actions/exec": "^1.0.1",
"@actions/github": "^2.0.2", "@actions/github": "^2.2.0",
"@actions/io": "^1.0.1", "@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"
+1 -1
View File
@@ -148,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}'`)
this.replaceTokenPlaceholder(configPath) await this.replaceTokenPlaceholder(configPath)
} }
if (this.settings.sshKey) { if (this.settings.sshKey) {
+67 -16
View File
@@ -3,6 +3,7 @@ import * as exec from '@actions/exec'
import * as fshelper from './fs-helper' import * as fshelper from './fs-helper'
import * as io from '@actions/io' import * as io from '@actions/io'
import * as path from 'path' import * as path from 'path'
import * as refHelper from './ref-helper'
import * as regexpHelper from './regexp-helper' import * as regexpHelper from './regexp-helper'
import * as retryHelper from './retry-helper' import * as retryHelper from './retry-helper'
import {GitVersion} from './git-version' import {GitVersion} from './git-version'
@@ -23,16 +24,19 @@ export interface IGitCommandManager {
globalConfig?: boolean globalConfig?: boolean
): Promise<void> ): Promise<void>
configExists(configKey: string, globalConfig?: boolean): Promise<boolean> configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
fetch(fetchDepth: number, refSpec: string[]): Promise<void> fetch(refSpec: string[], fetchDepth?: number): Promise<void>
getDefaultBranch(repositoryUrl: string): Promise<string>
getWorkingDirectory(): string getWorkingDirectory(): string
init(): Promise<void> init(): Promise<void>
isDetached(): Promise<boolean> isDetached(): Promise<boolean>
lfsFetch(ref: string): Promise<void> lfsFetch(ref: string): Promise<void>
lfsInstall(): Promise<void> lfsInstall(): Promise<void>
log1(): Promise<void> log1(format?: string): 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>
setEnvironmentVariable(name: string, value: string): void setEnvironmentVariable(name: string, value: string): void
shaExists(sha: string): Promise<boolean>
submoduleForeach(command: string, recursive: boolean): Promise<string> submoduleForeach(command: string, recursive: boolean): Promise<string>
submoduleSync(recursive: boolean): Promise<void> submoduleSync(recursive: boolean): Promise<void>
submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void> submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
@@ -164,17 +168,14 @@ class GitCommandManager {
return output.exitCode === 0 return output.exitCode === 0
} }
async fetch(fetchDepth: number, refSpec: string[]): Promise<void> { async fetch(refSpec: string[], fetchDepth?: number): Promise<void> {
const args = [ const args = ['-c', 'protocol.version=2', 'fetch']
'-c', if (!refSpec.some(x => x === refHelper.tagsRefSpec)) {
'protocol.version=2', args.push('--no-tags')
'fetch', }
'--no-tags',
'--prune', args.push('--prune', '--progress', '--no-recurse-submodules')
'--progress', if (fetchDepth && fetchDepth > 0) {
'--no-recurse-submodules'
]
if (fetchDepth > 0) {
args.push(`--depth=${fetchDepth}`) args.push(`--depth=${fetchDepth}`)
} else if ( } else if (
fshelper.fileExistsSync( fshelper.fileExistsSync(
@@ -195,6 +196,34 @@ class GitCommandManager {
}) })
} }
async getDefaultBranch(repositoryUrl: string): Promise<string> {
let output: GitOutput | undefined
await retryHelper.execute(async () => {
output = await this.execGit([
'ls-remote',
'--quiet',
'--exit-code',
'--symref',
repositoryUrl,
'HEAD'
])
})
if (output) {
// Satisfy compiler, will always be set
for (let line of output.stdout.trim().split('\n')) {
line = line.trim()
if (line.startsWith('ref:') || line.endsWith('HEAD')) {
return line
.substr('ref:'.length, line.length - 'ref:'.length - 'HEAD'.length)
.trim()
}
}
}
throw new Error('Unexpected output when retrieving default branch')
}
getWorkingDirectory(): string { getWorkingDirectory(): string {
return this.workingDirectory return this.workingDirectory
} }
@@ -225,8 +254,11 @@ class GitCommandManager {
await this.execGit(['lfs', 'install', '--local']) await this.execGit(['lfs', 'install', '--local'])
} }
async log1(): Promise<void> { async log1(format?: string): Promise<string> {
await this.execGit(['log', '-1']) var args = format ? ['log', '-1', format] : ['log', '-1']
var silent = format ? false : true
const output = await this.execGit(args, false, silent)
return output.stdout
} }
async remoteAdd(remoteName: string, remoteUrl: string): Promise<void> { async remoteAdd(remoteName: string, remoteUrl: string): Promise<void> {
@@ -237,10 +269,27 @@ class GitCommandManager {
delete this.gitEnv[name] delete this.gitEnv[name]
} }
/**
* Resolves a ref to a SHA. For a branch or lightweight tag, the commit SHA is returned.
* For an annotated tag, the tag SHA is returned.
* @param {string} ref For example: 'refs/heads/main' or '/refs/tags/v1'
* @returns {Promise<string>}
*/
async revParse(ref: string): Promise<string> {
const output = await this.execGit(['rev-parse', ref])
return output.stdout.trim()
}
setEnvironmentVariable(name: string, value: string): void { setEnvironmentVariable(name: string, value: string): void {
this.gitEnv[name] = value this.gitEnv[name] = value
} }
async shaExists(sha: string): Promise<boolean> {
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> { async submoduleForeach(command: string, recursive: boolean): Promise<string> {
const args = ['submodule', 'foreach'] const args = ['submodule', 'foreach']
if (recursive) { if (recursive) {
@@ -343,7 +392,8 @@ class GitCommandManager {
private async execGit( private async execGit(
args: string[], args: string[],
allowAllExitCodes = false allowAllExitCodes = false,
silent = false
): Promise<GitOutput> { ): Promise<GitOutput> {
fshelper.directoryExistsSync(this.workingDirectory, true) fshelper.directoryExistsSync(this.workingDirectory, true)
@@ -362,6 +412,7 @@ class GitCommandManager {
const options = { const options = {
cwd: this.workingDirectory, cwd: this.workingDirectory,
env, env,
silent,
ignoreReturnCode: allowAllExitCodes, ignoreReturnCode: allowAllExitCodes,
listeners: { listeners: {
stdout: (data: Buffer) => { stdout: (data: Buffer) => {
+22 -6
View File
@@ -5,13 +5,13 @@ import * as fsHelper from './fs-helper'
import * as io from '@actions/io' import * as io from '@actions/io'
import * as path from 'path' import * as path from 'path'
import {IGitCommandManager} from './git-command-manager' import {IGitCommandManager} from './git-command-manager'
import {IGitSourceSettings} from './git-source-settings'
export async function prepareExistingDirectory( export async function prepareExistingDirectory(
git: IGitCommandManager | undefined, git: IGitCommandManager | undefined,
repositoryPath: string, repositoryPath: string,
repositoryUrl: string, repositoryUrl: string,
clean: boolean clean: boolean,
ref: string
): Promise<void> { ): Promise<void> {
assert.ok(repositoryPath, 'Expected repositoryPath to be defined') assert.ok(repositoryPath, 'Expected repositoryPath to be defined')
assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined') assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined')
@@ -56,10 +56,26 @@ export async function prepareExistingDirectory(
await git.branchDelete(false, branch) await git.branchDelete(false, branch)
} }
// Remove all refs/remotes/origin/* to avoid conflicts // Remove any conflicting refs/remotes/origin/*
branches = await git.branchList(true) // Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar
for (const branch of branches) { // Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo
await git.branchDelete(true, branch) 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)
}
}
}
} }
core.endGroup() core.endGroup()
+50 -5
View File
@@ -42,7 +42,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
git, git,
settings.repositoryPath, settings.repositoryPath,
repositoryUrl, repositoryUrl,
settings.clean settings.clean,
settings.ref
) )
} }
@@ -102,6 +103,21 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
await authHelper.configureAuth() await authHelper.configureAuth()
core.endGroup() core.endGroup()
// Determine the default branch
if (!settings.ref && !settings.commit) {
core.startGroup('Determining the default branch')
if (settings.sshKey) {
settings.ref = await git.getDefaultBranch(repositoryUrl)
} else {
settings.ref = await githubApiHelper.getDefaultBranch(
settings.authToken,
settings.repositoryOwner,
settings.repositoryName
)
}
core.endGroup()
}
// LFS install // LFS install
if (settings.lfs) { if (settings.lfs) {
await git.lfsInstall() await git.lfsInstall()
@@ -109,8 +125,24 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Fetch // Fetch
core.startGroup('Fetching the repository') core.startGroup('Fetching the repository')
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit) if (settings.fetchDepth <= 0) {
await git.fetch(settings.fetchDepth, refSpec) // 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() core.endGroup()
// Checkout info // Checkout info
@@ -169,8 +201,21 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
} }
} }
// Dump some info about the checked out commit // Get commit information
await git.log1() const commitInfo = await git.log1()
// Log commit sha
await git.log1("--format='%H'")
// Check for incorrect pull request merge commit
await refHelper.checkCommitInfo(
settings.authToken,
commitInfo,
settings.repositoryOwner,
settings.repositoryName,
settings.ref,
settings.commit
)
} finally { } finally {
// Remove auth // Remove auth
if (!settings.persistCredentials) { if (!settings.persistCredentials) {
+51 -6
View File
@@ -6,9 +6,8 @@ import * as io from '@actions/io'
import * as path from 'path' 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 * as urlHelper from './url-helper'
import {default as uuid} from 'uuid/v4' import {default as uuid} from 'uuid/v4'
import {ReposGetArchiveLinkParams} from '@octokit/rest' import {Octokit} from '@octokit/rest'
const IS_WINDOWS = process.platform === 'win32' const IS_WINDOWS = process.platform === 'win32'
@@ -20,6 +19,12 @@ export async function downloadRepository(
commit: string, commit: string,
repositoryPath: string repositoryPath: string
): Promise<void> { ): Promise<void> {
// Determine the default branch
if (!ref && !commit) {
core.info('Determining the default branch')
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')
@@ -42,7 +47,7 @@ export async function downloadRepository(
} else { } else {
await toolCache.extractTar(archivePath, extractPath) await toolCache.extractTar(archivePath, extractPath)
} }
io.rmRF(archivePath) await 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.
@@ -65,7 +70,47 @@ export async function downloadRepository(
await io.mv(sourcePath, targetPath) await io.mv(sourcePath, targetPath)
} }
} }
io.rmRF(extractPath) await 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( async function downloadArchive(
@@ -75,8 +120,8 @@ async function downloadArchive(
ref: string, ref: string,
commit: string commit: string
): Promise<Buffer> { ): Promise<Buffer> {
const octokit = new github.GitHub(authToken, {baseUrl: urlHelper.getApiUrl()}) const octokit = new github.GitHub(authToken)
const params: ReposGetArchiveLinkParams = { const params: Octokit.ReposGetArchiveLinkParams = {
owner: owner, owner: owner,
repo: repo, repo: repo,
archive_format: IS_WINDOWS ? 'zipball' : 'tarball', archive_format: IS_WINDOWS ? 'zipball' : 'tarball',
+2 -6
View File
@@ -63,15 +63,11 @@ export function getInputs(): IGitSourceSettings {
result.commit = github.context.sha result.commit = github.context.sha
// Some events have an unqualifed ref. For example when a PR is merged (pull_request closed event), // Some events have an unqualifed ref. For example when a PR is merged (pull_request closed event),
// the ref is unqualifed like "master" instead of "refs/heads/master". // the ref is unqualifed like "main" instead of "refs/heads/main".
if (result.commit && result.ref && !result.ref.startsWith('refs/')) { if (result.commit && result.ref && !result.ref.startsWith('refs/')) {
result.ref = `refs/heads/${result.ref}` result.ref = `refs/heads/${result.ref}`
} }
} }
if (!result.ref && !result.commit) {
result.ref = 'refs/heads/master'
}
} }
// SHA? // SHA?
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) { else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) {
@@ -110,7 +106,7 @@ export function getInputs(): IGitSourceSettings {
core.debug(`recursive submodules = ${result.nestedSubmodules}`) core.debug(`recursive submodules = ${result.nestedSubmodules}`)
// Auth token // Auth token
result.authToken = core.getInput('token') result.authToken = core.getInput('token', {required: true})
// SSH // SSH
result.sshKey = core.getInput('ssh-key') result.sshKey = core.getInput('ssh-key')
+2
View File
@@ -16,6 +16,8 @@ async function run(): Promise<void> {
{}, {},
path.join(__dirname, 'problem-matcher.json') path.join(__dirname, 'problem-matcher.json')
) )
console.log(JSON.stringify(process.env, null, ' '))
// Get sources // Get sources
await gitSourceProvider.getSource(sourceSettings) await gitSourceProvider.getSource(sourceSettings)
+174
View File
@@ -1,4 +1,9 @@
import {URL} from 'url'
import {IGitCommandManager} from './git-command-manager' 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 { export interface ICheckoutInfo {
ref: string ref: string
@@ -57,6 +62,16 @@ export async function getCheckoutInfo(
return result return result
} }
export function getRefSpecForAllHistory(ref: string, commit: string): string[] {
const result = ['+refs/heads/*:refs/remotes/origin/*', tagsRefSpec]
if (ref && ref.toUpperCase().startsWith('REFS/PULL/')) {
const branch = ref.substring('refs/pull/'.length)
result.push(`+${commit || ref}:refs/remotes/pull/${branch}`)
}
return result
}
export function getRefSpec(ref: string, commit: string): string[] { export function getRefSpec(ref: string, commit: string): string[] {
if (!ref && !commit) { if (!ref && !commit) {
throw new Error('Args ref and commit cannot both be empty') throw new Error('Args ref and commit cannot both be empty')
@@ -107,3 +122,162 @@ export function getRefSpec(ref: string, commit: string): string[] {
return [`+${ref}:${ref}`] return [`+${ref}:${ref}`]
} }
} }
/**
* Tests whether the initial fetch created the ref at the expected commit
*/
export async function testRef(
git: IGitCommandManager,
ref: string,
commit: string
): Promise<boolean> {
if (!git) {
throw new Error('Arg git cannot be empty')
}
if (!ref && !commit) {
throw new Error('Args ref and commit cannot both be empty')
}
// No SHA? Nothing to test
if (!commit) {
return true
}
// SHA only?
else if (!ref) {
return await git.shaExists(commit)
}
const upperRef = ref.toUpperCase()
// refs/heads/
if (upperRef.startsWith('REFS/HEADS/')) {
const branch = ref.substring('refs/heads/'.length)
return (
(await git.branchExists(true, `origin/${branch}`)) &&
commit === (await git.revParse(`refs/remotes/origin/${branch}`))
)
}
// refs/pull/
else if (upperRef.startsWith('REFS/PULL/')) {
// Assume matches because fetched using the commit
return true
}
// refs/tags/
else if (upperRef.startsWith('REFS/TAGS/')) {
const tagName = ref.substring('refs/tags/'.length)
return (
(await git.tagExists(tagName)) && commit === (await git.revParse(ref))
)
}
// Unexpected
else {
core.debug(`Unexpected ref format '${ref}' when testing ref info`)
return true
}
}
export async function checkCommitInfo(
token: string,
commitInfo: string,
repositoryOwner: string,
repositoryName: string,
ref: string,
commit: string
): 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'
}
+6 -5
View File
@@ -2,10 +2,6 @@ import * as assert from 'assert'
import {IGitSourceSettings} from './git-source-settings' import {IGitSourceSettings} from './git-source-settings'
import {URL} from 'url' import {URL} from 'url'
export function getApiUrl(): string {
return process.env['GITHUB_API_URL'] || 'https://api.github.com'
}
export function getFetchUrl(settings: IGitSourceSettings): string { export function getFetchUrl(settings: IGitSourceSettings): string {
assert.ok( assert.ok(
settings.repositoryOwner, settings.repositoryOwner,
@@ -24,5 +20,10 @@ export function getFetchUrl(settings: IGitSourceSettings): string {
} }
export function getServerUrl(): URL { export function getServerUrl(): URL {
return new URL(process.env['GITHUB_URL'] || 'https://github.com') // 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'
)
} }