SourceForge to Forgejo migration
  • Rust 98.6%
  • Nix 0.7%
  • Shell 0.7%
Find a file
2026-10-11 22:13:24 +02:00
.beads Close completed beads with verification evidence 2026-10-10 00:42:22 +02:00
.claude Define canonical intermediate domain models and normalization error types 2026-08-25 19:59:57 +00:00
.serena Serena tools preference 2026-08-26 00:04:39 +02:00
src Let preflight accept repository-scoped Forgejo tokens (workspace-a40) 2026-10-11 11:31:59 +02:00
tests Let preflight accept repository-scoped Forgejo tokens (workspace-a40) 2026-10-11 11:31:59 +02:00
.crushrc Crush adaptation 2026-08-09 23:26:41 +02:00
.envrc Add flake 2026-08-04 23:43:31 +02:00
.gitignore Move to crush/serena 2026-08-25 22:04:22 +02:00
AGENTS.md Let preflight accept repository-scoped Forgejo tokens (workspace-a40) 2026-10-11 11:31:59 +02:00
Cargo.lock Implement timestamp parsing with policy-aware timezone handling 2026-08-25 20:12:53 +00:00
Cargo.toml Implement timestamp parsing with policy-aware timezone handling 2026-08-25 20:12:53 +00:00
CLAUDE.md Define canonical intermediate domain models and normalization error types 2026-08-25 19:59:57 +00:00
crushrc Move to crush/serena 2026-08-25 22:04:22 +02:00
flake.lock Add flake 2026-08-04 23:43:31 +02:00
flake.nix Implement preflight, apply and verify modes 2026-10-10 00:17:37 +02:00
migration.toml.example Implement preflight, apply and verify modes 2026-10-10 00:17:37 +02:00
plan.md Update plan.md with implementation order 2026-08-05 10:36:26 +02:00
README.md Let preflight accept repository-scoped Forgejo tokens (workspace-a40) 2026-10-11 11:31:59 +02:00
spec.md Initial spec 2026-08-04 21:33:57 +02:00
test-migration.toml Add test-migration config 2026-10-11 22:13:24 +02:00

empire-migrate

Purpose and scope

This repository is the task repository empire/migrate. It packages the empire-migrate command-line tool for the eventual migration of SourceForge empserver tracker records to the Forgejo empire/empserver repository. The task repository and the eventual migration destination are distinct: this checkout is not the empire/empserver destination.

All six modes are implemented. The tool stays fail-closed: every write path needs an explicit configuration opt-in plus a confirmation flag, ambiguous mapping decisions are configuration-driven and never guessed, and no mode ever executes SQL.

Clean-checkout execution

From a clean checkout, first materialize the explicitly supplied configuration file from the safe, non-secret example:

cp migration.toml.example migration.toml
mkdir -p artifacts
nix run . -- --config migration.toml <mode>

The configuration path is required. migration.toml is a local working file; do not put a secret or a token value in it. The example contains only token environment-variable names and leaves write-related safeguards disabled. Every artifact parent directory named under [artifacts] must already exist: the tool writes artifacts but never creates directories, and a missing parent is a fatal error before any network access.

The six modes are:

  • preflight
  • snapshot
  • plan
  • apply
  • verify
  • emit-sql

Every mode prints one deterministic JSON object to stdout. Modes exit 0 on success and 1 on fatal errors; preflight exits 1 when a check fails and verify exits 1 when a discrepancy is found. apply and emit-sql are rejected before any service access when their safety gate is disabled.

  • preflight probes SourceForge read access, Forgejo authentication, repository identity, push permission, and configuration completeness. It is read-only, writes no artifacts, and emits no timestamps. A repository-scoped Forgejo token (read:issue, write:issue, read:repository, write:repository) is sufficient: such a token cannot hold read:user, so the authentication check falls back to reading the configured destination repository and says so in its detail.
  • snapshot fetches every configured tracker from SourceForge and writes the raw snapshot artifact. Partial fetch failures are recorded, not fatal.
  • plan normalizes the snapshot under the configured policies and writes the deterministic plan artifact. It performs no network access.
  • apply reconciles the destination Forgejo repository with the plan (issues, comments, labels, milestones) and persists the manifest and apply report even when the run fails, so a rerun resumes without duplicating anything.
  • verify compares the destination with plan and manifest, writes the verification report, and reports every discrepancy with a typed kind.
  • emit-sql renders reviewed created_unix corrections to --output for an operator to run manually; entries that cannot be rendered are listed as explicit -- skipped lines.

Attachments are reported, not uploaded: snapshot acquisition retains only their hashes, so apply records an explicit no-op for each planned attachment while visible provenance keeps the original URL.

Safety contract

  • plan and verify never write to either service. plan contacts no service at all; verify reads the Forgejo destination and writes only the local verification report.
  • apply is eligible to proceed only when the configuration sets allow_apply = true, the command includes --confirm, and --confirm-target owner/repository exactly matches the configured Forgejo destination. The safe example keeps allow_apply = false.
  • The destination identity is configuration-driven: any owner/repository other than the reserved task repository empire/migrate is accepted, so a rehearsal destination such as gefla/emptest can be exercised. The guards on writes are the scoped Forgejo token, the allow_apply/--confirm gate, the exact --confirm-target match, and the requirement that an enabled apply targets the canonical Forgejo host or a loopback address.
  • emit-sql is eligible to proceed only when the configuration sets allow_sql = true, the command includes --confirm, and an --output path is supplied. The output path is checked with no-clobber rules. This mode writes reviewed instructions only; it never opens, connects to, or executes SQL, and the operator runs the statements by hand.

SourceForge and Forgejo credentials are referenced only by environment-variable names in configuration; token values do not belong in configuration or artifacts. Cargo.lock and the Nix flake lockfile pin the runtime and package dependencies.

Development commands

cargo fmt --check
cargo test --locked
cargo clippy --locked --all-targets --all-features -- -D warnings
nix flake check
nix run . -- --help

The flake's default package is the empire-migrate binary, so the nix run syntax above exercises the same packaged CLI boundary used by the smoke check.