Tucker McKnight <tucker.mcknight@gmail.com> | Sun Aug 31 2025
add path config option, which is used to determine the resulting URL
14 15 16 17 18
// TODO: throw an error if reposConfiguration is undefined
const reposData = await repos(reposConfiguration)
eleventyConfig.addFilter("getFileName", (filePath) => {
const pathParts = filePath.split("/")14 15 16 17 18 19 20
// TODO: throw an error if reposConfiguration is undefined
const reposData = await repos(reposConfiguration)
const reposPath = reposConfiguration.path || "/repos"
eleventyConfig.addGlobalData("reposPath", reposPath)
eleventyConfig.addFilter("getFileName", (filePath) => {
const pathParts = filePath.split("/")37 38 39 40 41 42
const repoConfig = reposConfiguration.repos[repoName]
if (repoConfig._type === "darcs") {
for (let branch in repoConfig.branches) {
const repoPath = eleventyConfig.dir.output + "/repos/" + eleventyConfig.getFilter("slugify")(repoName) + "/branches/" + branch
const originalLocation = repoConfig.branches[branch].location
// If it is there, do darcs pull
if (fsImport.existsSync(repoPath + "/_darcs")) {37 38 39 40 41 42
const repoConfig = reposConfiguration.repos[repoName]
if (repoConfig._type === "darcs") {
for (let branch in repoConfig.branches) {
const repoPath = eleventyConfig.dir.output + reposPath + "/" + eleventyConfig.getFilter("slugify")(repoName) + "/branches/" + branch
const originalLocation = repoConfig.branches[branch].location
// If it is there, do darcs pull
if (fsImport.existsSync(repoPath + "/_darcs")) {48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
}
}
} else if (repoConfig._type === "git") {
const repoPath = eleventyConfig.dir.output + "/repos/" + eleventyConfig.getFilter("slugify")(repoName)
const gitRepoName = eleventyConfig.getFilter("slugify")(repoName) + ".git"
// If it is there, do git pull
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 + "/repos/" + gitRepoName} && ${fetchCommands}; git update-server-info)`)
} else {
// If it is not there, do git clone
const originalLocation = repoConfig.location
await exec(`(cd ${eleventyConfig.dir.output + "/repos/"} && git clone ${originalLocation} ${gitRepoName} --bare)`)
await exec(`(cd ${eleventyConfig.dir.output + "/repos/" + gitRepoName} && git update-server-info)`)
}
}
}48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
}
}
} else if (repoConfig._type === "git") {
const repoPath = eleventyConfig.dir.output + reposPath + "/" + eleventyConfig.getFilter("slugify")(repoName)
const gitRepoName = eleventyConfig.getFilter("slugify")(repoName) + ".git"
// If it is there, do git pull
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
const originalLocation = 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)`)
}
}
}196 197 198 199 200 201
'repos/index.njk',
topLayoutPartial + indexTemplate + bottomLayoutPartial,
{
permalink: "repos/index.html",
}
)
196 197 198 199 200 201
'repos/index.njk',
topLayoutPartial + indexTemplate + bottomLayoutPartial,
{
permalink: `${reposPath}/index.html`,
}
)
215 216 217 218 219 220
permalink: (data) => {
const repoName = data.branchInfo.repoName
const branchName = data.branchInfo.branchName
return `repos/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/list/`
},
eleventyComputed: {
nav: {215 216 217 218 219 220
permalink: (data) => {
const repoName = data.branchInfo.repoName
const branchName = data.branchInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/list/`
},
eleventyComputed: {
nav: {243 244 245 246 247 248
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
return `repos/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/${eleventyConfig.getFilter("slugify")(data.fileInfo.file)}.html`
},
eleventyComputed: {
nav: {243 244 245 246 247 248
permalink: (data) => {
const repoName = data.fileInfo.repoName
const branchName = data.fileInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/${eleventyConfig.getFilter("slugify")(data.fileInfo.file)}.html`
},
eleventyComputed: {
nav: {269 270 271 272 273 274
permalink: (data) => {
const repoName = data.branchInfo.repoName
const branchName = data.branchInfo.branchName
return `repos/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/`
},
eleventyComputed: {
nav: {269 270 271 272 273 274
permalink: (data) => {
const repoName = data.branchInfo.repoName
const branchName = data.branchInfo.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/files/`
},
eleventyComputed: {
nav: {295 296 297 298 299 300
permalink: (data) => {
const repoName = data.branch.repoName
const branchName = data.branch.branchName
return `repos/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/`
},
eleventyComputed: {
nav: {295 296 297 298 299 300
permalink: (data) => {
const repoName = data.branch.repoName
const branchName = data.branch.branchName
return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/`
},
eleventyComputed: {
nav: {45 46 47 48 49 50
<div class="row d-flex justify-content-between pb-3">
<div class="col-auto">
<nav class="fs-4">
<a href="/">Repositories</a>{% if nav.repoName %}<span class="text-secondary mx-2">></span><a href="/repos/{{nav.repoName | slugify}}/branches/{{reposConfig.repos[nav.repoName].defaultBranch | slugify}}">{{nav.repoName}}</a>{% endif %}
</nav>
</div>
<div class="col-auto d-flex align-items-center">45 46 47 48 49 50
<div class="row d-flex justify-content-between pb-3">
<div class="col-auto">
<nav class="fs-4">
<a href="/">Repositories</a>{% if nav.repoName %}<span class="text-secondary mx-2">></span><a href="{{reposPath}}/{{nav.repoName | slugify}}/branches/{{reposConfig.repos[nav.repoName].defaultBranch | slugify}}">{{nav.repoName}}</a>{% endif %}
</nav>
</div>
<div class="col-auto d-flex align-items-center">67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
<nav class="nav-tabs">
<ul class="nav">
<li class="nav-item">
<a class="nav-link {% if navTab == "landing" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName}}">Landing Page</a>
</li>
<li class="nav-item">
<a class="nav-link {% if navTab == "files" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName}}/files">Files</a>
</li>
<li class="nav-item">
<a class="nav-link {% if navTab == "patches" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName}}/patches/page1">Changes</a>
</li>
<li class="nav-item">
<a class="nav-link {% if navTab == "branches" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName | slugify}}/list">Branches</a>
</li>
</ul>
</nav>67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
<nav class="nav-tabs">
<ul class="nav">
<li class="nav-item">
<a class="nav-link {% if navTab == "landing" %}active{% endif %}" href="{{reposPath}}/{{nav.repoName | slugify}}/branches/{{nav.branchName}}">Landing Page</a>
</li>
<li class="nav-item">
<a class="nav-link {% if navTab == "files" %}active{% endif %}" href="{{reposPath}}/{{nav.repoName | slugify}}/branches/{{nav.branchName}}/files">Files</a>
</li>
<li class="nav-item">
<a class="nav-link {% if navTab == "patches" %}active{% endif %}" href="{{reposPath}}/{{nav.repoName | slugify}}/branches/{{nav.branchName}}/patches/page1">Changes</a>
</li>
<li class="nav-item">
<a class="nav-link {% if navTab == "branches" %}active{% endif %}" href="{{reposPath}}/{{nav.repoName | slugify}}/branches/{{nav.branchName | slugify}}/list">Branches</a>
</li>
</ul>
</nav>1 2 3 4 5 6
{% for branch in branches %}
{% set description = reposConfig.repos[branch.repoName].branches[branch.branchName].description %}
{% if branch.repoName == branchInfo.repoName %}
<li><a href="/repos/{{branch.repoName | slugify}}/branches/{{branch.branchName | slugify}}">{{branch.branchName}}</a>{% if branch.branchName == branchInfo.branchName %} (current){% endif %}{% if description %} - {{ description }}{% endif %}</li>
{% endif %}
{% endfor %}
</ul>1 2 3 4 5 6
{% for branch in branches %}
{% set description = reposConfig.repos[branch.repoName].branches[branch.branchName].description %}
{% if branch.repoName == branchInfo.repoName %}
<li><a href="{{reposPath}}/{{branch.repoName | slugify}}/branches/{{branch.branchName | slugify}}">{{branch.branchName}}</a>{% if branch.branchName == branchInfo.branchName %} (current){% endif %}{% if description %} - {{ description }}{% endif %}</li>
{% endif %}
{% endfor %}
</ul>8 9 10 11 12 13
{% else %}
<i class="bi bi-file-earmark"></i>
{% endif %}
<a href="/repos/{{fileInfo.repoName | slugify}}/branches/{{fileInfo.branchName | slugify}}/files/{{dir.fullPath | slugify}}.html">{{fileInfo.file | getRelativePath(dir.name)}}</a>
</li>
{% endfor %}
</ul>8 9 10 11 12 13
{% else %}
<i class="bi bi-file-earmark"></i>
{% endif %}
<a href="{{reposPath}}/{{fileInfo.repoName | slugify}}/branches/{{fileInfo.branchName | slugify}}/files/{{dir.fullPath | slugify}}.html">{{fileInfo.file | getRelativePath(dir.name)}}</a>
</li>
{% endfor %}
</ul>33 34 35 36 37 38
{% set annotations = fileInfo.repoName | getFileLastTouchInfo(fileInfo.branchName, fileInfo.file) %}
<code style="white-space: pre;"><pre class="language-text">
{%- for annotation in annotations -%}
<a href="/repos/{{fileInfo.repoName | slugify}}/branches/{{fileInfo.branchName | slugify}}/patches/{{annotation.sha}}">{{ (annotation.sha | truncate(6, true, '')) }}</a> {{ annotation.author }}
{% endfor -%}
</pre></code>
</div>33 34 35 36 37 38
{% set annotations = fileInfo.repoName | getFileLastTouchInfo(fileInfo.branchName, fileInfo.file) %}
<code style="white-space: pre;"><pre class="language-text">
{%- for annotation in annotations -%}
<a href="{{reposPath}}/{{fileInfo.repoName | slugify}}/branches/{{fileInfo.branchName | slugify}}/patches/{{annotation.sha}}">{{ (annotation.sha | truncate(6, true, '')) }}</a> {{ annotation.author }}
{% endfor -%}
</pre></code>
</div>8 9 10 11 12 13
{% else %}
<i class="bi bi-file-earmark"></i>
{% endif %}
<a href="/repos/{{branchInfo.repoName | slugify}}/branches/{{branchInfo.branchName | slugify}}/files/{{file.fullPath | slugify}}.html">{{file.name}}</a>
</li>
{% endfor %}
</ul>8 9 10 11 12 13
{% else %}
<i class="bi bi-file-earmark"></i>
{% endif %}
<a href="{{reposPath}}/{{branchInfo.repoName | slugify}}/branches/{{branchInfo.branchName | slugify}}/files/{{file.fullPath | slugify}}.html">{{file.name}}</a>
</li>
{% endfor %}
</ul>0 1 2 3
<ul>
{% for repoName, options in repos %}
<li><a href="/repos/{{repoName | slugify}}/branches/{{reposConfig.repos[repoName].defaultBranch}}">{{repoName}}</a></li>
{% endfor %}
</ul>0 1 2 3
<ul>
{% for repoName, options in repos %}
<li><a href="{{reposPath}}/{{repoName | slugify}}/branches/{{reposConfig.repos[repoName].defaultBranch}}">{{repoName}}</a></li>
{% endfor %}
</ul>9 10 11 12 13 14 15 16 17 18 19 20
<h2 class="fs-6 my-0">Recent patches in {{branch.branchName}}</h2>
</div>
<div class="col-auto">
<a href="/repos/{{ branch.repoName | slugify }}/branches/{{ branch.branchName | slugify }}/patches.xml" class="initialism">RSS<i class="bi bi-rss-fill ms-2" style="color: orange;"></i></a>
</div>
</div>
{% for patch in repos[branch.repoName].branches[branch.branchName].patches | batch(3) | first %}
<div class="card mt-2 mb-4">
<div class="card-body">
<a href="/repos/{{branch.repoName | slugify}}/branches/{{branch.branchName | slugify}}/patches/{{patch.hash}}" class="text-primary d-inline-block card-title fs-5">{{patch.name}}</a>
<p class="card-subtitle fs-6 mb-2 text-body-secondary">{{patch.date}}</p>
<p class="card-subtitle fs-6 mb-2 text-body-secondary">{{patch.author}}</p>
<p class="card-text">{{patch.description | truncate(150)}}</p>9 10 11 12 13 14 15 16 17 18 19 20
<h2 class="fs-6 my-0">Recent patches in {{branch.branchName}}</h2>
</div>
<div class="col-auto">
<a href="{{reposPath}}/{{ branch.repoName | slugify }}/branches/{{ branch.branchName | slugify }}/patches.xml" class="initialism">RSS<i class="bi bi-rss-fill ms-2" style="color: orange;"></i></a>
</div>
</div>
{% for patch in repos[branch.repoName].branches[branch.branchName].patches | batch(3) | first %}
<div class="card mt-2 mb-4">
<div class="card-body">
<a href="{{reposPath}}/{{branch.repoName | slugify}}/branches/{{branch.branchName | slugify}}/patches/{{patch.hash}}" class="text-primary d-inline-block card-title fs-5">{{patch.name}}</a>
<p class="card-subtitle fs-6 mb-2 text-body-secondary">{{patch.date}}</p>
<p class="card-subtitle fs-6 mb-2 text-body-secondary">{{patch.author}}</p>
<p class="card-text">{{patch.description | truncate(150)}}</p>