Provision a repo across self-hosted GitLab, GitLab.com, and GitHub with push mirroring.
Project description
๐ Hydra
One source, many mirrors. Provision a single repo on self-hosted GitLab, GitLab.com, and GitHub in one shot โ with push mirroring wired up so every push fans out automatically.
Hydra is a small Python CLI for teams who keep code on a self-hosted GitLab but also need it on GitLab.com and/or GitHub โ for open-source releases, customer access, vendor integrations, or backup. You run one command and Hydra creates the project on all three hosts, then configures GitLab's built-in push mirrors so the self-hosted copy is the only place you ever push.
โโโโโโโโโโโโโโโโโโโโ
โโโโถ โ GitLab.com โ
โ โโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโ push
โ Self-hosted GitLab โ โโโค
โ (source of truth) โ push
โโโโโโโโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโโ
โฒ โโโโถ โ GitHub โ
โ โโโโโโโโโโโโโโโโโโโโ
git push (you)
Requirements
- Python 3.9 or newer
- Permission to create projects/repos on each host you want to use (self-hosted GitLab, GitLab.com, GitHub)
- A personal access token for each host (Hydra tells you exactly which scopes during setup โ see Token scopes)
Install
From PyPI:
pip install hydra-repo-syncer
hydra --version
From source (if you'd rather pin to a checkout, or want to hack on Hydra itself):
git clone <this-repo-url>
cd hydra
python -m venv venv && source venv/bin/activate
pip install -e .
hydra --version
After installing, the hydra command is on your PATH.
Quickstart
# 1. One-time setup โ pick hosts, defaults, and store tokens
hydra configure
# 2. See what *would* happen, without making any API calls
hydra create my-first-repo --dry-run
# 3. Do it for real
hydra create my-first-repo
That's it. The repo now exists on all three hosts, and any future git push to the self-hosted GitLab will mirror automatically to the other two.
Configure (one-time)
hydra configure
A four-step wizard walks you through:
| Step | What you provide |
|---|---|
| 1. Hosts | URLs for self-hosted GitLab, GitLab.com, and GitHub |
| 2. GitHub account | Your GitHub user, or an organisation name |
| 3. Defaults | Default group path; default visibility (private/public) |
| 4. Tokens | API tokens for each host, plus where to store them |
Non-secret settings are saved to ~/.config/hydra/config.yaml. Tokens go to your OS keyring (macOS Keychain, Linux Secret Service) โ never to the YAML.
Token scopes
When you mint personal access tokens, use these scopes:
| Host | Required scope | Mint a token at |
|---|---|---|
| Self-hosted GitLab | api |
<your-host>/-/user_settings/personal_access_tokens |
| GitLab.com | api |
https://gitlab.com/-/user_settings/personal_access_tokens |
| GitHub | repo (plus admin:org if creating under an organisation) |
https://github.com/settings/tokens |
Token resolution order
For each host, Hydra looks up the token in this order and stops at the first hit:
- OS keyring โ set via
hydra configure, or directly:keyring set hydra <github|gitlab|self_hosted_gitlab> - Environment variable โ
HYDRA_GITHUB_TOKEN,HYDRA_GITLAB_TOKEN,HYDRA_SELF_HOSTED_GITLAB_TOKEN .envfile in the current working directory (see.env.example)- Interactive prompt (only if attached to a TTY)
This lets you use the keyring on your laptop and env vars in CI without changing anything else.
Creating repos
Two modes โ interactive wizard (good for one-offs), or flag-driven (good for scripts).
Interactive
hydra create
The wizard collects the repo name, description, group, visibility, GitHub destination, and mirror toggle, shows a review summary, then asks you to create now, dry-run, or cancel.
Flags
# Dry-run โ recommended for the first try, no API calls
hydra create my-repo -d "demo" -g platform/services --dry-run
# Real run, using defaults from config.yaml
hydra create my-repo -d "demo" -g platform/services
# Public repo, under a GitHub org, skip mirror setup
hydra create my-repo --public --github-org acme --no-mirror
Omit the name to launch the wizard; pass a name to stay in flag mode.
| Flag | Meaning |
|---|---|
-d, --description |
Repo description |
-g, --group |
Group path on self-hosted GitLab |
--public |
Create as public (default is private) |
--github-org <name> |
Create under this GitHub org instead of your user |
--no-mirror |
Skip push-mirror setup |
--dry-run |
Print planned actions without making API calls |
--config <path> |
Use a non-default config file |
-v, --verbose |
Print extra detail (group IDs, etc.) |
Inspecting mirrors
hydra status my-repo --group platform/services
Shows the enabled state, last-sync status, and any errors per mirror โ useful when you push something and it doesn't appear on GitLab.com or GitHub.
Commands
| Command | Description |
|---|---|
hydra create [name] |
Create the repo across all three hosts. Without name, runs the wizard. |
hydra configure |
Onboarding wizard โ config + tokens. |
hydra status <name> |
Show the self-hosted project's mirror state. |
hydra config-path |
Print the resolved config-file path. |
Run hydra <cmd> --help for full flags.
Error handling
Hydra translates HTTP failures into actionable messages:
โ GitLab.com authentication failed (401) while searching for group 'platform/services'
The GitLab.com token was rejected. Rotate it at
https://gitlab.com/-/user_settings/personal_access_tokens
and re-run `hydra configure`, or set HYDRA_GITLAB_TOKEN in your environment.
If a failure happens after some resources have been created, the partial state is reported so you can clean up before retrying:
โ Partial progress before the failure:
โข self-hosted GitLab repo: https://gitlab.example.com/sandbox/demo
โข gitlab.com group: https://gitlab.com/repo-syncer-managed-groups/sandbox-20260508131245
These resources exist now. Delete them manually before retrying,
or use a different repo name.
Config file
Lives at ~/.config/hydra/config.yaml by default. Override with --config <path> or the HYDRA_CONFIG environment variable. See config.yaml.example for the full schema:
self_hosted_gitlab:
url: https://gitlab.example.com
gitlab:
url: https://gitlab.com
managed_group_prefix: repo-syncer-managed-groups
github:
url: https://api.github.com
org: null # null = create under your user; or set an org name
defaults:
private: true
group: "" # optional default group path on the self-hosted GitLab
Security notes
- Tokens are never written to the YAML config.
- Tokens injected into mirror URLs (
https://oauth2:<token>@host/...) are stored on the self-hosted GitLab'sremote_mirrorstable. Anyone with project admin access can read them back via the GitLab API โ use scoped tokens. - Keep
.envgitignored. It already is in this repo.
Development
Clone the repo and install with the dev extras:
git clone <this-repo-url>
cd hydra
python -m venv venv && source venv/bin/activate
pip install -e '.[dev]'
pytest
Unit tests cover error translation, slug generation, wizard validators, and credential injection. CI runs the same suite plus a hydra --help smoke test on every push (.gitlab-ci.yml).
License
MIT. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hydra_repo_syncer-0.0.4.tar.gz.
File metadata
- Download URL: hydra_repo_syncer-0.0.4.tar.gz
- Upload date:
- Size: 22.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b60dbb4b843ead039413b78f0d5c79e31ba6ce4e8b625072a39d9c62cc0de538
|
|
| MD5 |
224e70cab9717b5497ac25fa471a87b0
|
|
| BLAKE2b-256 |
add76e42d7248aa1f4e76175c2888882f115195434b932ed0749599e59c83d5f
|
File details
Details for the file hydra_repo_syncer-0.0.4-py3-none-any.whl.
File metadata
- Download URL: hydra_repo_syncer-0.0.4-py3-none-any.whl
- Upload date:
- Size: 19.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b6f44ac7edc92b710ae63a8d71a729413977bbbdb0addd9f4f9a34f8701fa67
|
|
| MD5 |
290513465e5b67c81eb24c1a5c6d1f18
|
|
| BLAKE2b-256 |
d80ffac2731a2e028338eb932adffa9685933c18905d2bd55198647739906ab0
|