Tucker McKnight <tucker@pangolin.lan> | Sun Dec 07 2025
Use the git -C flag instead of changing directories For some reason, when trying to generate the site in a git post-receive hook, the `cd` command doesn't seem to be working. Let's try it with git's built-in -C flag instead, which allows you to specify the location of the git repo without actually cd-ing into that directory.
2 3 4 5 6 7
export const NavHelper = (reposConfig: ReposConfiguration, slugify: Function, repoName: string, branchName: string) => {
const reposPath = reposConfig.path || ""
const rootPath = `${reposPath}/${slugify(repoName)}/branches`
return {
rootPath: () => {2 3 4 5 6 7 8 9 10 11 12 13 14 15
export const NavHelper = (reposConfig: ReposConfiguration, slugify: Function, repoName: string, branchName: string) => {
const reposPath = reposConfig.path || ""
// These two aren't actually used by any pages, but they're in almost
// every page URL. E.g. all of them start with 'repos/my-repo-name'
// or 'repos/my-repo-name/branches.'
const repoBasePath = `${reposPath}/${slugify(repoName)}`
const rootBasePathBranches = `${repoBasePath}/branches`
const currentBranchPath = `${rootBasePathBranches}/${slugify(branchName)}`
return {
rootPath: () => {14 15 16 17 18 19 20 21 22 23 24 25 26
}
},
repoHomePath: () => {
return `${rootPath}/${slugify(reposConfig.repos[repoName].defaultBranch)}`
},
repoCurrentBranchHome: () => rootPath,
repoCurrentBranchFiles: () => {
return `${rootPath}/files`
},
repoCurrentBranchCommits: () => `${rootPath}/commits`,
repoCurrentBranchBranches: () => `${rootPath}/branches`,
}
}
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
}
},
repoHomePath: () => {
return `${rootBasePathBranches}/${slugify(reposConfig.repos[repoName].defaultBranch)}`
},
repoCurrentBranchHome: () => {
return currentBranchPath
},
repoCurrentBranchFiles: () => {
return `${currentBranchPath}/files`
},
repoCurrentBranchCommits: () => `${currentBranchPath}/commits`,
repoCurrentBranchBranches: () => `${currentBranchPath}/branches`,
}
}
65 66 67 68 69 70 71 72 73 74 75 76 77 78
if (fsImport.existsSync(repoPath + ".git")) {
// git repos are just in the repos folder, not in their subdir
// create string of commands saying 'git fetch origin branch:branch' for each branch
const fetchCommands = repoConfig.branchesToPull.map(branch => `git fetch origin ${branch}:${branch}`).join('; ')
await exec(`(cd ${eleventyConfig.dir.output + reposPath + "/" + gitRepoName} && ${fetchCommands}; git update-server-info)`)
} else {
// If it is not there, do git clone
// todo: does this work if the latest branch is not checked
// out locally?
const originalLocation = cwd + "/" + repoConfig.location
await exec(`(cd ${eleventyConfig.dir.output + reposPath + "/"} && git clone ${originalLocation} ${gitRepoName} --bare)`)
await exec(`(cd ${eleventyConfig.dir.output + reposPath + "/" + gitRepoName} && git update-server-info)`)
}
if (typeof repoConfig.artifactSteps !== 'undefined') {
// make a temp directory for things to run in65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
if (fsImport.existsSync(repoPath + ".git")) {
// git repos are just in the repos folder, not in their subdir
// create string of commands saying 'git fetch origin branch:branch' for each branch
const location = eleventyConfig.dir.output + reposPath + "/" + gitRepoName
const fetchCommands = repoConfig.branchesToPull.map(branch => `git -C ${location} fetch origin ${branch}:${branch}`).join('; ')
await exec(`${fetchCommands} && git update-server-info`)
} else {
// If it is not there, do git clone
// todo: does this work if the latest branch is not checked
// out locally?
const originalLocation = cwd + "/" + repoConfig.location
await exec(`git clone ${originalLocation} ${eleventyConfig.dir.output + reposPath + "/" + gitRepoName} --bare`)
await exec(`git -C ${eleventyConfig.dir.output + reposPath + "/" + gitRepoName} update-server-info`)
}
if (typeof repoConfig.artifactSteps !== 'undefined') {
// make a temp directory for things to run in83 84 85 86 87 88
await exec(`mkdir ${tempDir}`)
await exec(`git clone -s ${directories.output}${eleventyConfig.getFilter("slugify")(repoName)}.git ${tempDirRepoPath}`)
for (let branch of repoConfig.branchesToPull) {
await exec(`(cd ${tempDirRepoPath} && git checkout ${branch})`)
for (let artifactStep of repoConfig.artifactSteps) {
// Run the command for each step in each branch
await exec(`(cd ${tempDirRepoPath} && ${artifactStep.command})`)83 84 85 86 87 88
await exec(`mkdir ${tempDir}`)
await exec(`git clone -s ${directories.output}${eleventyConfig.getFilter("slugify")(repoName)}.git ${tempDirRepoPath}`)
for (let branch of repoConfig.branchesToPull) {
await exec(`git -C ${tempDirRepoPath} checkout ${branch}`)
for (let artifactStep of repoConfig.artifactSteps) {
// Run the command for each step in each branch
await exec(`(cd ${tempDirRepoPath} && ${artifactStep.command})`)94 95 96 97 98
// delete the temp dirs
await exec(`rm -r ${tempDir}`)
}
}
}
);94 95 96 97 98 99 100 101
// delete the temp dirs
await exec(`rm -r ${tempDir}`)
}
const location = eleventyConfig.dir.output + reposPath + "/" + gitRepoName
await exec(`git -C ${location} symbolic-ref HEAD refs/heads/${repoConfig.defaultBranch}`)
}
}
);202 203 204 205 206 207 208
eleventyConfig.addAsyncFilter("getFileContents", async (repo: string, branch: string, filename: string) => {
const location = getLocation(reposConfiguration, repo)
const command = `git show ${branch}:${filename}`
const res = await exec(`(cd ${location} && ${command})`)
return res.stdout
})
202 203 204 205 206 207 208
eleventyConfig.addAsyncFilter("getFileContents", async (repo: string, branch: string, filename: string) => {
const location = getLocation(reposConfiguration, repo)
const command = `git -C ${location} show ${branch}:${filename}`
const res = await exec(command)
return res.stdout
})
221 222 223 224 225 226 227 228
eleventyConfig.addAsyncFilter("getReadMe", async (repoName: string, branchName: string) => {
const location = getLocation(reposConfiguration, repoName)
const command = `git show ${branchName}:README.md`
try {
const res = await exec(`(cd ${location} && ${command})`)
return res.stdout
} catch {
return ""221 222 223 224 225 226 227 228
eleventyConfig.addAsyncFilter("getReadMe", async (repoName: string, branchName: string) => {
const location = getLocation(reposConfiguration, repoName)
const command = `git -C ${location} show ${branchName}:README.md`
try {
const res = await exec(command)
return res.stdout
} catch {
return ""41 42 43 44 45 46
const branches = await Promise.all(branchNames.map(async (branchName) => {
const repoLocation = getLocation(reposConfig, repoName)
const branchHeadRes = await exec(`(cd ${repoLocation} && git show-ref --branch ${branchName})`)
const branchHead = branchHeadRes.stdout.split(" ")[0]
return {
name: branchName,41 42 43 44 45 46
const branches = await Promise.all(branchNames.map(async (branchName) => {
const repoLocation = getLocation(reposConfig, repoName)
const branchHeadRes = await exec(`git -C ${repoLocation} show-ref --branch ${branchName}`)
const branchHead = branchHeadRes.stdout.split(" ")[0]
return {
name: branchName,4 5 6 7 8 9 10 11
import { type Repository } from '../../dataTypes.ts'
export const getFileList = async (branchName: string, repoLocation: string) => {
const command = `git ls-tree -r --name-only ${branchName}`
const result = await exec(`(cd ${repoLocation} && ${command})`)
let files = result.stdout.split("\n").filter(item => item.length > 0 && item != ".")
// TODO: this could be better. This is adding each sub-path of a file to a set, so that
// we don't wind up with repeats, and then converting that back into an array. E.g. if4 5 6 7 8 9 10 11
import { type Repository } from '../../dataTypes.ts'
export const getFileList = async (branchName: string, repoLocation: string) => {
const command = `git -C ${repoLocation} ls-tree -r --name-only ${branchName}`
const result = await exec(command)
let files = result.stdout.split("\n").filter(item => item.length > 0 && item != ".")
// TODO: this could be better. This is adding each sub-path of a file to a set, so that
// we don't wind up with repeats, and then converting that back into an array. E.g. if38 39 40 41 42 43 44 45 46 47
}
export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<void> => {
const totalPatchesCountRes = await exec(`(cd ${repoLocation} && git rev-list --count ${branchName})`)
const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
let previousHash: null | string = null
for (let i = 0; i < totalPatchesCount; i = i + 10) {
const gitLogSubsetRes = await exec(`(cd ${repoLocation} && git log ${branchName} -p -n 10 --skip ${i})`)
let gitLogSubset = gitLogSubsetRes.stdout.split("\n")
do {
const nextPatchStart = gitLogSubset.findIndex((line, index) => {38 39 40 41 42 43 44 45 46 47
}
export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise<void> => {
const totalPatchesCountRes = await exec(`git -C ${repoLocation} rev-list --count ${branchName}`)
const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
let previousHash: null | string = null
for (let i = 0; i < totalPatchesCount; i = i + 10) {
const gitLogSubsetRes = await exec(`git -C ${repoLocation} log ${branchName} -p -n 10 --skip ${i}`)
let gitLogSubset = gitLogSubsetRes.stdout.split("\n")
do {
const nextPatchStart = gitLogSubset.findIndex((line, index) => {98 99 100 101 102 103 104
export const getFileLastTouchInfo = async (branch: string, filename: string, repoLocation: string) => {
const regex = RegExp(".* [0-9]+ [0-9]+")
const command = `git blame --porcelain ${branch} ${filename}`
const res = await exec(`(cd ${repoLocation} && ${command})`)
const output = res.stdout
const outputLines = output.split("\n")
const initialValue: Array<Array<string>> = [[outputLines[0]]]98 99 100 101 102 103 104
export const getFileLastTouchInfo = async (branch: string, filename: string, repoLocation: string) => {
const regex = RegExp(".* [0-9]+ [0-9]+")
const command = `git -C ${repoLocation} blame --porcelain ${branch} ${filename}`
const res = await exec(command)
const output = res.stdout
const outputLines = output.split("\n")
const initialValue: Array<Array<string>> = [[outputLines[0]]]