Skip to content

Commit

Permalink
Merge pull request #10 from namespacelabs/giulio/lfs-caching-support
Browse files Browse the repository at this point in the history
Add LFS download and caching support
  • Loading branch information
gmichelo authored Mar 5, 2025
2 parents b8c45d6 + 076c88a commit 63f88aa
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 16 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ steps:
# Whether to configure the token or SSH key with the local git config
# Default: true
persist-credentials: ''

# Whether to download and cache Git-LFS files
# Default: false
lfs: ''
```
## Development
Expand Down
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ inputs:
`true` to dissociate the main checkout, `recursive` to dissociate the main checkout and
all submodules.
default: "false"

lfs:
description: 'Whether to download and cache Git-LFS files'
default: "false"

runs:
using: node20
Expand Down
49 changes: 41 additions & 8 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 66 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,23 @@ See also https://namespace.so/docs/features/faster-github-actions#caching-git-re
)
if (!fs.existsSync(mirrorDir)) {
fs.mkdirSync(mirrorDir, { recursive: true })
await gitClone(config.owner, config.repo, mirrorDir, ['--mirror'])
await gitClone(
config.owner,
config.repo,
mirrorDir,
['--mirror'],
!config.downloadGitLFS
)
}

// Fetch commits for mirror
await gitFetch(mirrorDir)

// If Git LFS is required, download objects in cache
if (config.downloadGitLFS) {
await gitLFSFetch(mirrorDir, '', '')
}

// Prepare repo dir
let repoDir = workspacePath
if (config.targetPath) {
Expand All @@ -71,11 +82,13 @@ See also https://namespace.so/docs/features/faster-github-actions#caching-git-re
await exec.exec(`git config --global --add safe.directory ${repoDir}`)
const fetchDepthFlag = getFetchDepthFlag(config)
const dissociateFlag = config.dissociateMainRepo ? '--dissociate' : ''
await gitClone(config.owner, config.repo, repoDir, [
`--reference=${mirrorDir}`,
`${fetchDepthFlag}`,
`${dissociateFlag}`
])
await gitClone(
config.owner,
config.repo,
repoDir,
[`--reference=${mirrorDir}`, `${fetchDepthFlag}`, `${dissociateFlag}`],
!config.downloadGitLFS
)

// When ref is unspecified and for repositories different from the one where the workflow is running
// resolve their default branch and use it as `ref`
Expand Down Expand Up @@ -116,6 +129,15 @@ See also https://namespace.so/docs/features/faster-github-actions#caching-git-re
await gitSubmoduleUpdate(config, gitMirrorPath, repoDir)
}

// If Git LFS is required, download objects. This should use the mirror cached LFS objects.
if (config.downloadGitLFS) {
await gitLFSFetch(
`${repoDir}/.git`,
repoDir,
checkoutInfo.startPoint || checkoutInfo.ref
)
}

if (config.persistCredentials) {
// Persist authentication in local
await configGitRepoLocalAuth(config.token, repoDir)
Expand Down Expand Up @@ -145,6 +167,7 @@ interface IInputConfig {
dissociateMainRepo: boolean
dissociateSubmodules: boolean
persistCredentials: boolean
downloadGitLFS: boolean
}

function parseInputConfig(): IInputConfig {
Expand Down Expand Up @@ -225,6 +248,15 @@ function parseInputConfig(): IInputConfig {
}
core.debug(`persistCredentials = ${result.persistCredentials}`)

// Download and cache Git LFS objects
const downloadGitLFS = (core.getInput('lfs') || '').toUpperCase()
if (downloadGitLFS === 'TRUE') {
result.downloadGitLFS = true
} else {
result.downloadGitLFS = false
}
core.debug(`persistCredentials = ${result.downloadGitLFS}`)

return result
}

Expand Down Expand Up @@ -406,11 +438,25 @@ async function gitClone(
owner: string,
repo: string,
repoDir: string,
flags: string[]
flags: string[],
skipLFS: boolean
) {
// Copy over only the defined values from process.env
const cleanEnv: Record<string, string> = {}
Object.entries(process.env).forEach(([key, value]) => {
if (value !== undefined) {
cleanEnv[key] = value
}
})

// Git clone copies LFS objects from mirror if they exist by default. GIT_LFS_SKIP_SMUDGE=1 prevents that.
const envVars = skipLFS ? { ...cleanEnv, GIT_LFS_SKIP_SMUDGE: '1' } : cleanEnv

const flagString = flags.join(' ')
await exec.exec(
`git clone ${flagString} -- https://[email protected]/${owner}/${repo}.git ${repoDir}`
`git clone ${flagString} -- https://[email protected]/${owner}/${repo}.git ${repoDir}`,
[],
{ env: envVars }
)
}

Expand All @@ -420,6 +466,18 @@ async function gitFetch(gitDir: string) {
)
}

async function gitLFSFetch(gitDir: string, repoDir: string, ref: string) {
var flags: string[] = []
if (gitDir) {
flags.push(`--git-dir ${gitDir}`)
}
if (repoDir) {
flags.push(`--work-tree ${repoDir}`)
}
const flagString = flags.join(' ')
await exec.exec(`git ${flagString} lfs fetch origin ${ref}`)
}

async function gitSubmoduleUpdate(
config: IInputConfig,
mirrorDir: string,
Expand Down

0 comments on commit 63f88aa

Please sign in to comment.