1
0
mirror of https://github.com/actions/checkout.git synced 2026-06-14 16:53:47 +08:00

Compare commits

..

2 Commits

Author SHA1 Message Date
eric sciple
8e8c483db8 Clarify v6 README (#2328) 2025-12-01 20:08:49 -06:00
eric sciple
033fa0dc0b Add worktree support for persist-credentials includeIf (#2327) 2025-12-01 19:53:23 -06:00
13 changed files with 100 additions and 91 deletions

View File

@@ -154,17 +154,6 @@ jobs:
submodules: true
- name: Verify submodules true
run: __test__/verify-submodules-true.sh
# Submodules limited
- name: Checkout submodules limited
uses: ./
with:
ref: test-data/v2/submodule-ssh-url
path: submodules-true
submodules: true
submodule-directories: submodule-level-1
- name: Verify submodules true
run: __test__/verify-submodules-true.sh
# Submodules recursive
- name: Checkout submodules recursive
@@ -176,6 +165,22 @@ jobs:
- name: Verify submodules recursive
run: __test__/verify-submodules-recursive.sh
# Worktree credentials
- name: Checkout for worktree test
uses: ./
with:
path: worktree-test
- name: Verify worktree credentials
shell: bash
run: __test__/verify-worktree.sh worktree-test worktree-branch
# Worktree credentials in container step
- name: Verify worktree credentials in container step
if: runner.os == 'Linux'
uses: docker://bitnami/git:latest
with:
args: bash __test__/verify-worktree.sh worktree-test container-worktree-branch
# Basic checkout using REST API
- name: Remove basic
if: runner.os != 'windows'

View File

@@ -1,19 +1,19 @@
# Changelog
## V6.0.0
## v6.0.0
* Persist creds to a separate file by @ericsciple in https://github.com/actions/checkout/pull/2286
* Update README to include Node.js 24 support details and requirements by @salmanmkc in https://github.com/actions/checkout/pull/2248
## V5.0.1
## v5.0.1
* Port v6 cleanup to v5 by @ericsciple in https://github.com/actions/checkout/pull/2301
## V5.0.0
## v5.0.0
* Update actions checkout to use node 24 by @salmanmkc in https://github.com/actions/checkout/pull/2226
## V4.3.1
## v4.3.1
* Port v6 cleanup to v4 by @ericsciple in https://github.com/actions/checkout/pull/2305
## V4.3.0
## v4.3.0
* docs: update README.md by @motss in https://github.com/actions/checkout/pull/1971
* Add internal repos for checking out multiple repositories by @mouismail in https://github.com/actions/checkout/pull/1977
* Documentation update - add recommended permissions to Readme by @benwells in https://github.com/actions/checkout/pull/2043

View File

@@ -4,8 +4,9 @@
## What's new
- Updated `persist-credentials` to store the credentials under `$RUNNER_TEMP` instead of directly in the local git config.
- This requires a minimum Actions Runner version of [v2.329.0](https://github.com/actions/runner/releases/tag/v2.329.0) to access the persisted credentials for [Docker container action](https://docs.github.com/en/actions/tutorials/use-containerized-services/create-a-docker-container-action) scenarios.
- Improved credential security: `persist-credentials` now stores credentials in a separate file under `$RUNNER_TEMP` instead of directly in `.git/config`
- No workflow changes required — `git fetch`, `git push`, etc. continue to work automatically
- Running authenticated git commands from a [Docker container action](https://docs.github.com/actions/sharing-automations/creating-actions/creating-a-docker-container-action) requires Actions Runner [v2.329.0](https://github.com/actions/runner/releases/tag/v2.329.0) or later
# Checkout v5
@@ -149,10 +150,6 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
# Default: false
submodules: ''
# A list of submodules to checkout.
# Default: null
submodule-directories: ''
# Add repository path as safe.directory for Git global config by running `git
# config --global --add safe.directory <path>`
# Default: true

View File

@@ -1162,7 +1162,6 @@ async function setup(testName: string): Promise<void> {
lfs: false,
submodules: false,
nestedSubmodules: false,
submoduleDirectories: [],
persistCredentials: true,
ref: 'refs/heads/main',
repositoryName: 'my-repo',

View File

@@ -21,13 +21,6 @@ describe('input-helper tests', () => {
jest.spyOn(core, 'getInput').mockImplementation((name: string) => {
return inputs[name]
})
// Mock getMultilineInput
jest.spyOn(core, 'getMultilineInput').mockImplementation((name: string) => {
const input: string[] = (inputs[name] || '')
.split('\n')
.filter(x => x !== '')
return input.map(inp => inp.trim())
})
// Mock error/warning/info/debug
jest.spyOn(core, 'error').mockImplementation(jest.fn())
@@ -94,7 +87,6 @@ describe('input-helper tests', () => {
expect(settings.showProgress).toBe(true)
expect(settings.lfs).toBe(false)
expect(settings.ref).toBe('refs/heads/some-ref')
expect(settings.submoduleDirectories).toStrictEqual([])
expect(settings.repositoryName).toBe('some-repo')
expect(settings.repositoryOwner).toBe('some-owner')
expect(settings.repositoryPath).toBe(gitHubWorkspace)
@@ -152,13 +144,4 @@ describe('input-helper tests', () => {
const settings: IGitSourceSettings = await inputHelper.getInputs()
expect(settings.workflowOrganizationId).toBe(123456)
})
it('sets submoduleDirectories', async () => {
inputs['submodule-directories'] = 'submodule1\nsubmodule2'
const settings: IGitSourceSettings = await inputHelper.getInputs()
expect(settings.submoduleDirectories).toStrictEqual([
'submodule1',
'submodule2'
])
expect(settings.submodules).toBe(true)
})
})

51
__test__/verify-worktree.sh Executable file
View File

@@ -0,0 +1,51 @@
#!/bin/bash
set -e
# Verify worktree credentials
# This test verifies that git credentials work in worktrees created after checkout
# Usage: verify-worktree.sh <checkout-path> <worktree-name>
CHECKOUT_PATH="$1"
WORKTREE_NAME="$2"
if [ -z "$CHECKOUT_PATH" ] || [ -z "$WORKTREE_NAME" ]; then
echo "Usage: verify-worktree.sh <checkout-path> <worktree-name>"
exit 1
fi
cd "$CHECKOUT_PATH"
# Add safe directory for container environments
git config --global --add safe.directory "*" 2>/dev/null || true
# Show the includeIf configuration
echo "Git config includeIf entries:"
git config --list --show-origin | grep -i include || true
# Create the worktree
echo "Creating worktree..."
git worktree add "../$WORKTREE_NAME" HEAD --detach
# Change to worktree directory
cd "../$WORKTREE_NAME"
# Verify we're in a worktree
echo "Verifying worktree gitdir:"
cat .git
# Verify credentials are available in worktree by checking extraheader is configured
echo "Checking credentials in worktree..."
if git config --list --show-origin | grep -q "extraheader"; then
echo "Credentials are configured in worktree"
else
echo "ERROR: Credentials are NOT configured in worktree"
echo "Full git config:"
git config --list --show-origin
exit 1
fi
# Verify fetch works in the worktree
echo "Fetching in worktree..."
git fetch origin
echo "Worktree credentials test passed!"

View File

@@ -92,10 +92,6 @@ inputs:
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
converted to HTTPS.
default: false
submodule-directories:
description: >
A list of submodules to checkout.
default: null
set-safe-directory:
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
default: true

20
dist/index.js vendored
View File

@@ -412,6 +412,9 @@ class GitAuthHelper {
// Configure host includeIf
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`;
yield this.git.config(hostIncludeKey, credentialsConfigPath);
// Configure host includeIf for worktrees
const hostWorktreeIncludeKey = `includeIf.gitdir:${gitDir}/worktrees/*.path`;
yield this.git.config(hostWorktreeIncludeKey, credentialsConfigPath);
// Container git directory
const workingDirectory = this.git.getWorkingDirectory();
const githubWorkspace = process.env['GITHUB_WORKSPACE'];
@@ -424,6 +427,9 @@ class GitAuthHelper {
// Configure container includeIf
const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`;
yield this.git.config(containerIncludeKey, containerCredentialsPath);
// Configure container includeIf for worktrees
const containerWorktreeIncludeKey = `includeIf.gitdir:${containerGitDir}/worktrees/*.path`;
yield this.git.config(containerWorktreeIncludeKey, containerCredentialsPath);
}
});
}
@@ -975,10 +981,10 @@ class GitCommandManager {
yield this.execGit(args);
});
}
submoduleUpdate(fetchDepth, recursive, submoduleDirectories) {
submoduleUpdate(fetchDepth, recursive) {
return __awaiter(this, void 0, void 0, function* () {
const args = ['-c', 'protocol.version=2'];
args.push('submodule', 'update', '--init', '--force', ...submoduleDirectories);
args.push('submodule', 'update', '--init', '--force');
if (fetchDepth > 0) {
args.push(`--depth=${fetchDepth}`);
}
@@ -1576,7 +1582,7 @@ function getSource(settings) {
// Checkout submodules
core.startGroup('Fetching submodules');
yield git.submoduleSync(settings.nestedSubmodules);
yield git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules, settings.submoduleDirectories);
yield git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules);
yield git.submoduleForeach('git config --local gc.auto 0', settings.nestedSubmodules);
core.endGroup();
// Persist credentials
@@ -2037,7 +2043,6 @@ function getInputs() {
// Submodules
result.submodules = false;
result.nestedSubmodules = false;
result.submoduleDirectories = [];
const submodulesString = (core.getInput('submodules') || '').toUpperCase();
if (submodulesString == 'RECURSIVE') {
result.submodules = true;
@@ -2046,15 +2051,8 @@ function getInputs() {
else if (submodulesString == 'TRUE') {
result.submodules = true;
}
const submoduleDirectories = core.getMultilineInput('submodule-directories');
if (submoduleDirectories.length > 0) {
result.submoduleDirectories = submoduleDirectories;
if (!result.submodules)
result.submodules = true;
}
core.debug(`submodules = ${result.submodules}`);
core.debug(`recursive submodules = ${result.nestedSubmodules}`);
core.debug(`submodule directories = ${result.submoduleDirectories}`);
// Auth token
result.authToken = core.getInput('token', { required: true });
// SSH

View File

@@ -374,6 +374,10 @@ class GitAuthHelper {
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`
await this.git.config(hostIncludeKey, credentialsConfigPath)
// Configure host includeIf for worktrees
const hostWorktreeIncludeKey = `includeIf.gitdir:${gitDir}/worktrees/*.path`
await this.git.config(hostWorktreeIncludeKey, credentialsConfigPath)
// Container git directory
const workingDirectory = this.git.getWorkingDirectory()
const githubWorkspace = process.env['GITHUB_WORKSPACE']
@@ -395,6 +399,13 @@ class GitAuthHelper {
// Configure container includeIf
const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`
await this.git.config(containerIncludeKey, containerCredentialsPath)
// Configure container includeIf for worktrees
const containerWorktreeIncludeKey = `includeIf.gitdir:${containerGitDir}/worktrees/*.path`
await this.git.config(
containerWorktreeIncludeKey,
containerCredentialsPath
)
}
}

View File

@@ -56,11 +56,7 @@ export interface IGitCommandManager {
shaExists(sha: string): Promise<boolean>
submoduleForeach(command: string, recursive: boolean): Promise<string>
submoduleSync(recursive: boolean): Promise<void>
submoduleUpdate(
fetchDepth: number,
recursive: boolean,
submoduleDirectories: string[]
): Promise<void>
submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
submoduleStatus(): Promise<boolean>
tagExists(pattern: string): Promise<boolean>
tryClean(): Promise<boolean>
@@ -452,19 +448,9 @@ class GitCommandManager {
await this.execGit(args)
}
async submoduleUpdate(
fetchDepth: number,
recursive: boolean,
submoduleDirectories: string[]
): Promise<void> {
async submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void> {
const args = ['-c', 'protocol.version=2']
args.push(
'submodule',
'update',
'--init',
'--force',
...submoduleDirectories
)
args.push('submodule', 'update', '--init', '--force')
if (fetchDepth > 0) {
args.push(`--depth=${fetchDepth}`)
}

View File

@@ -242,11 +242,7 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Checkout submodules
core.startGroup('Fetching submodules')
await git.submoduleSync(settings.nestedSubmodules)
await git.submoduleUpdate(
settings.fetchDepth,
settings.nestedSubmodules,
settings.submoduleDirectories
)
await git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules)
await git.submoduleForeach(
'git config --local gc.auto 0',
settings.nestedSubmodules

View File

@@ -74,11 +74,6 @@ export interface IGitSourceSettings {
*/
nestedSubmodules: boolean
/**
* Indicates which submodule paths to checkout
*/
submoduleDirectories: string[]
/**
* The auth token to use when fetching the repository
*/

View File

@@ -125,7 +125,6 @@ export async function getInputs(): Promise<IGitSourceSettings> {
// Submodules
result.submodules = false
result.nestedSubmodules = false
result.submoduleDirectories = []
const submodulesString = (core.getInput('submodules') || '').toUpperCase()
if (submodulesString == 'RECURSIVE') {
result.submodules = true
@@ -133,16 +132,9 @@ export async function getInputs(): Promise<IGitSourceSettings> {
} else if (submodulesString == 'TRUE') {
result.submodules = true
}
const submoduleDirectories = core.getMultilineInput('submodule-directories')
if (submoduleDirectories.length > 0) {
result.submoduleDirectories = submoduleDirectories
if (!result.submodules) result.submodules = true
}
core.debug(`submodules = ${result.submodules}`)
core.debug(`recursive submodules = ${result.nestedSubmodules}`)
core.debug(`submodule directories = ${result.submoduleDirectories}`)
// Auth token
result.authToken = core.getInput('token', {required: true})