pCloud was never a hard dependency - the tool writes to any local directory. Renaming makes the tool clearly generic before publishing. - PCLOUD_TARGET → TARGET_DIR - probe_pcloud → probe_target - place_into_pcloud → place_into_target - pcloud_target config key → target_dir - COE_MIRROR_PCLOUD_TARGET env var → COE_MIRROR_TARGET_DIR - Hardcoded default: ~/pCloudDrive/Hacking/ChurchOfMalware → ~/coe-mirror - Log messages and docstrings updated - Tests updated to match DevTrack #1286
4.1 KiB
coe-mirror
Mirror repos from a Forgejo/Gitea instance to a local directory. Built for git.churchofmalware.org but usable against any Forgejo/Gitea host.
What it does
For every owner in your config, it fetches the repo list, downloads a zip of the current HEAD of the default branch, and writes it to <target>/<owner>/<repo>_<sha8>.zip. Old snapshots stay as history — new commits produce new files. Runs unattended via a systemd user timer.
Requirements
- Python 3.11+ (uses stdlib
tomllib) pip install -r requirements.txt
Quick start
git clone <this repo>
cd coe-mirror
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python coe_mirror.py config init # writes ~/.config/coe-mirror/config.toml
$EDITOR ~/.config/coe-mirror/config.toml # edit owners + target dir
python coe_mirror.py run # first mirror
Add or remove owners any time by editing the owners = [...] list in the config file.
Configuration
Precedence (uniform for every key): env var > config file > hardcoded default.
Config file location: ~/.config/coe-mirror/config.toml (override with COE_MIRROR_CONFIG=/path/to/file.toml).
Full schema:
api_base = "https://git.churchofmalware.org"
target_dir = "~/coe-mirror"
owners = ["Nightmare_Eclipse", "shai_hulud"]
# pending_dir = "~/.local/share/coe-mirror/pending" # optional
[matrix] # optional; leave blank to disable notifications
homeserver = ""
room = ""
Env-var overrides (all optional; each beats the config file):
| Var | Purpose |
|---|---|
COE_MIRROR_CONFIG |
Alternate path to config.toml |
COE_MIRROR_API_BASE |
Forgejo/Gitea base URL |
COE_MIRROR_OWNERS |
Comma-separated owners ("a,b,c") |
COE_MIRROR_TARGET_DIR |
Target directory |
COE_MIRROR_PENDING_DIR |
Local staging dir |
COE_MIRROR_MATRIX_HOMESERVER |
Matrix homeserver URL |
COE_MIRROR_MATRIX_ROOM |
Matrix room ID |
COE_MIRROR_MATRIX_USER |
Matrix bot user (fallback if creds binary unavailable) |
COE_MIRROR_MATRIX_PASSWORD |
Matrix bot password (fallback) |
CLI
python coe_mirror.py # run the mirror (default)
python coe_mirror.py run # same
python coe_mirror.py config init # write example config (won't overwrite)
python coe_mirror.py config path # print config file path
python coe_mirror.py --help
Install as a systemd user timer
bash install.sh
Timer defaults to 00:00 and 12:00 daily with boot catch-up (Persistent=true). Missed windows run on next boot.
Matrix notifications (optional)
- Disabled by default. Leave
[matrix]blank to skip. - If both
homeserverandroomare configured, the tool posts a summary after every run that lands at least one new zip (or reports any owner whose fetch failed). - Credential lookup order:
creds get COE_MIRROR_BOT_USER matrix/COE_MIRROR_BOT_PASSWORD matrix(Infisical CLI, if you use it), thenCOE_MIRROR_MATRIX_USER/COE_MIRROR_MATRIX_PASSWORDenv vars. Tokens cached at~/.local/share/coe-mirror/matrix-token.json(mode 0600), auto-refreshed on expiry or 401. - Matrix failures are non-fatal — a warning is logged and the run proceeds.
Filename convention
- Final:
<target>/<owner>/<repo>_<sha8>.zip - Staging (while pending flush):
<pending_dir>/<owner>__<repo>_<sha8>.zip— owner prefix prevents cross-owner collisions when two owners have identically-named repos.
Behavior when the target is unavailable
Downloads land in pending_dir. Next run (or any subsequent invocation) flushes them to the target once it's healthy. Owner-scoped: one owner's fetch failure logs and continues; the next run retries.
Logs
- File:
~/.local/share/coe-mirror/coe-mirror.log(rotated at 5 MB, 3 backups) - Journal:
journalctl --user -u coe-mirror.service -n 100
Storage
Old snapshots accumulate — no automatic pruning. Prune the target directory manually if you need to reclaim space.
Testing
python -m pytest tests/ -v
Uninstall
systemctl --user disable --now coe-mirror.timer
rm ~/.config/systemd/user/coe-mirror.{service,timer}
systemctl --user daemon-reload