Skip to main content

Context​

In the eigenoid org, repositories were created and deleted ad-hoc through the GitHub UI. This caused several problems:

  • Inconsistent configuration: new repos appeared without labels, rulesets, team permissions, or the org's standard configuration. safe-settings would eventually configure them, but there was a window where the repo lived ungoverned.
  • No audit trail: there was no record of who created or deleted a repo, or why. The history was buried in GitHub audit logs, which are hard to query.
  • No review process: any org member could create a repo without approval. Deletion required admin access, but had no peer review or documented justification.
  • Risk of accidental deletion: GitHub deletes repos permanently with no recovery window. An accidental (or malicious) click could destroy a repo and its entire history.
  • Repos outside governance: manually created repos had no entry in the safe-settings admin repo until someone added one manually, leaving a configuration gap.

ADR-0006 established safe-settings as the source of truth for GitHub configuration, but did not address the lifecycle (creation, archival, deletion) of repositories.

Decision​

We implement full repository lifecycle management via issue templates and GitHub Actions workflows in eigenoid/platform-settings. Repo creation by org members is disabled at the org level.

Repository creation​

  1. A member opens an issue in platform-settings using the "Request new repository" template.
  2. The new-repo.yml workflow triggers and generates a PR with the repo's YAML configuration file for safe-settings, using force_create: true.
  3. The PR is authored by eigenoid-settings-bot[bot] (both the commit and the PR), includes a table with the repo details, a review checklist, and Closes #N to auto-close the issue.
  4. A reviewer approves and merges the PR.
  5. safe-settings detects the merge and creates the repo with full configuration (labels, rulesets, teams, settings) in a single step.

Result: the repo is born fully governed, with a complete audit trail (issue → PR → merge → creation).

Archival (soft-delete)​

  1. A member opens an issue using the "Request repo deletion" template. The notify-lifecycle-approvers.yml workflow automatically notifies @eigenoid/platform-lifecycle-approvers.
  2. A member of @eigenoid/platform-lifecycle-approvers applies the approved-deletion label. The archive-repo.yml workflow verifies the sender's team membership before proceeding.
  3. The workflow archives the repo on GitHub and records the entry in archived-repos.json with a timestamp, the requester, and a reference to the issue.

Result: the repo becomes read-only but recoverable. The entry in archived-repos.json starts the 30-day countdown.

Permanent deletion​

  1. The delete-archived-repos.yml workflow runs daily via CRON.
  2. It reads archived-repos.json and permanently deletes repos archived for more than 30 days.
  3. It removes the registry entry and notifies the original issue author.

Result: 30-day recovery window. After that, automatic and clean deletion.

Lifecycle ruleset​

An org-level ruleset restricts repository lifecycle operations:

  • Restrictions: repo creation, deletion, transfer, and visibility changes.
  • Bypass: only OrganizationAdmin and the eigenoid-settings-bot GitHub App can perform these operations directly.
  • Admin repo: platform-settings has its own repo-level rulesets (require PR + 1 approval) since it is excluded from org-level rulesets to prevent lockout.

Concurrency​

The archival and deletion workflows (archive-repo.yml and delete-archived-repos.yml) use the repo-lifecycle concurrency group to prevent race conditions between archival and purge.

Consequences​

  • Complete audit trail: every creation and deletion is an issue with discussion, approvals, and linked PRs. The history is browsable by any org member.
  • YAML as source of truth: repo configuration lives in platform-settings alongside the rest of the governance config. A repo exists if and only if it has its YAML file.
  • 30-day recovery window: archived repos can be recovered before permanent deletion. Eliminates the risk of irreversible accidental loss.
  • Friction for emergency operations: creating or deleting a repo requires an issue and a PR. Mitigated by the OrganizationAdmin and App bypass in the lifecycle ruleset, which allows direct operations in urgent cases.
  • Bot authorship: PRs and commits are attributed to eigenoid-settings-bot[bot], not the generic github-actions[bot]. Consistent with ADR-0004 and the App's identity.
  • Single governance point: the entire lifecycle flows through a single repo (platform-settings) and a set of workflows. Simplifies auditing and reduces the error surface.
  • Team-based authorization: the platform-lifecycle-approvers team (managed in settings.yml) controls who can approve deletions. The workflow verifies membership before archiving, and CODEOWNERS requires team review for changes to workflows and configuration.

Alternatives considered​

  • Manual click-ops (status quo): no audit trail, inconsistent configuration, no recovery window. Does not scale and is not reproducible. Unacceptable for an org with multiple repos and contributors.
  • Terraform github_repository: good IaC, but does not offer an issue-based workflow, has no recovery window, and is heavier for managing lifecycle alone. Terraform is already used for GCP (ADR-0008); adding GitHub here would mix concerns.
  • GitHub Actions with gh api (imperative): scripts calling the API directly. No declarative configuration, no safe-settings integration, no persistent state. Fine for one-shot scripts, poor for governance.
  • Allow creation by org members + safe-settings auto-config: repos would briefly exist ungoverned before safe-settings configured them. The drift window (even if short) is unacceptable: a repo could receive pushes without rulesets, or be public by mistake.

References​