- Rust 98.6%
- Nix 0.7%
- Shell 0.7%
| .beads | ||
| .claude | ||
| .serena | ||
| src | ||
| tests | ||
| .crushrc | ||
| .envrc | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| crushrc | ||
| flake.lock | ||
| flake.nix | ||
| migration.toml.example | ||
| plan.md | ||
| README.md | ||
| spec.md | ||
| test-migration.toml | ||
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:
preflightsnapshotplanapplyverifyemit-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.
preflightprobes 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 holdread:user, so the authentication check falls back to reading the configured destination repository and says so in itsdetail.snapshotfetches every configured tracker from SourceForge and writes the raw snapshot artifact. Partial fetch failures are recorded, not fatal.plannormalizes the snapshot under the configured policies and writes the deterministic plan artifact. It performs no network access.applyreconciles 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.verifycompares the destination with plan and manifest, writes the verification report, and reports every discrepancy with a typed kind.emit-sqlrenders reviewedcreated_unixcorrections to--outputfor an operator to run manually; entries that cannot be rendered are listed as explicit-- skippedlines.
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
planandverifynever write to either service.plancontacts no service at all;verifyreads the Forgejo destination and writes only the local verification report.applyis eligible to proceed only when the configuration setsallow_apply = true, the command includes--confirm, and--confirm-target owner/repositoryexactly matches the configured Forgejo destination. The safe example keepsallow_apply = false.- The destination identity is configuration-driven: any
owner/repositoryother than the reserved task repositoryempire/migrateis accepted, so a rehearsal destination such asgefla/emptestcan be exercised. The guards on writes are the scoped Forgejo token, theallow_apply/--confirmgate, the exact--confirm-targetmatch, and the requirement that an enabledapplytargets the canonical Forgejo host or a loopback address. emit-sqlis eligible to proceed only when the configuration setsallow_sql = true, the command includes--confirm, and an--outputpath 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.