Interactive TUI

github-backup ships a full-screen terminal user interface built with Ratatui 0.30. Pass --tui and the tool enters the TUI instead of running non-interactively.

# Recommended first-run experience
export GITHUB_TOKEN=ghp_your_token_here
github-backup octocat --tui

# Flags pre-fill the Configure screen
github-backup octocat --tui --token "$GITHUB_TOKEN" --output /var/backup/github \
  --all --private --dry-run --org --concurrency 8 --manifest

The command line is resolved exactly as for a normal run, so --all, --private, --forks, --dry-run, --org, --full, --since, --include-repos, --clone-type, --concurrency, --manifest and the individual category flags all arrive in the Configure screen as initial values. Review and adjust them, then start the backup. Not carried over: --config files, --clone-host, and every option listed under What the TUI cannot do.

The TUI needs an interactive terminal on stdout. Without one (a pipe, cron, docker run without -t) it prints a one-line explanation and exits with status 2.


Screen Overview

The title bar lists five screens. Press the number key to switch (not while a backup is running, and not while editing a field). On narrow terminals the title bar shrinks to digits.

 github-backup v0.3.2  [1]Dashboard  [2]Configure  [3]Run  [4]Verify  [5]Results

Terminals smaller than 30x8 show a "Terminal too small" notice instead of a clipped screen.

1 — Dashboard

Owner, output directory, token status (never shown, only "configured") and the last recorded run: time, repository count and result (complete, or INCOMPLETE (N failures)). The last run is read from <output>/<owner>/json/backup_history.json, falling back to backup_state.json, and refreshed whenever you return to the Dashboard.

2 — Configure

Six tabs:

TabContents
AuthGitHub token (always masked), API URL for GitHub Enterprise (https:// only)
TargetOwner, output directory, organisation mode, since date, Full backup (ignore state)
Categories34 backup-category toggles
CloneClone type (mirror/bare/full/shallow), forks, private, LFS, prefer-SSH, prune deleted refs, concurrency (1–64)
Filterinclude / exclude repository globs (comma-separated)
OutputWrite SHA-256 manifest, dry run

Full backup ignores the saved incremental state and re-fetches everything. It cannot be combined with Since, as on the command line. The Output tab ends with a note: Command line only: mirror push, S3 sync, JSON report, Prometheus metrics, webhook notification and device-flow sign-in.

The focused field's help text is shown under the list when there is room. Starting a backup validates the form (owner, token, https:// API URL, date format, concurrency range) and shows what to fix in a dialog.

3 — Run

Live view of an active backup:

  • Progress gauge — repositories finished out of discovered, with a failure count. As soon as one repository fails the title reads INCOMPLETE: N failed and the bar turns red.
  • Repo list — >> running, ok done, !! failed (with the reason), -- skipped. Failed repositories are pinned to the top so they never scroll away.
  • Log panel — one row per entry (long lines are cut, never wrapped), so the newest entry is always visible while following. Only this tool's own INFO and above are shown by default; set RUST_LOG to change that.
  • Counters — repos finished/total, failed, elapsed (stops when the run ends), and key hints.

With no backup running this screen just says so; Esc or q leave it.

4 — Verify

Checks the JSON files listed in backup_manifest.json against the disk. It does not cover git mirrors. The manifest exists only if you ran a backup with Write SHA-256 Manifest (or --manifest); otherwise Verify says so. v starts it.

5 — Results

The verdict follows the engine, never the repo counters alone:

HeadlineMeaning
BACKUP COMPLETEran to the end, nothing failed
BACKUP INCOMPLETE - N failuresran to the end but something failed; not a complete backup
DRY RUN COMPLETE - nothing was writtendry run, no failures
BACKUP CANCELLEDstopped with Ctrl+C
BACKUP FAILEDstopped by a fatal error; the message is shown

Below the headline: counters (repositories discovered / backed up / skipped / failed, gists, issues, pull requests, workflows, discussions) and, when anything failed, a scrollable Failures list with the scope (usually owner/repo), the step (clone, wiki, issues, …) and the message. The selected failure's full text is shown underneath when there is room.


Key Reference

Global

KeyAction
1–5Switch screens (disabled while a backup runs or a field is being edited)
Ctrl+CCancel the running backup; anywhere else, quit
any keyDismiss an error dialog

Dashboard

KeyAction
j / k or ↓ / ↑Select action
EnterRun the selected action
r / c / v / qRun backup / Configure / Verify / Quit

Configure

KeyAction
Tab / Shift+TabNext / previous tab
j / k or ↓ / ↑Next / previous field
SpaceToggle a checkbox
EnterEdit a text field (toggles a checkbox)
← / →Change the Clone type
ACategories tab: select all, or none when all are on
s / F5Start the backup
EscBack to the Dashboard

While editing a text field: Enter saves, Esc discards the change, Backspace deletes. Pasting inserts a single clean line; a paste never triggers shortcuts. The Concurrency field accepts digits only. There is no cursor movement inside a field.

Run

KeyAction
Ctrl+CCancel (stops git and releases the lock; Results then shows CANCELLED)
j / kScroll the repo list
g / GLog: oldest / follow newest
PgUp / PgDnScroll the log
Esc / qLeave the screen (only when no backup is running)

Verify

KeyAction
vStart verification
j / k, PgUp / PgDnScroll
Esc / dDashboard
qQuit

Results

KeyAction
j / k, PgUp / PgDnSelect failure
g / GFirst / last failure
rRun again (starts a new backup immediately)
d / EscDashboard
cConfigure
qQuit

What the TUI cannot do

These exist only on the command line, and the TUI does not offer them:

  • mirror push (Gitea/GitLab), S3 sync, JSON report, Prometheus metrics, webhook notification;
  • device-flow sign-in (a token is required);
  • --config files and --clone-host;
  • the CLI-level output lock.

What the TUI does after a run, like the CLI: the engine writes the incremental state, the TUI appends a run-history entry (shown on the Dashboard, including the failure count) and, if enabled and not a dry run, writes the manifest. A manifest that cannot be written counts as a failure of the run.

Terminal safety

Raw mode, the alternate screen and bracketed paste are always restored on exit, on a panic (the message is printed after the terminal is restored), and on SIGINT, SIGTERM, SIGHUP and SIGQUIT. Such a signal cancels a running backup first (git is killed, the lock released) and exits with 128 + the signal number. A second signal exits immediately. SIGKILL cannot be handled; run reset if you ever use it. Closing the terminal window ends the process.

Architecture Notes

The TUI lives in the github-backup-tui crate and drives the same BackupEngine as the CLI. Per-repository progress and outcome arrive over the engine's event channel; the final verdict and failure list come from BackupStats. A tracing layer forwards log events to the Run screen's log panel. Terminal input is read on a dedicated thread; the async loop redraws only when something changed.

See Architecture for the crate dependency graph.