+title: lohr

=lohr= is a Git mirroring tool.

I created it to solve a simple problem I had: I host my own git server at [[https://git.alarsyo.net]], but want to mirror my public projects to GitHub / GitLab, for backup and visibility purposes.

GitLab has a mirroring setting, but it doesn't allow for multiple mirrors, as far as I know. I also wanted my instance to be the single source of truth.

** How it works

Gitea is setup to send webhooks to my =lohr= server on every push update. When =lohr= receives a push, it clones the concerned repository, or updates it if already cloned. Then it pushes the update to all remotes listed in the [[file:.lohr][.lohr]] file at the repo root.

* Destructive

This is a very destructive process: anything removed from the single source of truth is effectively removed from any mirror as well.

** Setup

* Quickstart

Setting up =lohr= should be quite simple:

  1. Create a =Rocket.toml= file and [[https://rocket.rs/v0.4/guide/configuration/][add your configuration]].

  2. Export a secret variable:

    +begin_src sh

    $ export LOHR_SECRET=42 # please don't use this secret

    +end_src

  3. Run =lohr=:

    +begin_src sh

    $ cargo run # or cargo run --release for production usage

    +end_src

  4. Configure your favorite git server to send a webhook to =lohr='s address on every push event.

    I used [[https://docs.gitea.io/en-us/webhooks/][Gitea's webhooks format]], but I think they're similar to GitHub and GitLab's webhooks, so these should work too! (If they don't, please file an issue!)

    Don't forget to set the webhook secret to the one you chose above.

  5. Add a =.lohr= file containing the remotes you want to mirror this repo to:

    +begin_example

    git@github.com:you/your_repo

    +end_example

    and push it. That's it! =lohr= is mirroring your repo now.

* Configuration

** Home directory

=lohr= needs a place to clone repos and store its data. By default, it's the current directory, but you can set the =LOHR_HOME= environment variable to customize it.

** Extra remote configuration

=lohr= looks for a =lohr-config.yaml= file in its =LOHR_HOME= directory. This file takes the following format:

+begin_src yaml

default_remotes: - "git@github:user" - "git@gitlab:user"

additional_remotes: - "git@git.sr.ht:~user"

+end_src

Both settings take as input a list of "stems", i.e. incomplete remote addresses, to which the repo's name will be appended (so for example, if my ~defaultremotes~ contains ~git@github.com:alarsyo~, and a push event webhook is received for repository =git@gitlab.com:some/long/path/reponame=, then the mirror destination will be =git@github.com:alarsyo/repo_name=.

** Contributing

I accept patches anywhere! Feel free to [[https://github.com/alarsyo/lohr/pulls][open a GitHub Pull Request]], [[https://gitlab.com/alarsyo/lohr/-/merge_requests][a GitLab Merge Request]], or [[https://lists.sr.ht/~alarsyo/lohr-dev][send me a patch by email]]!

** Why lohr?

I was looking for a cool name, and thought about the Magic Mirror in Snow White. Some [[https://en.wikipedia.org/wiki/Magic_Mirror_(Snow_White)][furious wikipedia searching]] later, I found that the Magic Mirror was probably inspired by [[http://spessartmuseum.de/seiten/schneewittchen_engl.html][the Talking Mirror in Lohr am Main]]. That's it, that's the story.

** License

=lohr= is distributed under the terms of both the MIT license and the Apache License (Version 2.0).

See [[file:LICENSE-APACHE][LICENSE-APACHE]] and [[file:LICENSE-MIT][LICENSE-MIT]] for details.