@opendevise/antora-git-lfs-extension
Version:
An Antora extension that uses the native git command to clone content source repositories in the playbook marked as lfs enabled and records the oid of all lfs files.
124 lines (112 loc) • 4.71 kB
JavaScript
const { createHash } = require('node:crypto')
const expandPath = require('@antora/expand-path-helper')
const fsp = require('node:fs/promises')
const os = require('node:os')
const ospath = require('node:path')
const runCommand = require('@antora/run-command-helper')
const invariably = { false: () => false, newObject: () => ({}) }
module.exports.register = function ({ config }) {
this.once('playbookBuilt', ({ playbook }) => {
const defaultBranches = playbook.content.branches
const lfsContentSources = playbook.content.sources.filter((it) => {
if (!it.lfs) return false
it.branches = [branchesToArray(it.branches ?? defaultBranches)[0] || 'HEAD']
return true
})
if (!lfsContentSources.length) return
let workspaceDir = resolveWorkspaceDir(config.workspaceDir, playbook.runtime.cacheDir, playbook.dir)
workspaceDir = '.' + ospath.sep + ospath.relative(playbook.dir, workspaceDir)
for (const contentSource of lfsContentSources) {
contentSource.remoteUrl = contentSource.url
contentSource.url = workspaceDir + ospath.sep + generateWorktreeName(contentSource.url, contentSource.branches[0])
}
this.updateVariables({ playbook })
})
this.once('beforeProcess', async ({ playbook }) => {
const lfsContentSources = playbook.content.sources.filter((it) => it.lfs)
if (!lfsContentSources.length) return
const processed = []
for (const contentSource of lfsContentSources) {
const cloneDir = ospath.join(playbook.dir, contentSource.url)
if (processed.includes(cloneDir)) continue
processed.push(cloneDir)
const cached = await fsp.stat(cloneDir).then((stat) => stat.isDirectory(), invariably.false)
let gitCmd
const gitArgs = []
let gitCwd
if (cached) {
if (playbook.runtime.fetch) gitCmd = 'pull'
gitCwd = cloneDir
} else {
gitCmd = 'clone'
const branch = contentSource.branches[0]
if (!isDefaultBranch(branch)) gitArgs.push('-b', branch)
gitArgs.push('-q', contentSource.remoteUrl, cloneDir)
}
if (gitCmd) {
if (!playbook.runtime.quiet) console.log(`[${gitCmd}] ${contentSource.remoteUrl}`)
await git(gitCmd, gitArgs, gitCwd)
}
}
})
this.once('contentClassified', async ({ contentCatalog }) => {
const gitLfsFilesByWorktree = {}
for (const file of contentCatalog.getFiles()) {
const worktree = file.src.origin?.worktree
if (!worktree) continue
if (!(worktree in gitLfsFilesByWorktree)) {
gitLfsFilesByWorktree[worktree] = await git('lfs', ['ls-files', '-l'], worktree).then(
(data) =>
data
.trimEnd()
.split('\n')
.reduce((accum, line) => {
const [oid, filepath] = line.split(/ [*-] /, 2)
return Object.assign(accum, { [filepath]: oid })
}, {}),
invariably.newObject
)
}
file.src.oid = gitLfsFilesByWorktree[worktree][file.src.path]
}
})
}
function branchesToArray (branches) {
if (!branches) return []
if (Array.isArray(branches)) return branches.map((it) => String(it))
return String(branches).split(/\s*,\s*/)
}
function generateWorktreeName (url, branch) {
const normalizedUrl = url.toLowerCase().replace(/\.git/, '')
const basename = normalizedUrl.split('/').pop()
const branchQualifier = isDefaultBranch(branch) ? '' : '-' + branch
return basename + branchQualifier + '-' + createHash('sha1').update(normalizedUrl).digest('hex')
}
function getCacheDir (userCacheDir) {
if (userCacheDir) return userCacheDir
let basedir
switch (process.platform) {
case 'win32':
if ((basedir = process.env.APPDATA)) return ospath.join(basedir, 'antora', 'Caches')
break
case 'darwin':
if ((basedir = os.homedir())) return path.join(basedir, 'Library/Caches', 'antora')
break
case 'linux':
if ((basedir = process.env.XDG_CACHE_HOME)) return ospath.join(basedir, 'antora')
if ((basedir = os.homedir())) return ospath.join(basedir, '.cache', 'antora')
}
return ospath.join(process.cwd(), '.cache', 'antora')
}
function isDefaultBranch (name) {
return name === 'HEAD' || name === '.' || name === 'main'
}
function git (command, args, cwd) {
const baseCall = process.env.GIT_COMMAND || 'git'
return runCommand(baseCall, [command].concat(args), { cwd }).then((stdout) => stdout.toString().trimEnd())
}
function resolveWorkspaceDir (requestedDir, userCacheDir, startDir) {
const workspaceDir = requestedDir || getCacheDir(userCacheDir) + ospath.sep + 'content-with-lfs'
return expandPath(workspaceDir, { dot: startDir })
}