Skip to content

Commit 75fd9e5

Browse files
Merge pull request #1 from MercuryTechnologies/philnielsen/add-reference-patch
feat(checkout): Add `--reference to Local Repo
2 parents 08ead55 + 35bd57b commit 75fd9e5

10 files changed

Lines changed: 184 additions & 110 deletions

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
126126
# running from unless specified. Example URLs are https://github.com or
127127
# https://my-ghes-server.example.com
128128
github-server-url: ''
129+
130+
# Path to a local bare git reference repository to minimize network usage. If the
131+
# provided path does not exist, it will be ignored with a logged message. See
132+
# https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---referenceif-abortnew-repository for more details.
133+
reference: ''
129134
```
130135
<!-- end usage -->
131136

__test__/git-auth-helper.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,7 @@ async function setup(testName: string): Promise<void> {
732732
sparseCheckoutNonConeMode: jest.fn(),
733733
checkout: jest.fn(),
734734
checkoutDetach: jest.fn(),
735+
referenceAdd: jest.fn(),
735736
config: jest.fn(
736737
async (key: string, value: string, globalConfig?: boolean) => {
737738
const configPath = globalConfig
@@ -824,7 +825,8 @@ async function setup(testName: string): Promise<void> {
824825
sshUser: '',
825826
workflowOrganizationId: 123456,
826827
setSafeDirectory: true,
827-
githubServerUrl: githubServerUrl
828+
githubServerUrl: githubServerUrl,
829+
reference: undefined
828830
}
829831
}
830832

__test__/git-directory-helper.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ async function setup(testName: string): Promise<void> {
467467
sparseCheckoutNonConeMode: jest.fn(),
468468
checkout: jest.fn(),
469469
checkoutDetach: jest.fn(),
470+
referenceAdd: jest.fn(),
470471
config: jest.fn(),
471472
configExists: jest.fn(),
472473
fetch: jest.fn(),

__test__/input-helper.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ describe('input-helper tests', () => {
9191
expect(settings.repositoryOwner).toBe('some-owner')
9292
expect(settings.repositoryPath).toBe(gitHubWorkspace)
9393
expect(settings.setSafeDirectory).toBe(true)
94+
expect(settings.reference).toBe(undefined)
9495
})
9596

9697
it('qualifies ref', async () => {

action.yml

Lines changed: 115 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,115 @@
1-
name: 'Checkout'
2-
description: 'Checkout a Git repository at a particular version'
3-
inputs:
4-
repository:
5-
description: 'Repository name with owner. For example, actions/checkout'
6-
default: ${{ github.repository }}
7-
ref:
8-
description: >
9-
The branch, tag or SHA to checkout. When checking out the repository that
10-
triggered a workflow, this defaults to the reference or SHA for that
11-
event. Otherwise, uses the default branch.
12-
token:
13-
description: >
14-
Personal access token (PAT) used to fetch the repository. The PAT is configured
15-
with the local git config, which enables your scripts to run authenticated git
16-
commands. The post-job step removes the PAT.
17-
18-
19-
We recommend using a service account with the least permissions necessary.
20-
Also when generating a new PAT, select the least scopes necessary.
21-
22-
23-
[Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
24-
default: ${{ github.token }}
25-
ssh-key:
26-
description: >
27-
SSH key used to fetch the repository. The SSH key is configured with the local
28-
git config, which enables your scripts to run authenticated git commands.
29-
The post-job step removes the SSH key.
30-
31-
32-
We recommend using a service account with the least permissions necessary.
33-
34-
35-
[Learn more about creating and using
36-
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
37-
ssh-known-hosts:
38-
description: >
39-
Known hosts in addition to the user and global host key database. The public
40-
SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example,
41-
`ssh-keyscan github.com`. The public key for github.com is always implicitly added.
42-
ssh-strict:
43-
description: >
44-
Whether to perform strict host key checking. When true, adds the options `StrictHostKeyChecking=yes`
45-
and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to
46-
configure additional hosts.
47-
default: true
48-
ssh-user:
49-
description: >
50-
The user to use when connecting to the remote SSH host. By default 'git' is used.
51-
default: git
52-
persist-credentials:
53-
description: 'Whether to configure the token or SSH key with the local git config'
54-
default: true
55-
path:
56-
description: 'Relative path under $GITHUB_WORKSPACE to place the repository'
57-
clean:
58-
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
59-
default: true
60-
filter:
61-
description: >
62-
Partially clone against a given filter.
63-
Overrides sparse-checkout if set.
64-
default: null
65-
sparse-checkout:
66-
description: >
67-
Do a sparse checkout on given patterns.
68-
Each pattern should be separated with new lines.
69-
default: null
70-
sparse-checkout-cone-mode:
71-
description: >
72-
Specifies whether to use cone-mode when doing a sparse checkout.
73-
default: true
74-
fetch-depth:
75-
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
76-
default: 1
77-
fetch-tags:
78-
description: 'Whether to fetch tags, even if fetch-depth > 0.'
79-
default: false
80-
show-progress:
81-
description: 'Whether to show progress status output when fetching.'
82-
default: true
83-
lfs:
84-
description: 'Whether to download Git-LFS files'
85-
default: false
86-
submodules:
87-
description: >
88-
Whether to checkout submodules: `true` to checkout submodules or `recursive` to
89-
recursively checkout submodules.
90-
91-
92-
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
93-
converted to HTTPS.
94-
default: false
95-
set-safe-directory:
96-
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
97-
default: true
98-
github-server-url:
99-
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
100-
required: false
101-
outputs:
102-
ref:
103-
description: 'The branch, tag or SHA that was checked out'
104-
commit:
105-
description: 'The commit SHA that was checked out'
106-
runs:
107-
using: node20
108-
main: dist/index.js
109-
post: dist/index.js
1+
name: 'Checkout'
2+
description: 'Checkout a Git repository at a particular version'
3+
inputs:
4+
repository:
5+
description: 'Repository name with owner. For example, actions/checkout'
6+
default: ${{ github.repository }}
7+
ref:
8+
description: >
9+
The branch, tag or SHA to checkout. When checking out the repository that
10+
triggered a workflow, this defaults to the reference or SHA for that
11+
event. Otherwise, uses the default branch.
12+
token:
13+
description: >
14+
Personal access token (PAT) used to fetch the repository. The PAT is configured
15+
with the local git config, which enables your scripts to run authenticated git
16+
commands. The post-job step removes the PAT.
17+
18+
19+
We recommend using a service account with the least permissions necessary.
20+
Also when generating a new PAT, select the least scopes necessary.
21+
22+
23+
[Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
24+
default: ${{ github.token }}
25+
ssh-key:
26+
description: >
27+
SSH key used to fetch the repository. The SSH key is configured with the local
28+
git config, which enables your scripts to run authenticated git commands.
29+
The post-job step removes the SSH key.
30+
31+
32+
We recommend using a service account with the least permissions necessary.
33+
34+
35+
[Learn more about creating and using
36+
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
37+
ssh-known-hosts:
38+
description: >
39+
Known hosts in addition to the user and global host key database. The public
40+
SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example,
41+
`ssh-keyscan github.com`. The public key for github.com is always implicitly added.
42+
ssh-strict:
43+
description: >
44+
Whether to perform strict host key checking. When true, adds the options `StrictHostKeyChecking=yes`
45+
and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to
46+
configure additional hosts.
47+
default: true
48+
ssh-user:
49+
description: >
50+
The user to use when connecting to the remote SSH host. By default 'git' is used.
51+
default: git
52+
persist-credentials:
53+
description: 'Whether to configure the token or SSH key with the local git config'
54+
default: true
55+
path:
56+
description: 'Relative path under $GITHUB_WORKSPACE to place the repository'
57+
clean:
58+
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
59+
default: true
60+
filter:
61+
description: >
62+
Partially clone against a given filter.
63+
Overrides sparse-checkout if set.
64+
default: null
65+
sparse-checkout:
66+
description: >
67+
Do a sparse checkout on given patterns.
68+
Each pattern should be separated with new lines.
69+
default: null
70+
sparse-checkout-cone-mode:
71+
description: >
72+
Specifies whether to use cone-mode when doing a sparse checkout.
73+
default: true
74+
fetch-depth:
75+
description: 'Number of commits to fetch. 0 indicates all history for all branches and tags.'
76+
default: 1
77+
fetch-tags:
78+
description: 'Whether to fetch tags, even if fetch-depth > 0.'
79+
default: false
80+
show-progress:
81+
description: 'Whether to show progress status output when fetching.'
82+
default: true
83+
lfs:
84+
description: 'Whether to download Git-LFS files'
85+
default: false
86+
submodules:
87+
description: >
88+
Whether to checkout submodules: `true` to checkout submodules or `recursive` to
89+
recursively checkout submodules.
90+
91+
92+
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
93+
converted to HTTPS.
94+
default: false
95+
set-safe-directory:
96+
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
97+
default: true
98+
github-server-url:
99+
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
100+
required: false
101+
reference:
102+
description: >
103+
Path to a local bare git reference repository to minimize network usage.
104+
If the provided path does not exist, it will be ignored with a logged message.
105+
See https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---referenceif-abortnew-repository for more details.
106+
required: false
107+
outputs:
108+
ref:
109+
description: 'The branch, tag or SHA that was checked out'
110+
commit:
111+
description: 'The commit SHA that was checked out'
112+
runs:
113+
using: node20
114+
main: dist/index.js
115+
post: dist/index.js

dist/index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,12 @@ class GitCommandManager {
627627
yield this.execGit(args);
628628
});
629629
}
630+
referenceAdd(reference) {
631+
return __awaiter(this, void 0, void 0, function* () {
632+
const alternatesPath = path.join(this.workingDirectory, '.git', 'objects', 'info', 'alternates');
633+
yield fs.promises.appendFile(alternatesPath, `${path.join(reference, 'objects')}\n`);
634+
});
635+
}
630636
config(configKey, configValue, globalConfig, add) {
631637
return __awaiter(this, void 0, void 0, function* () {
632638
const args = ['config', globalConfig ? '--global' : '--local'];
@@ -1241,6 +1247,18 @@ function getSource(settings) {
12411247
yield git.remoteAdd('origin', repositoryUrl);
12421248
core.endGroup();
12431249
}
1250+
// Reference repository
1251+
if (settings.reference) {
1252+
if (fsHelper.directoryExistsSync(settings.reference)) {
1253+
core.startGroup('Setting up reference repository');
1254+
core.info(`Using reference repository at ${settings.reference}`);
1255+
yield git.referenceAdd(settings.reference);
1256+
core.endGroup();
1257+
}
1258+
else {
1259+
core.warning(`Reference repository '${settings.reference}' does not exist, skipping`);
1260+
}
1261+
}
12441262
// Disable automatic garbage collection
12451263
core.startGroup('Disabling automatic garbage collection');
12461264
if (!(yield git.tryDisableAutomaticGarbageCollection())) {
@@ -1832,6 +1850,9 @@ function getInputs() {
18321850
// Determine the GitHub URL that the repository is being hosted from
18331851
result.githubServerUrl = core.getInput('github-server-url');
18341852
core.debug(`GitHub Host URL = ${result.githubServerUrl}`);
1853+
// Reference repository
1854+
result.reference = core.getInput('reference');
1855+
core.debug(`reference = ${result.reference}`);
18351856
return result;
18361857
});
18371858
}

src/git-command-manager.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface IGitCommandManager {
2424
sparseCheckoutNonConeMode(sparseCheckout: string[]): Promise<void>
2525
checkout(ref: string, startPoint: string): Promise<void>
2626
checkoutDetach(): Promise<void>
27+
referenceAdd(reference: string): Promise<void>
2728
config(
2829
configKey: string,
2930
configValue: string,
@@ -219,6 +220,20 @@ class GitCommandManager {
219220
await this.execGit(args)
220221
}
221222

223+
async referenceAdd(reference: string): Promise<void> {
224+
const alternatesPath = path.join(
225+
this.workingDirectory,
226+
'.git',
227+
'objects',
228+
'info',
229+
'alternates'
230+
)
231+
await fs.promises.appendFile(
232+
alternatesPath,
233+
`${path.join(reference, 'objects')}\n`
234+
)
235+
}
236+
222237
async config(
223238
configKey: string,
224239
configValue: string,

src/git-source-provider.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
115115
core.endGroup()
116116
}
117117

118+
// Reference repository
119+
if (settings.reference) {
120+
if (fsHelper.directoryExistsSync(settings.reference)) {
121+
core.startGroup('Setting up reference repository')
122+
core.info(`Using reference repository at ${settings.reference}`)
123+
await git.referenceAdd(settings.reference)
124+
core.endGroup()
125+
} else {
126+
core.warning(
127+
`Reference repository '${settings.reference}' does not exist, skipping`
128+
)
129+
}
130+
}
131+
118132
// Disable automatic garbage collection
119133
core.startGroup('Disabling automatic garbage collection')
120134
if (!(await git.tryDisableAutomaticGarbageCollection())) {

src/git-source-settings.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,9 @@ export interface IGitSourceSettings {
118118
* User override on the GitHub Server/Host URL that hosts the repository to be cloned
119119
*/
120120
githubServerUrl: string | undefined
121+
122+
/**
123+
* Path to a local bare git reference repository to minimize network usage
124+
*/
125+
reference: string | undefined
121126
}

src/input-helper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,5 +161,9 @@ export async function getInputs(): Promise<IGitSourceSettings> {
161161
result.githubServerUrl = core.getInput('github-server-url')
162162
core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
163163

164+
// Reference repository
165+
result.reference = core.getInput('reference')
166+
core.debug(`reference = ${result.reference}`)
167+
164168
return result
165169
}

0 commit comments

Comments
 (0)