Branch

commit first line goes here

Mon Nov 10 2025

Tucker McKnight <tucker@pangolin.lan>

Switching some more things over to the new Repository type

Also removed anything that prints out commits in order, as
that needs to be re-thought for the new format. So I left some
todos in there.

7e40e5b6c81b4a222015b3d5586bc53eebd258c5

Side-by-side
Stacked
main.ts:93
Before
93
    return reposData[repo].branches[branch].files.filter(file => file.startsWith(dirPath) && file !== dirPath)
After
93
    return reposData.find(current => current.name === repo).branches[branch].fileList.filter(file => file.startsWith(dirPath) && file !== dirPath)
main.ts:110
Before
110
  eleventyConfig.addFilter("highlightCode", (code, language) => {
After
110
  eleventyConfig.addFilter("highlightCode", (code: string, language: string) => {
main.ts:186
Before
186 187 188 189
  eleventyConfig.addFilter("isDirectory", (filename, repoName, branchName) => {
    const files = reposData[repoName].branches[branchName].files
  eleventyConfig.addAsyncFilter("getFileContents", async (repo, branch, filename) => {
    const config = reposConfiguration.repos[repo]
After
186 187 188 189
  eleventyConfig.addFilter("isDirectory", (filename: string, repoName: string, branchName: string) => {
    const repo = reposData.find(repo => repo.name === repoName)
    const files = repo.branches.find(branch => branch.name === branchName).fileList
  eleventyConfig.addAsyncFilter("getFileContents", async (repo: string, branch: string, filename: string) => {
main.ts:216
Before
216
    const config = reposConfiguration.repos[repoName]
After
216
main.ts:336
Before
336
        }
After
336 337 338 339 340 341 342 343 344
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branchInfo.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.branchInfo.repoName
        }).branches.find(branch => {
          return branch.name === data.branchInfo.branchName
        })
main.ts:362
Before
362
        }
After
362 363 364 365 366 367 368 369 370
        },
        currentRepo: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
        }),
        currentBranch: (data) => reposData.find(repo => {
          return repo.name === data.branch.repoName
        }).branches.find(branch => {
          return branch.name === data.branch.branchName
        }),
main.ts:398
Before
398
  const flatPatchesData = await flatPatches(reposConfiguration)
After
398
  const flatPatchesData = await flatPatches(reposData)
main.ts:429
Before
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
  if (eleventyConfig?.javascript?.functions?.htmlBaseUrl) {
    rssAvailable = true
    const feedTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/feed.njk`).toString()
    eleventyConfig.addTemplate(
      `repos/feed.njk`,
      feedTemplate,
      {
        pagination: {
          data: "branches",
          size: 1,
          alias: "branch",
        },
        permalink: (data) => {
          const repoName = data.branch.repoName
          const branchName = data.branch.branchName
          return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches.xml`
        },
        eleventyExcludeFromCollections: true,
      }
    )
  }
After
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
  // if (eleventyConfig?.javascript?.functions?.htmlBaseUrl) {
  //   rssAvailable = true
  //   const feedTemplate = fsImport.readFileSync(`${import.meta.dirname}/templates/feed.njk`).toString()
  //   eleventyConfig.addTemplate(
  //     `repos/feed.njk`,
  //     feedTemplate,
  //     {
  //       pagination: {
  //         data: "branches",
  //         size: 1,
  //         alias: "branch",
  //       },
  //       permalink: (data) => {
  //         const repoName = data.branch.repoName
  //         const branchName = data.branch.branchName
  //         return `${reposPath}/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${eleventyConfig.getFilter("slugify")(branchName)}/patches.xml`
  //       },
  //       eleventyComputed: {
  //         currentRepo: (data) => reposData.find(repo => {
  //           return repo.name === data.branch.repoName
  //         }),
  //         currentBranch: (data) => reposData.find(repo => {
  //           return repo.name === data.branch.repoName
  //         }).branches.find(branch => {
  //           return branch.name === data.branch.branchName
  //         }),
  //       },
  //       eleventyExcludeFromCollections: true,
  //     }
  //   )
  // }
partial_templates/main_top.njk:83
Before
83 84 85
                        {% for branch in branches %}
                        {% if branch.repoName == nav.repoName %}
                        {% endif %}
After
83
                        {% for branch in repos[nav.repoName].branches %}
schemas/ReposConfiguration.json:34
Before
34
            "type": "string"
After
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
            "anyOf": [
              {
                "type": "string"
              },
              {
                "additionalProperties": false,
                "properties": {
                  "max": {
                    "type": "number"
                  },
                  "regex": {
                    "type": "string"
                  }
                },
                "required": [
                  "regex",
                  "max"
                ],
                "type": "object"
              }
            ]
schemas/ReposConfiguration.json:52
Before
52
After
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
        },
        "tags": {
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "additionalProperties": false,
                "properties": {
                  "max": {
                    "type": "number"
                  },
                  "regex": {
                    "type": "string"
                  }
                },
                "required": [
                  "regex",
                  "max"
                ],
                "type": "object"
              }
            ]
          },
          "type": "array"
src/branches.ts:1
Before
1 2 3 4 5
export default (repos) => {
  cachedBranches = Object.keys(repos).flatMap((repoName) => {
    return Object.keys(repos[repoName].branches).map((branchName) => {
        branchName,
        repoName,
After
1 2 3 4 5 6 7
import { type Repository } from "./dataTypes.ts"

export default (repos: Array<Repository>) => {
  cachedBranches = repos.flatMap((repo) => {
    return repo.branches.map((branch) => {
        branchName: branch.name,
        repoName: repo.name,
src/configTypes.ts:51
Before
51
  branchesToPull: Array<string>,
After
51 52
  branchesToPull: Array<string | {regex: string, max: number}>,
  tags?: Array<string | {regex: string, max: number}>,
src/dataTypes.ts:5
Before
5
    description?: string,
After
5 6 7
    fileList: Array<string>,
    fileList: Array<string>,
    hash: string,
src/dataTypes.ts:25
Before
25 26 27 28 29

export type BranchInfo = {
  files: Array<string>,
  patches: Array<any>
}
After
25
src/flatFiles.ts:1
Before
1 2 3 4 5 6 7
import repos from &#39;./repos.ts&#39;
export default (repos) => {
  cachedFlatFiles = Object.keys(repos).flatMap((repoName) => {
    return Object.keys(repos[repoName].branches).flatMap((branchName) => {
      return repos[repoName].branches[branchName].files.map((file) => {
          branchName,
          repoName,
After
1 2 3 4 5 6 7
import { type Repository } from &quot;./dataTypes.ts&quot;
export default (repos: Array<Repository>) => {
  cachedFlatFiles = repos.flatMap((repo) => {
    return repo.branches.flatMap((branch) => {
      return branch.fileList.map((file) => {
          branchName: branch.name,
          repoName: repo.name,
src/flatPatches.ts:1
Before
1 2 3 4 5 6 7 8 9 10
export default async (repos) => {
  cachedFlatPatches = Object.keys(repos).flatMap((repoName) => {
    return Object.keys(repos[repoName].branches).flatMap((branchName) => {
      return repos[repoName].branches[branchName].patches.map((patch) => {
        return {
          patch,
          branchName,
          repoName,
        }
      })
After
1 2 3 4 5 6
import { type Repository } from "./dataTypes.ts"

export default async (repos: Array<Repository>) => {
  cachedFlatPatches = repos.flatMap((repo) => {
    return repo.branches.flatMap((branch) => {
      return [] // todo implement this with new commits format
src/repos.ts:1
Before
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
import {type BranchInfo} from './dataTypes.ts'
import repoOperations from './vcses/operations.ts'
import { getBranchInfo } from './vcses/git/operations.ts'
import repoHelpers from './vcses/helpers.ts&#39;

type BranchInfoTuple = [string, BranchInfo]
type BranchObject = {
  [key: string]: BranchInfo
}
type RepoObjectTuple = [string, BranchObject]
type RepoObject = {
  [key: string]: {
    branches: BranchObject,
    cloneUrl: string,
  }
}
  return repoConfig.branchesToPull
const repos: (reposConfig: any) => Promise<RepoObject> = async (reposConfig) => {
  const reposTuples: RepoObjectTuple[] = await Promise.all(repoNames.map(async (repoName): Promise<RepoObjectTuple> => {
    const vcs = reposConfig.repos[repoName]._type
    const branchTuples: BranchInfoTuple[] = await Promise.all(branchNames.map(async (branchName): Promise&lt;BranchInfoTuple&gt; => {
      const files = await repoOperations[vcs].getFileList(repoName, branchName, repoLocation)
      const patches = await getBranchInfo(branchName, repoLocation)
      return [branchName, {
        files,
        patches,
      }]
    }))

    const branchesObject: BranchObject = {}
    for (let branchTuple of branchTuples) {
      branchesObject[branchTuple[0]] = branchTuple[1]
    return [repoName, branchesObject]
  }))
  const reposObject: RepoObject = {}
  for (let repoTuple of reposTuples) {
    const repoName = repoTuple[0]
    const repoType = reposConfig.repos[repoName]._type
    reposObject[repoName] = {
      branches: repoTuple[1],
      cloneUrl: repoHelpers[repoType].cloneUrl(reposConfig.baseUrl + (reposConfig.path || ""), repoName)
    }
  cachedRepos = reposObject
  return reposObject
After
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
  type ReposConfiguration,
import { type Repository} from './dataTypes.ts'
import { addBranchToCommitsMap } from './vcses/git/operations.ts'
  return repoConfig.branchesToPull.map((branch) =&gt; {
    if (typeof branch === "string") {
      return branch
    }
    else {
      return "" // todo
    }
  })
const repos: (reposConfig: ReposConfiguration) => Promise<Array&lt;Repository&gt;> = async (reposConfig) => {
  const repos: Array<Repository> = []

  for (const repoName of repoNames) {
    const commits: Repository[&#39;commits&#39;] = new Map()
    for (const branchName of branchNames) {
      await addBranchToCommitsMap(branchName, repoLocation, commits)
    repos.push({
      name: repoName,
      branches: branchNames.map((branchName) => {
        return {
          name: branchName,
          head: "", // todo
          fileList: [], //todo
        }
      }),
      cloneUrl: "",
      defaultBranch: "main",
      tags: [],
      commits,
    })
  return repos
src/vcses/git/operations.ts:38
Before
38 39 40 41 42 43 44 45 46 47
export const getBranchInfo = async (branchName: string, repoLocation: string) => {
  const patches: Map<string, {
    name: string,
    description: string,
    author: string,
    date: string,
    hash: string,
    diffs: ReturnType<Repository['commits'][&#39;get&#39;]&gt;[&#39;diffs'],
  }&gt; = new Map()
After
38
export const addBranchToCommitsMap = async(branchName: string, repoLocation: string, commits: Repository['commits']): Promise&lt;void> => {
src/vcses/git/operations.ts:62
Before
62
After
62 63 64 65 66 67

      // exit early if the commits map already includes this hash
      if (commits.has(hash)) {
        break
      }
src/vcses/git/operations.ts:75
Before
75 76 77 78 79 80 81 82 83 84 85 86
      const commitMessage = currentPatch.slice(4, diffStart).map(str => str.replace("    ", ""))
      const name = commitMessage[0].trim()
      const description = commitMessage.slice(1, commitMessage.length - 1).filter((line) => {
      patches.set(hash, {
        name,
        description,
        author,
        date,
        diffs,

  return Array.from(patches.values())
export const getFileLastTouchInfo = async (repo, branch, filename, repoLocation) => {
After
75 76 77 78 79 80 81 82
      const commitMessage = currentPatch.slice(4, diffStart).map(str => str.replace("    ", "")).filter((line) => {
      commits.set(hash, {
        message: commitMessage,
        author,
        date: new Date(date),
                  diffs,
                  parent: "todo",
export const getFileLastTouchInfo = async (repo: string, branch: string, filename: string, repoLocation: string) => {
src/vcses/operations.ts:2
Before
2 3
import {type BranchInfo} from '../dataTypes.ts'
    getFileList: (repoName: string, branchName: string, repoLocation: string) => Promise<BranchInfo[&#39;files&#39;]>,
After
2
    getFileList: (repoName: string, branchName: string, repoLocation: string) => Promise<Array&lt;string&gt;>,
templates/files.njk:1
Before
1
    {% set files = repos[branchInfo.repoName].branches[branchInfo.branchName].files | topLevelFilesOnly('') %}
After
1
    {% set files = currentBranch.fileList | topLevelFilesOnly('') %}
templates/index.njk:1
Before
1 2
{% for repoName, options in repos %}
  <li><a href="{{reposPath}}/{{repoName | slugify}}/branches/{{reposConfig.repos[repoName].defaultBranch}}">{{repoName}}</a></li>
After
1 2
{% for repo in repos %}
  <li><a href="{{reposPath}}/{{repo.name | slugify}}/branches/{{reposConfig.repos[repo.name].defaultBranch}}">{{repo.name}}</a></li>
templates/repo.njk:15
Before
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
        {% 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>
          </div>
          <div class="card-footer">
            <button data-hash="{{patch.hash}}" data-vcs="git" class="copy-btn btn btn-sm btn-outline-primary">
              {{patch.hash | truncate(8, true, "")}} <i class="bi-copy bi me-1"></i>
            </button>
          </div>
        </div>
        {% endfor %}
After
15