Public and Private Repo Harvesting

A multi-repo content pipeline must handle mixed visibility gracefully — token scope, clone failure semantics, and local fallbacks all need explicit design.

Tags

Public and Private Repo Harvesting

The Lesson

When a content pipeline clones from multiple GitHub repositories, mixing public and private repos introduces three failure modes that don't exist in a public-only setup: token scope gaps, clone failures that abort the entire pipeline, and local development without credentials. Each needs an explicit design choice, not a workaround.

Context

Lessons Hub harvests markdown lesson files from multiple GitHub repositories into a single static site. The harvester reads a YAML registry that lists every source repo — each entry specifies an ID, GitHub owner/repo, branch, and the path where lessons live (e.g. docs/lessons). An enabled: true/false flag controls whether the repo is included in the harvest. The harvester clones each enabled repo to a temp directory, parses lesson files, and generates JSON for the Astro build. In CI, LESSONS_REPO_TOKEN is injected from a GitHub secret to authenticate clones. Locally, the developer may or may not have the token set.

The pipeline started with only public repos owned by the same user. Adding a private repo (MoreLessons) immediately broke the CI build because the harvester treats any clone failure as a fatal error.

What Happened

  1. A new private repo (MoreLessons) was added to the registry with enabled: true.
  2. The commit was pushed. CI ran the harvest step.
  3. The harvester attempted to clone MoreLessons. The LESSONS_REPO_TOKEN secret existed but its scope did not include the new repo (fine-grained tokens are scoped per-repository).
  4. git clone failed with could not read Username for 'https://github.com' — the same error you'd get for a nonexistent repo, giving no hint that the real issue was token scope.
  5. The harvester logged this as an [ERROR], and because any error during harvest triggers FATAL ... Aborting, the entire pipeline failed — no lessons from any repo were published.
  6. The fix was to set enabled: false on the new repo until the token and repo were both ready, then push a second commit.

Key Insights

Recommendations

  1. Before enabling a private repo, verify that LESSONS_REPO_TOKEN has read access to it. For fine-grained tokens: GitHub Settings → Developer settings → Fine-grained personal access tokens → edit the token → add the repo under "Repository access."

  2. Add new repos with enabled: false until the repo exists on GitHub, has content at the configured lessons_path, and the token covers it. Flip to enabled: true only after confirming all three.

  3. Consider downgrading clone failures from error to warning in the harvester so that one unreachable repo doesn't block publishing lessons from the other five. Reserve errors for data integrity issues (duplicate IDs, corrupt frontmatter, empty content).

  4. For local development, set LESSONS_REPO_TOKEN in your shell profile or a .env file (never committed) so private repos are harvested locally too. Without it, only public repos will appear in your local build.

Applicability

This applies to any CI pipeline that aggregates content from multiple repositories with mixed visibility — documentation hubs, monorepo federation tools, multi-repo changelog generators, or any system where git clone is a build step. The same token-scope and error-severity considerations apply whenever you mix public and private sources in a single pipeline.

Related Lessons

Related Lessons