Compare commits

..

7 Commits

Author SHA1 Message Date
0ad4b8fada Prep Release v4.1.4 (#1704)
* Update package.json and package-lock.json to reflect v4.1.4
* Update CHANGELOG
2024-04-24 15:29:05 +02:00
43045ae669 Disable extensions.worktreeConfig when disabling sparse-checkout (#1692) 2024-04-23 22:07:43 +02:00
37b082107b Bump the minor-actions-dependencies group with 2 updates (#1693)
Bumps the minor-actions-dependencies group with 2 updates: [docker/login-action](https://github.com/docker/login-action) and [docker/build-push-action](https://github.com/docker/build-push-action).


Updates `docker/login-action` from 3.0.0 to 3.1.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3.0.0...v3.1.0)

Updates `docker/build-push-action` from 5.1.0 to 5.3.0
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5.1.0...v5.3.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-actions-dependencies
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 13:14:23 -04:00
9839dc14a0 Add dependabot config (#1688)
* Add dependabot config
* Group minor and patch updates for NPM and GH Actions
2024-04-23 13:02:51 -04:00
9b4c13b0bf Bump word-wrap from 1.2.3 to 1.2.5 (#1643)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.5.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.5)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 12:52:06 +02:00
1d96c772d1 Add SSH user parameter (#1685)
* Add a configurable SSH user

* Update docs with param

* Indentation of readme

* formatting woes

* Update src/url-helper.ts

Co-authored-by: Josh Gross <joshmgross@github.com>

* Update action.yml

Co-authored-by: Josh Gross <joshmgross@github.com>

* Update genfiles

---------

Co-authored-by: Josh Gross <joshmgross@github.com>
2024-04-18 15:29:55 -04:00
cd7d8d697e Check git version before attempting to disable sparse-checkout (#1656)
* Check git version before attempting to disable `sparse-checkout`
* Bump `MinimumGitSparseCheckoutVersion` to 2.28 due to #1386
* Initial prep for release 4.1.3
2024-03-14 15:40:14 +01:00
19 changed files with 207 additions and 44 deletions

20
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,20 @@
---
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
minor-npm-dependencies:
# NPM: Only group minor and patch updates (we want to carefully review major updates)
update-types: [minor, patch]
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
minor-actions-dependencies:
# GitHub Actions: Only group minor and patch updates (we want to carefully review major updates)
update-types: [minor, patch]

View File

@ -37,7 +37,7 @@ jobs:
steps: steps:
# Clone this repo # Clone this repo
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4.1.1
# Basic checkout # Basic checkout
- name: Checkout basic - name: Checkout basic

View File

@ -31,7 +31,7 @@ jobs:
# Use `docker/login-action` to log in to GHCR.io. # Use `docker/login-action` to log in to GHCR.io.
# Once published, the packages are scoped to the account defined here. # Once published, the packages are scoped to the account defined here.
- name: Log in to the ghcr.io container registry - name: Log in to the ghcr.io container registry
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3.1.0
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@ -48,7 +48,7 @@ jobs:
# Use `docker/build-push-action` to build (and optionally publish) the image. # Use `docker/build-push-action` to build (and optionally publish) the image.
- name: Build Docker Image (with optional Push) - name: Build Docker Image (with optional Push)
uses: docker/build-push-action@v5.1.0 uses: docker/build-push-action@v5.3.0
with: with:
context: . context: .
file: images/test-ubuntu-git.Dockerfile file: images/test-ubuntu-git.Dockerfile

View File

@ -1,5 +1,16 @@
# Changelog # Changelog
## v4.1.4
- Disable `extensions.worktreeConfig` when disabling `sparse-checkout` by @jww3 in https://github.com/actions/checkout/pull/1692
- Add dependabot config by @cory-miller in https://github.com/actions/checkout/pull/1688
- Bump the minor-actions-dependencies group with 2 updates by @dependabot in https://github.com/actions/checkout/pull/1693
- Bump word-wrap from 1.2.3 to 1.2.5 by @dependabot in https://github.com/actions/checkout/pull/1643
## v4.1.3
- Check git version before attempting to disable `sparse-checkout` by @jww3 in https://github.com/actions/checkout/pull/1656
- Add SSH user parameter by @cory-miller in https://github.com/actions/checkout/pull/1685
- Update `actions/checkout` version in `update-main-version.yml` by @jww3 in https://github.com/actions/checkout/pull/1650
## v4.1.2 ## v4.1.2
- Fix: Disable sparse checkout whenever `sparse-checkout` option is not present @dscho in https://github.com/actions/checkout/pull/1598 - Fix: Disable sparse checkout whenever `sparse-checkout` option is not present @dscho in https://github.com/actions/checkout/pull/1598

View File

@ -8,7 +8,7 @@ Only a single commit is fetched by default, for the ref/SHA that triggered the w
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.
When Git 2.28 or higher is not in your PATH, falls back to the REST API to download the files. When Git 2.18 or higher is not in your PATH, falls back to the REST API to download the files.
# What's new # What's new
@ -62,6 +62,11 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
# Default: true # Default: true
ssh-strict: '' ssh-strict: ''
# The user to use when connecting to the remote SSH host. By default 'git' is
# used.
# Default: git
ssh-user: ''
# Whether to configure the token or SSH key with the local git config # Whether to configure the token or SSH key with the local git config
# Default: true # Default: true
persist-credentials: '' persist-credentials: ''

View File

@ -796,7 +796,8 @@ async function setup(testName: string): Promise<void> {
), ),
tryDisableAutomaticGarbageCollection: jest.fn(), tryDisableAutomaticGarbageCollection: jest.fn(),
tryGetFetchUrl: jest.fn(), tryGetFetchUrl: jest.fn(),
tryReset: jest.fn() tryReset: jest.fn(),
version: jest.fn()
} }
settings = { settings = {
@ -820,6 +821,7 @@ async function setup(testName: string): Promise<void> {
sshKey: sshPath ? 'some ssh private key' : '', sshKey: sshPath ? 'some ssh private key' : '',
sshKnownHosts: '', sshKnownHosts: '',
sshStrict: true, sshStrict: true,
sshUser: '',
workflowOrganizationId: 123456, workflowOrganizationId: 123456,
setSafeDirectory: true, setSafeDirectory: true,
githubServerUrl: githubServerUrl githubServerUrl: githubServerUrl

View File

@ -24,7 +24,7 @@ describe('git-auth-helper tests', () => {
console.log(args, options.listeners.stdout) console.log(args, options.listeners.stdout)
if (args.includes('version')) { if (args.includes('version')) {
options.listeners.stdout(Buffer.from('2.28')) options.listeners.stdout(Buffer.from('2.18'))
return 0 return 0
} }
@ -57,7 +57,7 @@ describe('git-auth-helper tests', () => {
console.log(args, options.listeners.stdout) console.log(args, options.listeners.stdout)
if (args.includes('version')) { if (args.includes('version')) {
options.listeners.stdout(Buffer.from('2.28')) options.listeners.stdout(Buffer.from('2.18'))
return 0 return 0
} }
@ -97,7 +97,7 @@ describe('Test fetchDepth and fetchTags options', () => {
console.log(args, options.listeners.stdout) console.log(args, options.listeners.stdout)
if (args.includes('version')) { if (args.includes('version')) {
options.listeners.stdout(Buffer.from('2.28')) options.listeners.stdout(Buffer.from('2.18'))
} }
return 0 return 0

View File

@ -501,6 +501,7 @@ async function setup(testName: string): Promise<void> {
}), }),
tryReset: jest.fn(async () => { tryReset: jest.fn(async () => {
return true return true
}) }),
version: jest.fn()
} }
} }

View File

@ -1,4 +1,5 @@
import {GitVersion} from '../lib/git-version' import {GitVersion} from '../src/git-version'
import {MinimumGitSparseCheckoutVersion} from '../src/git-command-manager'
describe('git-version tests', () => { describe('git-version tests', () => {
it('basics', async () => { it('basics', async () => {
@ -42,4 +43,44 @@ describe('git-version tests', () => {
expect(version.checkMinimum(new GitVersion('5.1'))).toBeFalsy() expect(version.checkMinimum(new GitVersion('5.1'))).toBeFalsy()
expect(version.checkMinimum(new GitVersion('5.1.2'))).toBeFalsy() expect(version.checkMinimum(new GitVersion('5.1.2'))).toBeFalsy()
}) })
it('sparse checkout', async () => {
const minSparseVer = MinimumGitSparseCheckoutVersion
expect(new GitVersion('1.0').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('1.99').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.0').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.24').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.24.0').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.24.9').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.25').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.25.0').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.25.1').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.25.9').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.26').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.26.0').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.26.1').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.26.9').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.27').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.27.0').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.27.1').checkMinimum(minSparseVer)).toBeFalsy()
expect(new GitVersion('2.27.9').checkMinimum(minSparseVer)).toBeFalsy()
// /---------------------------------------
// ^^^ before / after vvv
// --------------------------/
expect(new GitVersion('2.28').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.28.0').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.28.1').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.28.9').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.29').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.29.0').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.29.1').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.29.9').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('2.99').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('3.0').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('3.99').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('4.0').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('4.99').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('5.0').checkMinimum(minSparseVer)).toBeTruthy()
expect(new GitVersion('5.99').checkMinimum(minSparseVer)).toBeTruthy()
})
}) })

View File

@ -18,6 +18,20 @@ else
exit 1 exit 1
fi fi
# Verify that sparse-checkout is disabled.
SPARSE_CHECKOUT_ENABLED=$(git -C ./basic config --local --get-all core.sparseCheckout)
if [ "$SPARSE_CHECKOUT_ENABLED" != "" ]; then
echo "Expected sparse-checkout to be disabled (discovered: $SPARSE_CHECKOUT_ENABLED)"
exit 1
fi
# Verify git configuration shows worktreeConfig is effectively disabled
WORKTREE_CONFIG_ENABLED=$(git -C ./basic config --local --get-all extensions.worktreeConfig)
if [[ "$WORKTREE_CONFIG_ENABLED" != "" ]]; then
echo "Expected extensions.worktreeConfig (boolean) to be disabled in git config. This could be an artifact of sparse checkout functionality."
exit 1
fi
# Verify auth token # Verify auth token
cd basic cd basic
git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main git fetch --no-tags --depth=1 origin +refs/heads/main:refs/remotes/origin/main

View File

@ -45,6 +45,10 @@ inputs:
and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to
configure additional hosts. configure additional hosts.
default: true default: true
ssh-user:
description: >
The user to use when connecting to the remote SSH host. By default 'git' is used.
default: git
persist-credentials: persist-credentials:
description: 'Whether to configure the token or SSH key with the local git config' description: 'Whether to configure the token or SSH key with the local git config'
default: true default: true

49
dist/index.js vendored
View File

@ -467,7 +467,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.createCommandManager = exports.MinimumGitVersion = void 0; exports.createCommandManager = exports.MinimumGitSparseCheckoutVersion = exports.MinimumGitVersion = void 0;
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const exec = __importStar(__nccwpck_require__(1514)); const exec = __importStar(__nccwpck_require__(1514));
const fs = __importStar(__nccwpck_require__(7147)); const fs = __importStar(__nccwpck_require__(7147));
@ -481,7 +481,8 @@ const git_version_1 = __nccwpck_require__(3142);
// Auth header not supported before 2.9 // Auth header not supported before 2.9
// Wire protocol v2 not supported before 2.18 // Wire protocol v2 not supported before 2.18
// sparse-checkout not [well-]supported before 2.28 (see https://github.com/actions/checkout/issues/1386) // sparse-checkout not [well-]supported before 2.28 (see https://github.com/actions/checkout/issues/1386)
exports.MinimumGitVersion = new git_version_1.GitVersion('2.28'); exports.MinimumGitVersion = new git_version_1.GitVersion('2.18');
exports.MinimumGitSparseCheckoutVersion = new git_version_1.GitVersion('2.28');
function createCommandManager(workingDirectory, lfs, doSparseCheckout) { function createCommandManager(workingDirectory, lfs, doSparseCheckout) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield GitCommandManager.createCommandManager(workingDirectory, lfs, doSparseCheckout); return yield GitCommandManager.createCommandManager(workingDirectory, lfs, doSparseCheckout);
@ -499,6 +500,7 @@ class GitCommandManager {
this.lfs = false; this.lfs = false;
this.doSparseCheckout = false; this.doSparseCheckout = false;
this.workingDirectory = ''; this.workingDirectory = '';
this.gitVersion = new git_version_1.GitVersion();
} }
branchDelete(remote, branch) { branchDelete(remote, branch) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@ -524,7 +526,13 @@ class GitCommandManager {
branchList(remote) { branchList(remote) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const result = []; const result = [];
const args = ['rev-parse', '--symbolic']; // Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
// "branch --list" is more difficult when in a detached HEAD state.
// TODO(https://github.com/actions/checkout/issues/786): this implementation uses
// "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names. When
// 2.18 is no longer supported, we can switch back to --symbolic.
const args = ['rev-parse', '--symbolic-full-name'];
if (remote) { if (remote) {
args.push('--remotes=origin'); args.push('--remotes=origin');
} }
@ -574,6 +582,8 @@ class GitCommandManager {
disableSparseCheckout() { disableSparseCheckout() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
yield this.execGit(['sparse-checkout', 'disable']); yield this.execGit(['sparse-checkout', 'disable']);
// Disabling 'sparse-checkout` leaves behind an undesirable side-effect in config (even in a pristine environment).
yield this.tryConfigUnset('extensions.worktreeConfig', false);
}); });
} }
sparseCheckout(sparseCheckout) { sparseCheckout(sparseCheckout) {
@ -845,6 +855,11 @@ class GitCommandManager {
return output.exitCode === 0; return output.exitCode === 0;
}); });
} }
version() {
return __awaiter(this, void 0, void 0, function* () {
return this.gitVersion;
});
}
static createCommandManager(workingDirectory, lfs, doSparseCheckout) { static createCommandManager(workingDirectory, lfs, doSparseCheckout) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const result = new GitCommandManager(); const result = new GitCommandManager();
@ -896,21 +911,21 @@ class GitCommandManager {
this.gitPath = yield io.which('git', true); this.gitPath = yield io.which('git', true);
// Git version // Git version
core.debug('Getting git version'); core.debug('Getting git version');
let gitVersion = new git_version_1.GitVersion(); this.gitVersion = new git_version_1.GitVersion();
let gitOutput = yield this.execGit(['version']); let gitOutput = yield this.execGit(['version']);
let stdout = gitOutput.stdout.trim(); let stdout = gitOutput.stdout.trim();
if (!stdout.includes('\n')) { if (!stdout.includes('\n')) {
const match = stdout.match(/\d+\.\d+(\.\d+)?/); const match = stdout.match(/\d+\.\d+(\.\d+)?/);
if (match) { if (match) {
gitVersion = new git_version_1.GitVersion(match[0]); this.gitVersion = new git_version_1.GitVersion(match[0]);
} }
} }
if (!gitVersion.isValid()) { if (!this.gitVersion.isValid()) {
throw new Error('Unable to determine git version'); throw new Error('Unable to determine git version');
} }
// Minimum git version // Minimum git version
if (!gitVersion.checkMinimum(exports.MinimumGitVersion)) { if (!this.gitVersion.checkMinimum(exports.MinimumGitVersion)) {
throw new Error(`Minimum required git version is ${exports.MinimumGitVersion}. Your git ('${this.gitPath}') is ${gitVersion}`); throw new Error(`Minimum required git version is ${exports.MinimumGitVersion}. Your git ('${this.gitPath}') is ${this.gitVersion}`);
} }
if (this.lfs) { if (this.lfs) {
// Git-lfs version // Git-lfs version
@ -937,8 +952,13 @@ class GitCommandManager {
} }
} }
this.doSparseCheckout = doSparseCheckout; this.doSparseCheckout = doSparseCheckout;
if (this.doSparseCheckout) {
if (!this.gitVersion.checkMinimum(exports.MinimumGitSparseCheckoutVersion)) {
throw new Error(`Minimum Git version required for sparse checkout is ${exports.MinimumGitSparseCheckoutVersion}. Your git ('${this.gitPath}') is ${this.gitVersion}`);
}
}
// Set the user agent // Set the user agent
const gitHttpUserAgent = `git/${gitVersion} (github-actions-checkout)`; const gitHttpUserAgent = `git/${this.gitVersion} (github-actions-checkout)`;
core.debug(`Set git useragent to: ${gitHttpUserAgent}`); core.debug(`Set git useragent to: ${gitHttpUserAgent}`);
this.gitEnv['GIT_HTTP_USER_AGENT'] = gitHttpUserAgent; this.gitEnv['GIT_HTTP_USER_AGENT'] = gitHttpUserAgent;
}); });
@ -1143,6 +1163,7 @@ const path = __importStar(__nccwpck_require__(1017));
const refHelper = __importStar(__nccwpck_require__(8601)); const refHelper = __importStar(__nccwpck_require__(8601));
const stateHelper = __importStar(__nccwpck_require__(8647)); const stateHelper = __importStar(__nccwpck_require__(8647));
const urlHelper = __importStar(__nccwpck_require__(9437)); const urlHelper = __importStar(__nccwpck_require__(9437));
const git_command_manager_1 = __nccwpck_require__(738);
function getSource(settings) { function getSource(settings) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// Repository URL // Repository URL
@ -1276,7 +1297,11 @@ function getSource(settings) {
} }
// Sparse checkout // Sparse checkout
if (!settings.sparseCheckout) { if (!settings.sparseCheckout) {
yield git.disableSparseCheckout(); let gitVersion = yield git.version();
// no need to disable sparse-checkout if the installed git runtime doesn't even support it.
if (gitVersion.checkMinimum(git_command_manager_1.MinimumGitSparseCheckoutVersion)) {
yield git.disableSparseCheckout();
}
} }
else { else {
core.startGroup('Setting up sparse checkout'); core.startGroup('Setting up sparse checkout');
@ -1775,6 +1800,7 @@ function getInputs() {
result.sshKnownHosts = core.getInput('ssh-known-hosts'); result.sshKnownHosts = core.getInput('ssh-known-hosts');
result.sshStrict = result.sshStrict =
(core.getInput('ssh-strict') || 'true').toUpperCase() === 'TRUE'; (core.getInput('ssh-strict') || 'true').toUpperCase() === 'TRUE';
result.sshUser = core.getInput('ssh-user');
// Persist credentials // Persist credentials
result.persistCredentials = result.persistCredentials =
(core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'; (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE';
@ -2377,7 +2403,8 @@ function getFetchUrl(settings) {
const encodedOwner = encodeURIComponent(settings.repositoryOwner); const encodedOwner = encodeURIComponent(settings.repositoryOwner);
const encodedName = encodeURIComponent(settings.repositoryName); const encodedName = encodeURIComponent(settings.repositoryName);
if (settings.sshKey) { if (settings.sshKey) {
return `git@${serviceUrl.hostname}:${encodedOwner}/${encodedName}.git`; const user = settings.sshUser.length > 0 ? settings.sshUser : 'git';
return `${user}@${serviceUrl.hostname}:${encodedOwner}/${encodedName}.git`;
} }
// "origin" is SCHEME://HOSTNAME[:PORT] // "origin" is SCHEME://HOSTNAME[:PORT]
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`; return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`;

16
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "checkout", "name": "checkout",
"version": "5.0.0", "version": "4.1.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "checkout", "name": "checkout",
"version": "5.0.0", "version": "4.1.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.10.0",
@ -9129,9 +9129,9 @@
} }
}, },
"node_modules/word-wrap": { "node_modules/word-wrap": {
"version": "1.2.3", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -16055,9 +16055,9 @@
} }
}, },
"word-wrap": { "word-wrap": {
"version": "1.2.3", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true "dev": true
}, },
"wrap-ansi": { "wrap-ansi": {

View File

@ -1,6 +1,6 @@
{ {
"name": "checkout", "name": "checkout",
"version": "5.0.0", "version": "4.1.4",
"description": "checkout action", "description": "checkout action",
"main": "lib/main.js", "main": "lib/main.js",
"scripts": { "scripts": {

View File

@ -12,7 +12,8 @@ import {GitVersion} from './git-version'
// Auth header not supported before 2.9 // Auth header not supported before 2.9
// Wire protocol v2 not supported before 2.18 // Wire protocol v2 not supported before 2.18
// sparse-checkout not [well-]supported before 2.28 (see https://github.com/actions/checkout/issues/1386) // sparse-checkout not [well-]supported before 2.28 (see https://github.com/actions/checkout/issues/1386)
export const MinimumGitVersion = new GitVersion('2.28') export const MinimumGitVersion = new GitVersion('2.18')
export const MinimumGitSparseCheckoutVersion = new GitVersion('2.28')
export interface IGitCommandManager { export interface IGitCommandManager {
branchDelete(remote: boolean, branch: string): Promise<void> branchDelete(remote: boolean, branch: string): Promise<void>
@ -61,6 +62,7 @@ export interface IGitCommandManager {
tryDisableAutomaticGarbageCollection(): Promise<boolean> tryDisableAutomaticGarbageCollection(): Promise<boolean>
tryGetFetchUrl(): Promise<string> tryGetFetchUrl(): Promise<string>
tryReset(): Promise<boolean> tryReset(): Promise<boolean>
version(): Promise<GitVersion>
} }
export async function createCommandManager( export async function createCommandManager(
@ -84,6 +86,7 @@ class GitCommandManager {
private lfs = false private lfs = false
private doSparseCheckout = false private doSparseCheckout = false
private workingDirectory = '' private workingDirectory = ''
private gitVersion: GitVersion = new GitVersion()
// Private constructor; use createCommandManager() // Private constructor; use createCommandManager()
private constructor() {} private constructor() {}
@ -111,7 +114,16 @@ class GitCommandManager {
async branchList(remote: boolean): Promise<string[]> { async branchList(remote: boolean): Promise<string[]> {
const result: string[] = [] const result: string[] = []
const args = ['rev-parse', '--symbolic']
// Note, this implementation uses "rev-parse --symbolic-full-name" because the output from
// "branch --list" is more difficult when in a detached HEAD state.
// TODO(https://github.com/actions/checkout/issues/786): this implementation uses
// "rev-parse --symbolic-full-name" because there is a bug
// in Git 2.18 that causes "rev-parse --symbolic" to output symbolic full names. When
// 2.18 is no longer supported, we can switch back to --symbolic.
const args = ['rev-parse', '--symbolic-full-name']
if (remote) { if (remote) {
args.push('--remotes=origin') args.push('--remotes=origin')
} else { } else {
@ -166,6 +178,8 @@ class GitCommandManager {
async disableSparseCheckout(): Promise<void> { async disableSparseCheckout(): Promise<void> {
await this.execGit(['sparse-checkout', 'disable']) await this.execGit(['sparse-checkout', 'disable'])
// Disabling 'sparse-checkout` leaves behind an undesirable side-effect in config (even in a pristine environment).
await this.tryConfigUnset('extensions.worktreeConfig', false)
} }
async sparseCheckout(sparseCheckout: string[]): Promise<void> { async sparseCheckout(sparseCheckout: string[]): Promise<void> {
@ -472,6 +486,10 @@ class GitCommandManager {
return output.exitCode === 0 return output.exitCode === 0
} }
async version(): Promise<GitVersion> {
return this.gitVersion
}
static async createCommandManager( static async createCommandManager(
workingDirectory: string, workingDirectory: string,
lfs: boolean, lfs: boolean,
@ -548,23 +566,23 @@ class GitCommandManager {
// Git version // Git version
core.debug('Getting git version') core.debug('Getting git version')
let gitVersion = new GitVersion() this.gitVersion = new GitVersion()
let gitOutput = await this.execGit(['version']) let gitOutput = await this.execGit(['version'])
let stdout = gitOutput.stdout.trim() let stdout = gitOutput.stdout.trim()
if (!stdout.includes('\n')) { if (!stdout.includes('\n')) {
const match = stdout.match(/\d+\.\d+(\.\d+)?/) const match = stdout.match(/\d+\.\d+(\.\d+)?/)
if (match) { if (match) {
gitVersion = new GitVersion(match[0]) this.gitVersion = new GitVersion(match[0])
} }
} }
if (!gitVersion.isValid()) { if (!this.gitVersion.isValid()) {
throw new Error('Unable to determine git version') throw new Error('Unable to determine git version')
} }
// Minimum git version // Minimum git version
if (!gitVersion.checkMinimum(MinimumGitVersion)) { if (!this.gitVersion.checkMinimum(MinimumGitVersion)) {
throw new Error( throw new Error(
`Minimum required git version is ${MinimumGitVersion}. Your git ('${this.gitPath}') is ${gitVersion}` `Minimum required git version is ${MinimumGitVersion}. Your git ('${this.gitPath}') is ${this.gitVersion}`
) )
} }
@ -597,9 +615,15 @@ class GitCommandManager {
} }
this.doSparseCheckout = doSparseCheckout this.doSparseCheckout = doSparseCheckout
if (this.doSparseCheckout) {
if (!this.gitVersion.checkMinimum(MinimumGitSparseCheckoutVersion)) {
throw new Error(
`Minimum Git version required for sparse checkout is ${MinimumGitSparseCheckoutVersion}. Your git ('${this.gitPath}') is ${this.gitVersion}`
)
}
}
// Set the user agent // Set the user agent
const gitHttpUserAgent = `git/${gitVersion} (github-actions-checkout)` const gitHttpUserAgent = `git/${this.gitVersion} (github-actions-checkout)`
core.debug(`Set git useragent to: ${gitHttpUserAgent}`) core.debug(`Set git useragent to: ${gitHttpUserAgent}`)
this.gitEnv['GIT_HTTP_USER_AGENT'] = gitHttpUserAgent this.gitEnv['GIT_HTTP_USER_AGENT'] = gitHttpUserAgent
} }

View File

@ -9,7 +9,10 @@ import * as path from 'path'
import * as refHelper from './ref-helper' import * as refHelper from './ref-helper'
import * as stateHelper from './state-helper' import * as stateHelper from './state-helper'
import * as urlHelper from './url-helper' import * as urlHelper from './url-helper'
import {IGitCommandManager} from './git-command-manager' import {
MinimumGitSparseCheckoutVersion,
IGitCommandManager
} from './git-command-manager'
import {IGitSourceSettings} from './git-source-settings' import {IGitSourceSettings} from './git-source-settings'
export async function getSource(settings: IGitSourceSettings): Promise<void> { export async function getSource(settings: IGitSourceSettings): Promise<void> {
@ -209,7 +212,11 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Sparse checkout // Sparse checkout
if (!settings.sparseCheckout) { if (!settings.sparseCheckout) {
await git.disableSparseCheckout() let gitVersion = await git.version()
// no need to disable sparse-checkout if the installed git runtime doesn't even support it.
if (gitVersion.checkMinimum(MinimumGitSparseCheckoutVersion)) {
await git.disableSparseCheckout()
}
} else { } else {
core.startGroup('Setting up sparse checkout') core.startGroup('Setting up sparse checkout')
if (settings.sparseCheckoutConeMode) { if (settings.sparseCheckoutConeMode) {

View File

@ -94,6 +94,11 @@ export interface IGitSourceSettings {
*/ */
sshStrict: boolean sshStrict: boolean
/**
* The SSH user to login as
*/
sshUser: string
/** /**
* Indicates whether to persist the credentials on disk to enable scripting authenticated git commands * Indicates whether to persist the credentials on disk to enable scripting authenticated git commands
*/ */

View File

@ -143,6 +143,7 @@ export async function getInputs(): Promise<IGitSourceSettings> {
result.sshKnownHosts = core.getInput('ssh-known-hosts') result.sshKnownHosts = core.getInput('ssh-known-hosts')
result.sshStrict = result.sshStrict =
(core.getInput('ssh-strict') || 'true').toUpperCase() === 'TRUE' (core.getInput('ssh-strict') || 'true').toUpperCase() === 'TRUE'
result.sshUser = core.getInput('ssh-user')
// Persist credentials // Persist credentials
result.persistCredentials = result.persistCredentials =

View File

@ -12,7 +12,8 @@ export function getFetchUrl(settings: IGitSourceSettings): string {
const encodedOwner = encodeURIComponent(settings.repositoryOwner) const encodedOwner = encodeURIComponent(settings.repositoryOwner)
const encodedName = encodeURIComponent(settings.repositoryName) const encodedName = encodeURIComponent(settings.repositoryName)
if (settings.sshKey) { if (settings.sshKey) {
return `git@${serviceUrl.hostname}:${encodedOwner}/${encodedName}.git` const user = settings.sshUser.length > 0 ? settings.sshUser : 'git'
return `${user}@${serviceUrl.hostname}:${encodedOwner}/${encodedName}.git`
} }
// "origin" is SCHEME://HOSTNAME[:PORT] // "origin" is SCHEME://HOSTNAME[:PORT]