mirror of
https://github.com/Andre-Bzk/github-to-gitea-mirror.git
synced 2026-11-15 09:33:13 +01:00
Without a stable documentation entry point, readers have to reconstruct the mirror, release, and asset flows from code alone, which makes operational review and onboarding harder.
445 lines
19 KiB
Markdown
445 lines
19 KiB
Markdown
# GitHub → Gitea Mirror
|
|
|
|
[English](#english) | [Deutsch](#deutsch)
|
|
|
|
---
|
|
|
|
## English
|
|
|
|
> **Disclaimer:** This project was vibe coded with Claude Code — architecture
|
|
> and structure were planned together with AI, and all implementation was done
|
|
> through prompts. Not a single line was written by hand.
|
|
|
|
Mirrors **all** GitHub repositories of a user into a self-hosted Gitea instance
|
|
as pull mirrors — including repositories created later.
|
|
|
|
Gitea can already mirror a single repository through its web UI. What it cannot
|
|
do is watch an account and pick up new repositories on its own, or continuously
|
|
mirror GitHub release metadata and uploaded assets. A cron job closes both gaps.
|
|
Keeping the repositories' *git content* up to date remains Gitea's job.
|
|
|
|
| Task | Handled by | Frequency |
|
|
| --- | --- | --- |
|
|
| Discover **new** repositories, create mirrors | this script (cron) | daily |
|
|
| Pull new commits into **existing** mirrors | Gitea itself | `MIRROR_INTERVAL` |
|
|
| Synchronize release metadata and uploaded assets | this script (cron) | daily |
|
|
|
|
### Architecture
|
|
|
|
- [Architecture diagram](docs/architecture/sync_mirrors.architecture.html)
|
|
- [Sequence diagram](docs/architecture/sync_mirrors.sequence.html)
|
|
|
|
### Requirements
|
|
|
|
- Gitea 1.20+ with an API token (scopes `write:repository,read:user`)
|
|
- Python 3.7+ — standard library only, no `pip install`, no `jq`
|
|
- `cron`
|
|
|
|
Deliberately dependency-free so it runs on a small box: on a Raspberry Pi 3B
|
|
with 1 GB RAM the sync is invisible in `free -h`.
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
git clone <this-repo> github-to-gitea-mirror
|
|
cd github-to-gitea-mirror
|
|
./install.sh
|
|
```
|
|
|
|
`install.sh` copies the scripts to `~/github-to-gitea-mirror`, creates `mirror.env`
|
|
from the example, fixes permissions and adds the cron entry. It is safe to
|
|
re-run — an existing `mirror.env` is never overwritten.
|
|
|
|
```bash
|
|
TARGET=/opt/gitea-mirror ./install.sh # different directory
|
|
CRON_TIME="0 3" ./install.sh # different schedule
|
|
```
|
|
|
|
Create the Gitea token on the Gitea host:
|
|
|
|
```bash
|
|
docker exec -u git <container> gitea admin user generate-access-token \
|
|
--username <user> --token-name github-mirror-sync \
|
|
--scopes write:repository,read:user
|
|
```
|
|
|
|
Then fill in `mirror.env` and verify:
|
|
|
|
```bash
|
|
cd ~/github-to-gitea-mirror
|
|
DRY_RUN=true python3 sync_mirrors.py # preview, changes nothing
|
|
python3 sync_mirrors.py # run for real
|
|
```
|
|
|
|
### Configuration (`mirror.env`)
|
|
|
|
| Key | Meaning |
|
|
| --- | --- |
|
|
| `GITHUB_USER` | GitHub account to mirror |
|
|
| `GITHUB_TOKEN` | See below. Empty = public repositories only |
|
|
| `GITEA_URL` | Gitea base URL as seen from this machine |
|
|
| `GITEA_TOKEN` | Gitea API token |
|
|
| `GITEA_OWNER` | Gitea user the mirrors belong to |
|
|
| `MIRROR_INTERVAL` | Go duration, e.g. `24h`, `8h`, `30m` |
|
|
| `INCLUDE_FORKS` | Mirror forks too (default `false`) |
|
|
| `INCLUDE_ARCHIVED` | Mirror archived repositories (default `true`) |
|
|
| `SYNC_RELEASES` | Synchronize GitHub releases on every run (default `true`) |
|
|
| `SYNC_RELEASE_ASSETS` | Download uploaded release assets too (default `true`) |
|
|
| `DRY_RUN` | `true` = show what would happen, change nothing |
|
|
|
|
Every key can be overridden by an environment variable of the same name, which
|
|
is what `DRY_RUN=true python3 sync_mirrors.py` uses.
|
|
|
|
### GitHub token
|
|
|
|
Needed only for private repositories. Use a **fine-grained** token
|
|
(<https://github.com/settings/personal-access-tokens>):
|
|
|
|
- Repository access: **All repositories** — required, otherwise repositories
|
|
created later are invisible to the script and auto-discovery is pointless.
|
|
- Permissions: **Contents: Read-only**. Metadata read is added automatically,
|
|
everything else stays on "No access".
|
|
- Expiration: **No expiration** is the sensible choice for an unattended cron
|
|
job — an expiring token stalls the mirror without anyone noticing. The token
|
|
can only read, so an unlimited lifetime costs little here.
|
|
|
|
A classic token with scope `repo` works too, but grants full write and delete
|
|
access to every repository — far more than a read-only mirror needs, for a
|
|
token that sits in plaintext on the server.
|
|
|
|
If you do set an expiry (or your organization enforces one), each run logs the
|
|
remaining validity, warns from 14 days out and exits non-zero once expired:
|
|
|
|
```
|
|
[INFO] GITHUB_TOKEN valid until 2026-12-31 (145 days)
|
|
[WARN] GITHUB_TOKEN expires in 7 day(s), on 2026-08-15 - renew it
|
|
[ERROR] GITHUB_TOKEN expired on 2026-08-01 - renew it
|
|
```
|
|
|
|
### Scope and limits
|
|
|
|
- Gitea mirrors **git data** itself: all branches and tags. The script separately
|
|
synchronizes GitHub release titles, notes, draft/prerelease state and uploaded
|
|
assets on every run. Issues, pull requests and the wiki remain excluded.
|
|
- A release is synchronized only after its git tag has reached Gitea. If the tag
|
|
is still missing, the release is logged and retried by the next cron run.
|
|
- Source archives generated automatically for a tag are not copied because
|
|
Gitea generates its own. Assets explicitly uploaded to GitHub are copied.
|
|
- The script **never deletes** anything in Gitea. If a repository disappears or
|
|
is renamed on GitHub, its mirror stays and is reported as an orphan in the
|
|
log — for a backup that is the right direction. The same applies to releases
|
|
and assets deleted upstream. An asset with the same name but a different size
|
|
is reported, while the existing Gitea copy is retained.
|
|
- GitHub author identities, original release timestamps and download counters
|
|
cannot be reproduced through Gitea's regular release API.
|
|
- A Gitea repository that exists but is **not** a mirror is left untouched, so
|
|
a real working repository can never be overwritten.
|
|
- Mirrors are read-only in Gitea. Push to GitHub, not to the mirror.
|
|
|
|
### Files
|
|
|
|
| File | Purpose |
|
|
| --- | --- |
|
|
| `sync_mirrors.py` | Creates mirrors, aligns intervals and synchronizes releases |
|
|
| `tests/` | Unit tests for repeatable release synchronization |
|
|
| `run_sync.sh` | Cron wrapper: appends to `sync.log`, trims it to 1000 lines |
|
|
| `install.sh` | Installs scripts, config and cron entry |
|
|
| `mirror.env.example` | Configuration template |
|
|
| `mirror.env` | Real config with secrets — git-ignored, `chmod 600` |
|
|
| `sync.log` | Last ~2000 log lines |
|
|
|
|
### Developer flow
|
|
|
|
The control flow is intentionally linear:
|
|
|
|
1. Load `mirror.env`, then let environment variables override individual keys.
|
|
2. Read all GitHub repositories for `GITHUB_USER`.
|
|
3. Apply the fork and archived-repository filters.
|
|
4. For each repository, query Gitea for a repository with the same name.
|
|
5. If it is missing, create a Gitea pull mirror.
|
|
6. If it already exists and is a mirror, align its pull interval.
|
|
7. Synchronize GitHub release metadata and uploaded assets.
|
|
8. Report Gitea mirrors that no longer have a GitHub counterpart.
|
|
|
|
Keep these invariants in mind when changing the code:
|
|
|
|
- Gitea owns the recurring git sync for branches and tags. This script only
|
|
creates missing mirrors, aligns their interval and fills the release gap.
|
|
- A newly created mirror may not have fetched its tags yet. Non-draft releases
|
|
therefore wait until the tag appears in Gitea and are retried by a later run.
|
|
- The script never deletes target-side data. Missing upstream repositories,
|
|
releases or assets are reported and left in Gitea.
|
|
- Asset names are treated as stable backup keys. If an upstream asset with the
|
|
same name has a different size, the existing Gitea asset is kept.
|
|
- Private mirror credentials are stored by Gitea when the mirror is created.
|
|
Rotating `GITHUB_TOKEN` in `mirror.env` does not update existing private
|
|
mirrors.
|
|
- `DRY_RUN=true` should avoid all writes. It may still query both APIs to show
|
|
what would happen.
|
|
|
|
### Tests
|
|
|
|
Run the unit tests from the repository root:
|
|
|
|
```bash
|
|
python3 -m unittest discover -s tests -v
|
|
```
|
|
|
|
### Troubleshooting
|
|
|
|
```bash
|
|
tail -f ~/github-to-gitea-mirror/sync.log
|
|
```
|
|
|
|
| Log line | Cause |
|
|
| --- | --- |
|
|
| `GitHub API 401` | Token invalid or expired |
|
|
| `GitHub API 403` | Rate limit, or token lacks repository access |
|
|
| `migrate failed (HTTP 409)` | Name already taken in Gitea |
|
|
| `... exists in Gitea but is not a mirror` | Real repo with that name — rename one of them |
|
|
| `release ... waits for its tag` | Gitea has not fetched the GitHub tag yet; the next run retries it |
|
|
| `asset ... differs` | An upstream asset changed; the retained Gitea backup is not overwritten |
|
|
| `Missing config value(s)` | `mirror.env` not filled in |
|
|
| `no private repositories` despite a token | Token's repository access is "Public repositories", or no repository permission is set at all |
|
|
|
|
**Fine-grained token shows no private repositories.** Repository access and
|
|
permissions are two separate settings and both are needed: access decides
|
|
*which* repositories the token covers, permissions decide *what* it may do with
|
|
them. With "All repositories" but an empty permission list the token can see
|
|
nothing, and private repositories do not even appear in the listing. Set
|
|
**Contents: Read-only**; Metadata read is added automatically.
|
|
|
|
**After rotating the GitHub token**, updating `mirror.env` is not enough. Gitea
|
|
stores the credentials inside each private mirror's remote URL when the mirror
|
|
is created, so existing private mirrors keep using the old token and start
|
|
failing silently. Either update *Settings → Mirror Settings* per repository in
|
|
Gitea, or delete the private mirrors and let the next run recreate them. Public
|
|
mirrors are unaffected — they clone without credentials.
|
|
|
|
---
|
|
|
|
## Deutsch
|
|
|
|
> **Hinweis:** Dieses Projekt wurde mit Claude Code vibe coded — Architektur und
|
|
> Struktur wurden gemeinsam mit KI geplant, die gesamte Implementierung erfolgte
|
|
> per Prompt. Keine einzige Zeile wurde von Hand geschrieben.
|
|
|
|
Spiegelt **alle** GitHub-Repositories eines Benutzers als Pull-Mirror in eine
|
|
selbst gehostete Gitea-Instanz — auch Repositories, die erst später entstehen.
|
|
|
|
Ein einzelnes Repository kann Gitea bereits über die Weboberfläche spiegeln.
|
|
Was Gitea nicht kann: einen Account beobachten und neue Repositories von selbst
|
|
aufnehmen oder GitHub-Release-Metadaten und hochgeladene Assets fortlaufend
|
|
spiegeln. Ein Cron-Job schließt beide Lücken. Die *Git-Inhalte* aktuell zu halten
|
|
bleibt Giteas Aufgabe.
|
|
|
|
| Aufgabe | Erledigt von | Häufigkeit |
|
|
| --- | --- | --- |
|
|
| **Neue** Repositories finden, Mirror anlegen | dieses Skript (Cron) | täglich |
|
|
| Commits in **bestehende** Mirrors holen | Gitea selbst | `MIRROR_INTERVAL` |
|
|
| Release-Metadaten und hochgeladene Assets synchronisieren | dieses Skript (Cron) | täglich |
|
|
|
|
### Architektur
|
|
|
|
- [Architecture-Diagramm](docs/architecture/sync_mirrors.architecture.html)
|
|
- [Sequence-Diagramm](docs/architecture/sync_mirrors.sequence.html)
|
|
|
|
### Voraussetzungen
|
|
|
|
- Gitea 1.20+ mit API-Token (Scopes `write:repository,read:user`)
|
|
- Python 3.7+ — nur Standardbibliothek, kein `pip install`, kein `jq`
|
|
- `cron`
|
|
|
|
Bewusst ohne Abhängigkeiten, damit es auf kleiner Hardware läuft: Auf einem
|
|
Raspberry Pi 3B mit 1 GB RAM ist der Sync in `free -h` nicht zu sehen.
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
git clone <dieses-repo> github-to-gitea-mirror
|
|
cd github-to-gitea-mirror
|
|
./install.sh
|
|
```
|
|
|
|
`install.sh` kopiert die Skripte nach `~/github-to-gitea-mirror`, legt `mirror.env`
|
|
aus der Vorlage an, setzt die Rechte und trägt den Cron-Job ein. Mehrfaches
|
|
Ausführen ist unbedenklich — eine vorhandene `mirror.env` wird nie überschrieben.
|
|
|
|
```bash
|
|
TARGET=/opt/gitea-mirror ./install.sh # anderes Verzeichnis
|
|
CRON_TIME="0 3" ./install.sh # andere Uhrzeit
|
|
```
|
|
|
|
Den Gitea-Token auf dem Gitea-Host erzeugen:
|
|
|
|
```bash
|
|
docker exec -u git <container> gitea admin user generate-access-token \
|
|
--username <user> --token-name github-mirror-sync \
|
|
--scopes write:repository,read:user
|
|
```
|
|
|
|
Danach `mirror.env` ausfüllen und prüfen:
|
|
|
|
```bash
|
|
cd ~/github-to-gitea-mirror
|
|
DRY_RUN=true python3 sync_mirrors.py # Vorschau, ändert nichts
|
|
python3 sync_mirrors.py # echter Lauf
|
|
```
|
|
|
|
### Konfiguration (`mirror.env`)
|
|
|
|
| Schlüssel | Bedeutung |
|
|
| --- | --- |
|
|
| `GITHUB_USER` | Zu spiegelnder GitHub-Account |
|
|
| `GITHUB_TOKEN` | Siehe unten. Leer = nur öffentliche Repositories |
|
|
| `GITEA_URL` | Gitea-Basis-URL, von dieser Maschine aus gesehen |
|
|
| `GITEA_TOKEN` | Gitea-API-Token |
|
|
| `GITEA_OWNER` | Gitea-Benutzer, dem die Mirrors gehören |
|
|
| `MIRROR_INTERVAL` | Go-Duration, z. B. `24h`, `8h`, `30m` |
|
|
| `INCLUDE_FORKS` | Forks mitspiegeln (Standard `false`) |
|
|
| `INCLUDE_ARCHIVED` | Archivierte Repositories spiegeln (Standard `true`) |
|
|
| `SYNC_RELEASES` | GitHub-Releases bei jedem Lauf synchronisieren (Standard `true`) |
|
|
| `SYNC_RELEASE_ASSETS` | Auch hochgeladene Release-Assets laden (Standard `true`) |
|
|
| `DRY_RUN` | `true` = nur anzeigen, nichts ändern |
|
|
|
|
Jeder Schlüssel lässt sich durch eine gleichnamige Umgebungsvariable
|
|
überschreiben — genau das nutzt `DRY_RUN=true python3 sync_mirrors.py`.
|
|
|
|
### GitHub-Token
|
|
|
|
Nur für private Repositories nötig. Empfohlen ist ein **fine-grained** Token
|
|
(<https://github.com/settings/personal-access-tokens>):
|
|
|
|
- Repository access: **All repositories** — zwingend, sonst sind später
|
|
angelegte Repositories für das Skript unsichtbar und die automatische
|
|
Erkennung wäre sinnlos.
|
|
- Permissions: **Contents: Read-only**. Metadata-Lesen kommt automatisch dazu,
|
|
alles andere bleibt auf „No access".
|
|
- Expiration: **No expiration** ist für einen unbeaufsichtigten Cron-Job die
|
|
sinnvolle Wahl — ein ablaufender Token legt den Spiegel still, ohne dass es
|
|
jemand merkt. Der Token kann ohnehin nur lesen, unbegrenzte Laufzeit kostet
|
|
hier also wenig.
|
|
|
|
Ein Classic-Token mit Scope `repo` funktioniert ebenfalls, gibt aber Vollzugriff
|
|
zum Schreiben und Löschen auf jedes Repository — deutlich mehr, als ein
|
|
Lese-Spiegel braucht, für einen Token, der im Klartext auf dem Server liegt.
|
|
|
|
Wird doch ein Ablaufdatum gesetzt (oder von der Organisation erzwungen),
|
|
protokolliert jeder Lauf die Restlaufzeit, warnt ab 14 Tagen und endet nach
|
|
Ablauf mit Exit-Code 1:
|
|
|
|
```
|
|
[INFO] GITHUB_TOKEN valid until 2026-12-31 (145 days)
|
|
[WARN] GITHUB_TOKEN expires in 7 day(s), on 2026-08-15 - renew it
|
|
[ERROR] GITHUB_TOKEN expired on 2026-08-01 - renew it
|
|
```
|
|
|
|
### Umfang und Grenzen
|
|
|
|
- Gitea selbst spiegelt die **Git-Daten**: alle Branches und Tags. Das Skript
|
|
synchronisiert zusätzlich bei jedem Lauf GitHub-Release-Titel, Beschreibung,
|
|
Draft-/Prerelease-Status und hochgeladene Assets. Issues, Pull Requests und
|
|
Wiki bleiben außen vor.
|
|
- Ein Release wird erst synchronisiert, wenn sein Git-Tag in Gitea angekommen
|
|
ist. Fehlt der Tag noch, wird das protokolliert und beim nächsten Cron-Lauf
|
|
erneut versucht.
|
|
- Automatisch für Tags erzeugte Quellcodearchive werden nicht kopiert, weil
|
|
Gitea eigene erzeugt. Explizit auf GitHub hochgeladene Assets werden kopiert.
|
|
- Das Skript **löscht nie** etwas in Gitea. Verschwindet ein Repository auf
|
|
GitHub oder wird umbenannt, bleibt der Mirror bestehen und wird im Log als
|
|
verwaist gemeldet — für ein Backup ist das die richtige Richtung. Gleiches
|
|
gilt für upstream gelöschte Releases und Assets. Hat ein gleichnamiges Asset
|
|
eine andere Größe, wird dies gemeldet und die Gitea-Kopie behalten.
|
|
- GitHub-Autoren, ursprüngliche Release-Zeitpunkte und Downloadzähler können
|
|
über die reguläre Gitea-Release-API nicht identisch übernommen werden.
|
|
- Ein Gitea-Repository, das existiert, aber **kein** Mirror ist, wird nicht
|
|
angefasst; ein echtes Arbeits-Repository kann so nie überschrieben werden.
|
|
- Mirrors sind in Gitea schreibgeschützt. Gepusht wird nach GitHub, nicht in
|
|
den Spiegel.
|
|
|
|
### Dateien
|
|
|
|
| Datei | Zweck |
|
|
| --- | --- |
|
|
| `sync_mirrors.py` | Legt Mirrors an, gleicht Intervalle ab und synchronisiert Releases |
|
|
| `tests/` | Unit-Tests für die wiederholbare Release-Synchronisierung |
|
|
| `run_sync.sh` | Cron-Wrapper: schreibt nach `sync.log`, kürzt es auf 1000 Zeilen |
|
|
| `install.sh` | Installiert Skripte, Konfiguration und Cron-Eintrag |
|
|
| `mirror.env.example` | Konfigurationsvorlage |
|
|
| `mirror.env` | Echte Konfiguration mit Secrets — git-ignoriert, `chmod 600` |
|
|
| `sync.log` | Die letzten ~2000 Log-Zeilen |
|
|
|
|
### Entwickler-Ablauf
|
|
|
|
Der Kontrollfluss ist bewusst linear:
|
|
|
|
1. `mirror.env` laden, danach einzelne Schlüssel durch Umgebungsvariablen
|
|
überschreiben lassen.
|
|
2. Alle GitHub-Repositories für `GITHUB_USER` lesen.
|
|
3. Fork- und Archiv-Filter anwenden.
|
|
4. Für jedes Repository in Gitea nach einem Repository mit gleichem Namen
|
|
suchen.
|
|
5. Fehlt es, einen Gitea-Pull-Mirror anlegen.
|
|
6. Existiert es bereits und ist ein Mirror, das Pull-Intervall abgleichen.
|
|
7. GitHub-Release-Metadaten und hochgeladene Assets synchronisieren.
|
|
8. Gitea-Mirrors melden, die kein GitHub-Gegenstück mehr haben.
|
|
|
|
Diese Invarianten sind bei Codeänderungen wichtig:
|
|
|
|
- Gitea besitzt den wiederkehrenden Git-Sync für Branches und Tags. Dieses
|
|
Skript legt nur fehlende Mirrors an, gleicht deren Intervall ab und schließt
|
|
die Release-Lücke.
|
|
- Ein neu angelegter Mirror hat seine Tags möglicherweise noch nicht geholt.
|
|
Nicht-Draft-Releases warten deshalb, bis der Tag in Gitea sichtbar ist, und
|
|
werden bei einem späteren Lauf erneut versucht.
|
|
- Das Skript löscht nie Daten auf der Zielseite. Fehlende Upstream-Repositories,
|
|
Releases oder Assets werden gemeldet und in Gitea behalten.
|
|
- Asset-Namen gelten als stabile Backup-Schlüssel. Hat ein Upstream-Asset mit
|
|
gleichem Namen eine andere Größe, bleibt das vorhandene Gitea-Asset erhalten.
|
|
- Zugangsdaten für private Mirrors werden beim Anlegen von Gitea gespeichert.
|
|
Ein Wechsel von `GITHUB_TOKEN` in `mirror.env` aktualisiert bestehende private
|
|
Mirrors nicht.
|
|
- `DRY_RUN=true` darf keine Schreibzugriffe ausführen. API-Abfragen sind erlaubt,
|
|
damit sichtbar wird, was passieren würde.
|
|
|
|
### Tests
|
|
|
|
Die Unit-Tests im Wurzelverzeichnis des Repositorys starten:
|
|
|
|
```bash
|
|
python3 -m unittest discover -s tests -v
|
|
```
|
|
|
|
### Fehlersuche
|
|
|
|
```bash
|
|
tail -f ~/github-to-gitea-mirror/sync.log
|
|
```
|
|
|
|
| Log-Zeile | Ursache |
|
|
| --- | --- |
|
|
| `GitHub API 401` | Token ungültig oder abgelaufen |
|
|
| `GitHub API 403` | Rate Limit, oder Token hat keinen Repository-Zugriff |
|
|
| `migrate failed (HTTP 409)` | Name in Gitea bereits vergeben |
|
|
| `... exists in Gitea but is not a mirror` | Echtes Repo gleichen Namens — eines umbenennen |
|
|
| `release ... waits for its tag` | Gitea hat den GitHub-Tag noch nicht geholt; der nächste Lauf versucht es erneut |
|
|
| `asset ... differs` | Ein Upstream-Asset wurde geändert; das Gitea-Backup wird nicht überschrieben |
|
|
| `Missing config value(s)` | `mirror.env` nicht ausgefüllt |
|
|
| `no private repositories` trotz Token | Repository access steht auf „Public repositories", oder es ist gar keine Repository-Permission gesetzt |
|
|
|
|
**Fine-grained Token zeigt keine privaten Repos.** Repository access und
|
|
Permissions sind zwei getrennte Einstellungen, beide werden gebraucht: Der
|
|
Access bestimmt, *welche* Repositories der Token betrifft, die Permissions,
|
|
*was* er damit darf. Mit „All repositories", aber leerer Permission-Liste sieht
|
|
der Token nichts — private Repositories erscheinen dann nicht einmal in der
|
|
Auflistung. **Contents: Read-only** setzen, Metadata-Lesen kommt automatisch
|
|
dazu.
|
|
|
|
**Nach dem Wechsel des GitHub-Tokens** genügt es nicht, `mirror.env`
|
|
anzupassen. Gitea hinterlegt die Zugangsdaten beim Anlegen eines privaten
|
|
Mirrors fest in dessen Remote-URL — bestehende private Mirrors arbeiten also
|
|
weiter mit dem alten Token und scheitern still. Entweder pro Repository unter
|
|
*Einstellungen → Mirror-Einstellungen* in Gitea nachziehen, oder die privaten
|
|
Mirrors löschen und vom nächsten Lauf neu anlegen lassen. Öffentliche Mirrors
|
|
sind nicht betroffen, sie klonen ohne Zugangsdaten.
|