Skip to content

Getting startedIntroduction

Introduction

joflux moves a GitHub organization's repositories into Forgejo or Codeberg in five stages, each leaving JSON you can read.

joflux moves the repositories of a GitHub organization into a Forgejo-compatible instance such as Codeberg. It exports an inventory of the organization, has Forgejo migrate every repository in it, confirms they arrived, checks what came across, and archives the GitHub originals once you’re happy with the result.

Each of those steps is its own command, and each one writes what it did as JSON in migration_output/. You can read exactly what happened, re-run a stage, and keep the files as the record of the move. The default install needs nothing beyond the Python 3.11+ standard library.

joflux copies each repository once. It sends Forgejo a normal migration rather than a mirror, so the copy doesn’t keep following GitHub afterwards, and changes pushed to GitHub after the migration stay there. It never deletes anything on GitHub either: the last stage archives the originals rather than removing them.

  1. export reads the organization from GitHub and saves the list as repos-inventory.json. Nothing changes on either side, so it’s the safe first step.

  2. migrate reads that inventory and has Forgejo migrate each repository, waiting for each import to finish and logging what happened in migration-log.json.

  3. monitor rechecks any migration Forgejo didn’t answer for in time, and records what it can tell about each in migration-status.json.

  4. verify checks every repository in the inventory on the target and writes its size, its issue, pull request, label and release counts, and whether it has a wiki to verification-report.json.

  5. archive asks you to confirm, then archives the GitHub originals that verify recorded as verified and logs each one in archive-log.json.

Every stage after export works from the files the earlier stages wrote, so you can stop between stages, read the JSON, and carry on when you’re ready. A stage that only partly worked exits with code 1, which lets a script stop at the first one that didn’t go cleanly.