1
0
Fork 0
mirror of https://github.com/actions/checkout.git synced 2025-03-31 21:40:05 +02:00

sparse-checkout: optionally turn off cone mode

While it _is_ true that cone mode is the default nowadays (mainly for
performance reasons: code mode is much faster than non-cone mode), there
_are_ legitimate use cases where non-cone mode is really useful.

Let's add a flag to optionally disable cone mode.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin 2023-06-03 23:39:45 +02:00
parent 9f59c817cf
commit a241939688
12 changed files with 143 additions and 2 deletions

View file

@ -85,6 +85,20 @@ jobs:
- name: Verify sparse checkout - name: Verify sparse checkout
run: __test__/verify-sparse-checkout.sh run: __test__/verify-sparse-checkout.sh
# Sparse checkout (non-cone mode)
- name: Sparse checkout (non-cone mode)
uses: ./
with:
sparse-checkout: |
/__test__/
/.github/
/dist/
sparse-checkout-cone-mode: false
path: sparse-checkout-non-cone-mode
- name: Verify sparse checkout (non-cone mode)
run: __test__/verify-sparse-checkout-non-cone-mode.sh
# LFS # LFS
- name: Checkout LFS - name: Checkout LFS
uses: ./ uses: ./

View file

@ -79,6 +79,10 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
# Default: null # Default: null
sparse-checkout: '' sparse-checkout: ''
# Specifies whether to use cone-mode when doing a sparse checkout.
# Default: true
sparse-checkout-cone-mode: ''
# Number of commits to fetch. 0 indicates all history for all branches and tags. # Number of commits to fetch. 0 indicates all history for all branches and tags.
# Default: 1 # Default: 1
fetch-depth: '' fetch-depth: ''
@ -113,6 +117,7 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
- [Fetch only the root files](#Fetch-only-the-root-files) - [Fetch only the root files](#Fetch-only-the-root-files)
- [Fetch only the root files and `.github` and `src` folder](#Fetch-only-the-root-files-and-github-and-src-folder) - [Fetch only the root files and `.github` and `src` folder](#Fetch-only-the-root-files-and-github-and-src-folder)
- [Fetch only a single file](#Fetch-only-a-single-file)
- [Fetch all history for all tags and 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)
- [Checkout a different branch](#Checkout-a-different-branch) - [Checkout a different branch](#Checkout-a-different-branch)
- [Checkout HEAD^](#Checkout-HEAD) - [Checkout HEAD^](#Checkout-HEAD)
@ -141,6 +146,16 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
src src
``` ```
## Fetch only a single file
```yaml
- uses: actions/checkout@v3
with:
sparse-checkout: |
README.md
sparse-checkout-cone-mode: false
```
## Fetch all history for all tags and branches ## Fetch all history for all tags and branches
```yaml ```yaml

View file

@ -728,6 +728,7 @@ async function setup(testName: string): Promise<void> {
branchExists: jest.fn(), branchExists: jest.fn(),
branchList: jest.fn(), branchList: jest.fn(),
sparseCheckout: jest.fn(), sparseCheckout: jest.fn(),
sparseCheckoutNonConeMode: jest.fn(),
checkout: jest.fn(), checkout: jest.fn(),
checkoutDetach: jest.fn(), checkoutDetach: jest.fn(),
config: jest.fn( config: jest.fn(
@ -802,6 +803,7 @@ async function setup(testName: string): Promise<void> {
clean: true, clean: true,
commit: '', commit: '',
sparseCheckout: [], sparseCheckout: [],
sparseCheckoutConeMode: true,
fetchDepth: 1, fetchDepth: 1,
lfs: false, lfs: false,
submodules: false, submodules: false,

View file

@ -463,6 +463,7 @@ async function setup(testName: string): Promise<void> {
return [] return []
}), }),
sparseCheckout: jest.fn(), sparseCheckout: jest.fn(),
sparseCheckoutNonConeMode: jest.fn(),
checkout: jest.fn(), checkout: jest.fn(),
checkoutDetach: jest.fn(), checkoutDetach: jest.fn(),
config: jest.fn(), config: jest.fn(),

View file

@ -80,6 +80,7 @@ describe('input-helper tests', () => {
expect(settings.commit).toBeTruthy() expect(settings.commit).toBeTruthy()
expect(settings.commit).toBe('1234567890123456789012345678901234567890') expect(settings.commit).toBe('1234567890123456789012345678901234567890')
expect(settings.sparseCheckout).toBe(undefined) expect(settings.sparseCheckout).toBe(undefined)
expect(settings.sparseCheckoutConeMode).toBe(true)
expect(settings.fetchDepth).toBe(1) expect(settings.fetchDepth).toBe(1)
expect(settings.lfs).toBe(false) expect(settings.lfs).toBe(false)
expect(settings.ref).toBe('refs/heads/some-ref') expect(settings.ref).toBe('refs/heads/some-ref')

View file

@ -0,0 +1,51 @@
#!/bin/sh
# Verify .git folder
if [ ! -d "./sparse-checkout-non-cone-mode/.git" ]; then
echo "Expected ./sparse-checkout-non-cone-mode/.git folder to exist"
exit 1
fi
# Verify sparse-checkout (non-cone-mode)
cd sparse-checkout-non-cone-mode
ENABLED=$(git config --local --get-all core.sparseCheckout)
if [ "$?" != "0" ]; then
echo "Failed to verify that sparse-checkout is enabled"
exit 1
fi
# Check that sparse-checkout is enabled
if [ "$ENABLED" != "true" ]; then
echo "Expected sparse-checkout to be enabled (is: $ENABLED)"
exit 1
fi
SPARSE_CHECKOUT_FILE=$(git rev-parse --git-path info/sparse-checkout)
if [ "$?" != "0" ]; then
echo "Failed to validate sparse-checkout"
exit 1
fi
# Check that sparse-checkout list is not empty
if [ ! -f "$SPARSE_CHECKOUT_FILE" ]; then
echo "Expected sparse-checkout file to exist"
exit 1
fi
# Check that all folders from sparse-checkout exists
for pattern in $(cat "$SPARSE_CHECKOUT_FILE")
do
if [ ! -d "${pattern#/}" ]; then
echo "Expected directory '${pattern#/}' to exist"
exit 1
fi
done
# Verify that the root directory is not checked out
if [ -f README.md ]; then
echo "Expected top-level files not to exist"
exit 1
fi

View file

@ -58,6 +58,10 @@ inputs:
Do a sparse checkout on given patterns. Do a sparse checkout on given patterns.
Each pattern should be separated with new lines Each pattern should be separated with new lines
default: null default: null
sparse-checkout-cone-mode:
description: >
Specifies whether to use cone-mode when doing a sparse checkout.
default: true
fetch-depth: fetch-depth:
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.' description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
default: 1 default: 1

23
dist/index.js vendored
View file

@ -470,6 +470,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.createCommandManager = exports.MinimumGitVersion = void 0; exports.createCommandManager = 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 fshelper = __importStar(__nccwpck_require__(7219)); const fshelper = __importStar(__nccwpck_require__(7219));
const io = __importStar(__nccwpck_require__(7436)); const io = __importStar(__nccwpck_require__(7436));
const path = __importStar(__nccwpck_require__(1017)); const path = __importStar(__nccwpck_require__(1017));
@ -579,6 +580,18 @@ class GitCommandManager {
yield this.execGit(['sparse-checkout', 'set', ...sparseCheckout]); yield this.execGit(['sparse-checkout', 'set', ...sparseCheckout]);
}); });
} }
sparseCheckoutNonConeMode(sparseCheckout) {
return __awaiter(this, void 0, void 0, function* () {
yield this.execGit(['config', 'core.sparseCheckout', 'true']);
const output = yield this.execGit([
'rev-parse',
'--git-path',
'info/sparse-checkout'
]);
const sparseCheckoutPath = path.join(this.workingDirectory, output.stdout.trimRight());
yield fs.promises.appendFile(sparseCheckoutPath, `\n${sparseCheckout.join('\n')}\n`);
});
}
checkout(ref, startPoint) { checkout(ref, startPoint) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const args = ['checkout', '--progress', '--force']; const args = ['checkout', '--progress', '--force'];
@ -1253,7 +1266,12 @@ function getSource(settings) {
// Sparse checkout // Sparse checkout
if (settings.sparseCheckout) { if (settings.sparseCheckout) {
core.startGroup('Setting up sparse checkout'); core.startGroup('Setting up sparse checkout');
yield git.sparseCheckout(settings.sparseCheckout); if (settings.sparseCheckoutConeMode) {
yield git.sparseCheckout(settings.sparseCheckout);
}
else {
yield git.sparseCheckoutNonConeMode(settings.sparseCheckout);
}
core.endGroup(); core.endGroup();
} }
// Checkout // Checkout
@ -1697,6 +1715,9 @@ function getInputs() {
result.sparseCheckout = sparseCheckout; result.sparseCheckout = sparseCheckout;
core.debug(`sparse checkout = ${result.sparseCheckout}`); core.debug(`sparse checkout = ${result.sparseCheckout}`);
} }
result.sparseCheckoutConeMode =
(core.getInput('sparse-checkout-cone-mode') || 'true').toUpperCase() ===
'TRUE';
// Fetch depth // Fetch depth
result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1')); result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1'));
if (isNaN(result.fetchDepth) || result.fetchDepth < 0) { if (isNaN(result.fetchDepth) || result.fetchDepth < 0) {

View file

@ -1,5 +1,6 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as exec from '@actions/exec' import * as exec from '@actions/exec'
import * as fs from 'fs'
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'
@ -17,6 +18,7 @@ export interface IGitCommandManager {
branchExists(remote: boolean, pattern: string): Promise<boolean> branchExists(remote: boolean, pattern: string): Promise<boolean>
branchList(remote: boolean): Promise<string[]> branchList(remote: boolean): Promise<string[]>
sparseCheckout(sparseCheckout: string[]): Promise<void> sparseCheckout(sparseCheckout: string[]): Promise<void>
sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void>
checkout(ref: string, startPoint: string): Promise<void> checkout(ref: string, startPoint: string): Promise<void>
checkoutDetach(): Promise<void> checkoutDetach(): Promise<void>
config( config(
@ -165,6 +167,23 @@ class GitCommandManager {
await this.execGit(['sparse-checkout', 'set', ...sparseCheckout]) await this.execGit(['sparse-checkout', 'set', ...sparseCheckout])
} }
async sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void> {
await this.execGit(['config', 'core.sparseCheckout', 'true'])
const output = await this.execGit([
'rev-parse',
'--git-path',
'info/sparse-checkout'
])
const sparseCheckoutPath = path.join(
this.workingDirectory,
output.stdout.trimRight()
)
await fs.promises.appendFile(
sparseCheckoutPath,
`\n${sparseCheckout.join('\n')}\n`
)
}
async checkout(ref: string, startPoint: string): Promise<void> { async checkout(ref: string, startPoint: string): Promise<void> {
const args = ['checkout', '--progress', '--force'] const args = ['checkout', '--progress', '--force']
if (startPoint) { if (startPoint) {

View file

@ -197,7 +197,11 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Sparse checkout // Sparse checkout
if (settings.sparseCheckout) { if (settings.sparseCheckout) {
core.startGroup('Setting up sparse checkout') core.startGroup('Setting up sparse checkout')
await git.sparseCheckout(settings.sparseCheckout) if (settings.sparseCheckoutConeMode) {
await git.sparseCheckout(settings.sparseCheckout)
} else {
await git.sparseCheckoutNonConeMode(settings.sparseCheckout)
}
core.endGroup() core.endGroup()
} }

View file

@ -34,6 +34,11 @@ export interface IGitSourceSettings {
*/ */
sparseCheckout: string[] sparseCheckout: string[]
/**
* Indicates whether to use cone mode in the sparse checkout (if any)
*/
sparseCheckoutConeMode: boolean
/** /**
* The depth when fetching * The depth when fetching
*/ */

View file

@ -89,6 +89,10 @@ export async function getInputs(): Promise<IGitSourceSettings> {
core.debug(`sparse checkout = ${result.sparseCheckout}`) core.debug(`sparse checkout = ${result.sparseCheckout}`)
} }
result.sparseCheckoutConeMode =
(core.getInput('sparse-checkout-cone-mode') || 'true').toUpperCase() ===
'TRUE'
// Fetch depth // Fetch depth
result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1')) result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1'))
if (isNaN(result.fetchDepth) || result.fetchDepth < 0) { if (isNaN(result.fetchDepth) || result.fetchDepth < 0) {