Tucker McKnight <tucker@pangolin.lan> | Mon Dec 01 2025
some work on using the design-board page style
-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
import { type ReposConfiguration } from "../../src/configTypes.ts"
export const NavHelper = (reposConfig: ReposConfiguration, slugify: Function, repoName: string, branchName: string) => {
const reposPath = reposConfig.baseUrl
const rootPath = `${reposPath}/${slugify(repoName)}/branches`
return {
rootPath: () => {
if (reposPath === "") {
return "/"
}
else {
return reposPath
}
},
repoHomePath: () => {
return `${rootPath}/${slugify(reposConfig.repos[repoName].defaultBranch)}`
},
repoCurrentBranchHome: () => rootPath,
repoCurrentBranchFiles: () => {
return `${rootPath}/files`
},
repoCurrentBranchCommits: () => `${rootPath}/commits`,
repoCurrentBranchBranches: () => `${rootPath}/branches`,
}
}
10 11 12 13 14
import {ReposConfiguration} from './src/configTypes.ts'
import {Ajv} from 'ajv'
import ConfigSchema from './schemas/ReposConfiguration.json' with { type: 'json' }
const ajv = new Ajv()
const exec = util.promisify(childProcess.exec)10 11 12 13 14 15
import {ReposConfiguration} from './src/configTypes.ts'
import {Ajv} from 'ajv'
import ConfigSchema from './schemas/ReposConfiguration.json' with { type: 'json' }
import repoJsTemplate from './js_templates/repo.ts'
const ajv = new Ajv()
const exec = util.promisify(childProcess.exec)21 22 23 24 25
throw new Error(validator.errors.map(error => `config object at ${error.instancePath.replaceAll("/", ".")}: ${error.message}`).join("\n"))
}
const reposData = await repos(reposConfiguration)
// TODO: make a better way of making this default to "" so that it doesn't have to
// be done again in src/repos.ts.21 22 23 24 25 26 27
throw new Error(validator.errors.map(error => `config object at ${error.instancePath.replaceAll("/", ".")}: ${error.message}`).join("\n"))
}
eleventyConfig.addTemplateFormats("js")
const reposData = await repos(reposConfiguration)
// TODO: make a better way of making this default to "" so that it doesn't have to
// be done again in src/repos.ts.40 41 42 43 44 45 46 47 48
const vendor = `${import.meta.dirname}/vendor`
const frontend = `${import.meta.dirname}/frontend`
eleventyConfig.addPassthroughCopy({
[vendor]: `${reposPath}/vendor`,
[frontend]: `${reposPath}/frontend`,
})
eleventyConfig.on(40 41 42 43 44 45 46 47 48 49 50
const vendor = `${import.meta.dirname}/vendor`
const frontend = `${import.meta.dirname}/frontend`
const css = `${import.meta.dirname}/css`
eleventyConfig.addPassthroughCopy({
[vendor]: `${reposPath}/vendor`,
[frontend]: `${reposPath}/frontend`,
[css]: `${reposPath}/css`,
})
eleventyConfig.on(397 398 399 400 401 402 403 404 405
return branch.name === data.branch.branchName
}),
},
navTab: "landing",
}
)
// PATCHES.NJK
const patchesTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/patches.njk`).toString()
const paginatedPatchesData = await paginatedPatches(reposData)397 398 399 400 401 402 403 404 405 406
return branch.name === data.branch.branchName
}),
},
}
)
// PATCHES.NJK
const patchesTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/patches.njk`).toString()
const paginatedPatchesData = await paginatedPatches(reposData)0 1 2 3 4
#!/bin/bash
mkdir -p dist
mkdir -p dist/templates
mkdir -p dist/partial_templates
cp -r frontend dist/
cp templates/*.njk dist/templates0 1 2 3 4 5
#!/bin/bash
mkdir -p dist
mkdir -p dist/templates
mkdir -p dist/css
mkdir -p dist/partial_templates
cp -r frontend dist/
cp templates/*.njk dist/templates14 15 16 17 18
},
"devDependencies": {
"@types/node": "^24.0.7",
"ts-json-schema-generator": "^2.4.0",
"typedoc": "^0.28.13",
"typescript": "^5.8.3"14 15 16 17 18 19 20
},
"devDependencies": {
"@types/node": "^24.0.7",
"bootstrap": "^5.3.8",
"sass": "^1.94.0",
"ts-json-schema-generator": "^2.4.0",
"typedoc": "^0.28.13",
"typescript": "^5.8.3"203 204 205 206 207
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/bootstrap": {
"version": "5.3.8",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
"integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
],
"peerDependencies": {
"@popperjs/core": "^2.11.8"
}
},
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",257 258 259 260 261
"node": ">= 8"
}
},
"node_modules/diff": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz",257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
"node": ">= 8"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/diff": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz",314 315 316 317 318
],
"license": "BSD-3-Clause"
},
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
],
"license": "BSD-3-Clause"
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"optional": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",355 356 357 358 359
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/immutable": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
"integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
"dev": true
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",365 366 367 368 369
"node": ">=8"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
"node": ">=8"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"optional": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"optional": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",461 462 463 464 465
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"dev": true
},
"node_modules/minimatch": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"dev": true
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"optional": true,
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/minimatch": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",487 488 489 490 491
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",487 488 489 490 491 492 493 494 495 496 497 498
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"optional": true
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",531 532 533 534 535
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"optional": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",540 541 542 543 544
"node": ">=6"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
"node": ">=6"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",559 560 561 562 563
"node": ">=10"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
"node": ">=10"
}
},
"node_modules/sass": {
"version": "1.94.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.94.0.tgz",
"integrity": "sha512-Dqh7SiYcaFtdv5Wvku6QgS5IGPm281L+ZtVD1U2FJa7Q0EFRlq8Z3sjYtz6gYObsYThUOz9ArwFqPZx+1azILQ==",
"dev": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",595 596 597 598 599
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",595 596 597 598 599 600 601 602 603 604 605 606 607 608
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",699 700 701 702 703
"node": ">=8"
}
},
"node_modules/ts-json-schema-generator": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.4.0.tgz",699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
"node": ">=8"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"optional": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/ts-json-schema-generator": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.4.0.tgz",3 4 5 6 7 8 9 10 11
"version": "0.0.1-alpha.1",
"main": "dist/main.js",
"scripts": {
"build": "npm run schemas && npm run ts && npm run docs",
"ts": "./make.sh && npx tsc",
"schemas": "npx ts-json-schema-generator --path src/configTypes.ts --type ReposConfiguration > schemas/ReposConfiguration.json",
"docs": "npx typedoc src/configTypes.ts --readme none"
},
"keywords": [],
"author": "",3 4 5 6 7 8 9 10 11 12 13
"version": "0.0.1-alpha.1",
"main": "dist/main.js",
"scripts": {
"build": "npm run schemas && npm run ts && npm run docs && npm run css",
"ts": "./make.sh && npx tsc",
"schemas": "npx ts-json-schema-generator --path src/configTypes.ts --type ReposConfiguration > schemas/ReposConfiguration.json",
"docs": "npx typedoc src/configTypes.ts --readme none",
"css": "npx sass scss/:dist/css/",
"css:watch": "npx sass --watch scss/:dist/css/"
},
"keywords": [],
"author": "",14 15 16 17 18
"description": "",
"devDependencies": {
"@types/node": "^24.0.7",
"ts-json-schema-generator": "^2.4.0",
"typedoc": "^0.28.13",
"typescript": "^5.8.3"14 15 16 17 18 19 20
"description": "",
"devDependencies": {
"@types/node": "^24.0.7",
"bootstrap": "^5.3.8",
"sass": "^1.94.0",
"ts-json-schema-generator": "^2.4.0",
"typedoc": "^0.28.13",
"typescript": "^5.8.3"4 5 6 7 8 9 10 11 12
"GitConfig": {
"additionalProperties": false,
"properties": {
"_type": {
"const": "git",
"type": "string"
},
"artifactSteps": {
"items": {
"additionalProperties": false,4 5 6 7 8
"GitConfig": {
"additionalProperties": false,
"properties": {
"artifactSteps": {
"items": {
"additionalProperties": false,100 101 102 103 104 105
}
},
"required": [
"_type",
"location",
"defaultBranch",
"branchesToPull"100 101 102 103 104
}
},
"required": [
"location",
"defaultBranch",
"branchesToPull"9 10 11 12 13 14
* baseUrl: "https://repos.tuckerm.us",
* repos: {
* "My Git Project": {
* _type: "git",
* defaultBranch: 'main',
* branches: ['main', 'develop']
* },9 10 11 12 13
* baseUrl: "https://repos.tuckerm.us",
* repos: {
* "My Git Project": {
* defaultBranch: 'main',
* branches: ['main', 'develop']
* },43 44 45 46 47 48
}
export type GitConfig = {
_type: "git",
/* The absolute path to the repository.
* @example location: "/home/alice/projects/git_repo"
*/43 44 45 46 47
}
export type GitConfig = {
/* The absolute path to the repository.
* @example location: "/home/alice/projects/git_repo"
*/23 24 25 26 27 28 29 30 31 32 33 34
})
}
let cachedRepos = null
const repos: (reposConfig: ReposConfiguration) => Promise<Array<Repository>> = async (reposConfig) => {
if (cachedRepos !== null) { return cachedRepos }
const repoNames = Object.keys(reposConfig.repos)
const repos: Array<Repository> = []
for (const repoName of repoNames) {
const branchNames = getBranchNames(reposConfig.repos[repoName])23 24 25 26 27 28 29 30 31 32 33 34
})
}
let cachedRepos: Array<Repository> | null = null
const repos: (reposConfig: ReposConfiguration) => Promise<Array<Repository>> = async (reposConfig) => {
if (cachedRepos !== null) { return cachedRepos }
const repoNames = Object.keys(reposConfig.repos)
cachedRepos = []
for (const repoName of repoNames) {
const branchNames = getBranchNames(reposConfig.repos[repoName])50 51 52 53 54 55 56
}
}))
repos.push({
name: repoName,
branches,
cloneUrl: cloneUrl.cloneUrl(reposConfig.baseUrl, repoName),
defaultBranch: "main",50 51 52 53 54 55 56 57
}
}))
cachedRepos.push({
name: repoName,
description: reposConfig.repos[repoName].description,
branches,
cloneUrl: cloneUrl.cloneUrl(reposConfig.baseUrl, repoName),
defaultBranch: "main",60 61 62 63 64 65
})
}
return repos
}
export default repos60 61 62 63 64 65
})
}
return cachedRepos
}
export default repos