Docs: Dokumentiert SmartMount 2.0.0 in der README
Beschreibt die neue Architektur (Laufwerkspaare, Mount-Backends, verschlüsselte Zugangsdaten, mac2ip-Integration), Voraussetzungen, Setup-Ablauf (setup fstab, service install) sowie die vollständige CLI-Referenz. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FZ3VzCWgbQRMyFEEKPvnZz
This commit is contained in:
@@ -1,14 +1,311 @@
|
||||
# SmartMount
|
||||
|
||||
SmartMount ist ein innovatives Tool zur intelligenten Verwaltung von Netzwerk-Dateisystemen. Es ermöglicht das
|
||||
automatische Einbinden von Netzwerk-Freigaben über das lokale Netzwerk und wechselt nahtlos zu einer Cloud-basierten
|
||||
Lösung, falls keine lokale Verbindung verfügbar ist. Durch diese hybride Architektur wird ein zuverlässiger Zugriff auf
|
||||
wichtige Daten sichergestellt - egal ob zu Hause oder unterwegs.
|
||||
SmartMount bindet Paare aus einem **lokalen** (LAN, WebDAV/SMB/NFS) und einem **Cloud**-Laufwerk
|
||||
ein: ist das lokale Laufwerk erreichbar, wird es gemountet - sonst automatisch das
|
||||
Cloud-Laufwerk. Ein Watchdog prüft periodisch die Erreichbarkeit und schaltet bei Bedarf
|
||||
zwischen beiden um. Zugangsdaten werden verschlüsselt in einer lokalen [Turso](https://github.com/tursodatabase/turso)-Datenbank
|
||||
gespeichert, niemals im Klartext in der Konfigurationsdatei. Das Command heißt `smart-mount`.
|
||||
|
||||
### Build:
|
||||
Die Synchronisation der Inhalte zwischen lokalem und Cloud-Laufwerk ist **nicht** Teil dieses
|
||||
Programms - SmartMount setzt voraus, dass beide Seiten bereits inhaltlich synchron gehalten
|
||||
werden (z. B. über ein separates Sync-Tool).
|
||||
|
||||
Für Debian muss `cargo-deb` installiert sein, dann kann man das Paket mit diesem Paket builden:
|
||||
---
|
||||
|
||||
```shell
|
||||
cargo deb --separate-debug-symbols --compress-debug-symbols
|
||||
## Voraussetzungen
|
||||
|
||||
- **[`mac2ip`](https://gitea.creative-dragonslayer.de/Linuxapps/Mac2Ip)** und **`nmap`**: hart
|
||||
benötigt, sofern lokale Laufwerke per MAC-Adresse adressiert werden (Auflösung MAC → IP).
|
||||
- **`timeout`** (GNU coreutils): auf praktisch jedem Linux-System bereits vorhanden (Basis-
|
||||
Systempaket). Wird verwendet, um `mount`/`umount`-Aufrufe zeitlich zu begrenzen - siehe
|
||||
"Verhalten bei nicht mehr erreichbarem Server" unten.
|
||||
- **Pro genutztem Mount-Typ** (nur was tatsächlich konfiguriert ist, wird zur Laufzeit
|
||||
geprüft - siehe `MountBackend::check_available`):
|
||||
- `davfs2` für WebDAV-Laufwerke
|
||||
- `cifs-utils` für SMB/CIFS-Laufwerke
|
||||
- `nfs-common` (Debian/Ubuntu) bzw. `nfs-utils` (Fedora/Arch) für NFS-Laufwerke
|
||||
- Für MAC-basierte lokale Laufwerke im **Nutzerkontext**: `mac2ip` löst über `ip neigh` auf,
|
||||
ohne dafür Root-Rechte zu benötigen - nur der letzte Fallback-Schritt (ein aktiver
|
||||
`nmap`-Scan, falls das Zielgerät nicht in der ARP-Nachbartabelle steht) braucht
|
||||
passwortlosen `sudo`-Zugriff auf `nmap`. Ohne das schlägt die Auflösung in diesem Fall sauber
|
||||
fehl (kein Absturz) - entweder passwortlosen `sudo` für `nmap` einrichten, oder das
|
||||
entsprechende Laufwerkspaar im System-/root-Kontext betreiben.
|
||||
|
||||
---
|
||||
|
||||
## CLI-Nutzung
|
||||
|
||||
```bash
|
||||
# Neues Laufwerkspaar interaktiv anlegen (fragt Name, Kontext, Mount-Typ, Adresse,
|
||||
# Freigabe und ggf. Zugangsdaten ab)
|
||||
smart-mount drive add
|
||||
|
||||
# Nicht-interaktiv per Flags (für Skripte/Automatisierung) - fehlende Pflichtfelder sind
|
||||
# dann ein Fehler statt eines (ohne Terminal ohnehin unmöglichen) Prompts. Jedes
|
||||
# *-password-stdin-Flag liest genau eine Zeile von stdin (bei mehreren im selben Aufruf
|
||||
# entsprechend mehrere Zeilen, eine pro Flag in Reihenfolge):
|
||||
smart-mount drive add --non-interactive \
|
||||
--name NAS --context system \
|
||||
--local-kind smb --local-ip 192.168.1.50 --local-share share --local-username nasuser --local-password-stdin \
|
||||
--cloud-kind webdav --cloud-host https://cloud.example.com/dav --cloud-share / --cloud-username clouduser --cloud-password cloud-pw-direkt \
|
||||
<<< "lokales-passwort"
|
||||
# (--local-password/--cloud-password gehen auch direkt als Argument, sind aber im
|
||||
# Prozess-Listing/Shell-Verlauf sichtbar - für Skripte lieber *-stdin verwenden)
|
||||
|
||||
# Bestehendes Laufwerkspaar bearbeiten - nur angegebene Felder ändern sich, alles andere
|
||||
# (inkl. gespeichertem Passwort) bleibt unangetastet. Ohne Flags: interaktiver Durchlauf,
|
||||
# vorbelegt mit den aktuellen Werten (inkl. Rückfrage, ob das Passwort geändert werden soll).
|
||||
smart-mount drive edit <id> --name "Neuer Name" --local-ip 192.168.1.99
|
||||
smart-mount drive edit <id> --non-interactive --local-password-stdin <<< "neues-passwort"
|
||||
|
||||
# Konfigurierte Laufwerkspaare auflisten / entfernen (--json für Skripte)
|
||||
smart-mount drive list
|
||||
smart-mount drive list --json
|
||||
smart-mount drive remove <id>
|
||||
|
||||
# Ein einzelnes Paar oder alle einbinden/aushängen
|
||||
smart-mount mount --name <id>
|
||||
smart-mount mount --all
|
||||
smart-mount unmount --all
|
||||
|
||||
# Aktuellen Status (Mount-Zustand + Erreichbarkeit beider Seiten) anzeigen (--json für Skripte)
|
||||
smart-mount status
|
||||
smart-mount status --json
|
||||
|
||||
# Ein Reconcile-Durchlauf (lokal/Cloud-Umschaltung) - für systemd-Timer/Cron gedacht
|
||||
smart-mount watch
|
||||
|
||||
# systemd-Units installieren (System- bzw. Nutzerkontext)
|
||||
sudo smart-mount service install --system
|
||||
smart-mount service install --user
|
||||
|
||||
# Einmaliges root-Setup, damit Nutzer-Kontext-Paare unprivilegiert (un)gemountet werden können
|
||||
sudo smart-mount setup fstab
|
||||
|
||||
# Voraussetzungen prüfen (Binaries, Gruppenmitgliedschaft, fstab-Setup, Scheduler) - deckt
|
||||
# gebündelt ab, was man sonst erst einzeln beim Mount-Fehlschlag entdecken würde
|
||||
smart-mount doctor
|
||||
smart-mount doctor --json
|
||||
|
||||
# Shell-Completion-Skript ausgeben (bash/zsh/fish/elvish/powershell)
|
||||
smart-mount completions bash > /etc/bash_completion.d/smart-mount
|
||||
smart-mount completions zsh > "${fpath[1]}/_smart-mount"
|
||||
```
|
||||
|
||||
Die Konfiguration liegt unter `~/.config/smart-mount/config.toml` (Nutzerkontext) bzw.
|
||||
`/etc/smart-mount/config.toml` (root/System-Kontext) - automatisch aufgelöst je nachdem, ob
|
||||
`smart-mount` mit Root-Rechten läuft. Die verschlüsselte Zugangsdaten-Datenbank
|
||||
(`smart-mount.db`) liegt im selben Verzeichnis.
|
||||
|
||||
### Wo die Laufwerke eingebunden werden
|
||||
|
||||
Jedes Laufwerkspaar bekommt sein **eigenes** Unterverzeichnis unter `settings.mount_base_dir`
|
||||
(`<mount_base_dir>/<pair-id>`) - mehrere Paare stören sich also nie gegenseitig. Standardwert
|
||||
für `mount_base_dir`: `/run/media/smart-mount` im System-Kontext (root, ein gemeinsamer,
|
||||
keinem Nutzer zugeordneter Namensraum), `/run/media/<Nutzer>/smart-mount` im Nutzerkontext -
|
||||
`/run/media` ist auf den meisten Systemen bereits die übliche Konvention für eingebundene
|
||||
Wechseldatenträger/Netzlaufwerke (z. B. udisks2/GNOME) und liegt auf `tmpfs`, muss also nie
|
||||
persistieren. Der Mountpoint selbst (und alle nötigen Elternverzeichnisse) werden bei jedem
|
||||
`mount`/`watch`-Lauf automatisch angelegt, falls sie fehlen - eigenes Anlegen ist nicht nötig.
|
||||
|
||||
**Achtung bei Nutzer-Kontext-Paaren:** `/run/media` gehört standardmäßig `root:root` mit Modus
|
||||
`0755` - ein normaler Nutzer kann dort also nicht einmal sein eigenes Unterverzeichnis
|
||||
anlegen. `sudo smart-mount setup fstab` übernimmt das einmalig (legt `/run/media/<Nutzer>`
|
||||
sowie `/run/media/<Nutzer>/smart-mount` an und macht den Nutzer zum Besitzer) - ohne diesen
|
||||
Schritt schlägt das automatische Anlegen für Nutzer-Kontext-Paare fehl.
|
||||
|
||||
Der Standard lässt sich in `config.toml` unter `[settings] mount_base_dir = "..."` jederzeit
|
||||
auf einen beliebigen anderen Pfad ändern.
|
||||
|
||||
---
|
||||
|
||||
## Automatischer Start beim Systemstart
|
||||
|
||||
```bash
|
||||
sudo smart-mount setup fstab # einmalig, nur nötig bei Nutzer-Kontext-Paaren
|
||||
sudo smart-mount service install --system # für System-Kontext-Paare
|
||||
smart-mount service install --user # für die eigenen Nutzer-Kontext-Paare
|
||||
```
|
||||
|
||||
`service install` wählt automatisch den passenden Mechanismus: ist `systemctl` vorhanden,
|
||||
werden systemd-Units installiert (ein `oneshot`-Service für den initialen Mount beim Boot
|
||||
sowie ein Timer, der periodisch `smart-mount watch` aufruft - Intervall:
|
||||
`settings.watch_interval_secs`, Standard 120s). Ist kein systemd vorhanden, wird automatisch
|
||||
auf Cron ausgewichen - als root wird `/etc/cron.d/smart-mount` geschrieben, als normaler
|
||||
Nutzer die eigene, persönliche Crontab über `crontab -l`/`crontab -` aktualisiert (ein
|
||||
verwalteter Block lässt dabei bereits vorhandene, unabhängige Cron-Einträge unangetastet und
|
||||
verhindert Duplikate bei wiederholten Aufrufen). Ist weder systemd noch Cron vorhanden, werden
|
||||
stattdessen die beiden äquivalenten Zeilen zum manuellen Eintragen ausgegeben:
|
||||
|
||||
```cron
|
||||
@reboot smart-mount mount --all
|
||||
*/2 * * * * smart-mount watch
|
||||
```
|
||||
|
||||
`smart-mount service crontab` erzwingt gezielt den Cron-Weg (z. B. um systemd bewusst zu
|
||||
umgehen), mit identischem Verhalten wie der automatische Fallback von `install`.
|
||||
|
||||
```bash
|
||||
sudo smart-mount service uninstall --system
|
||||
smart-mount service uninstall --user
|
||||
```
|
||||
|
||||
räumt alles wieder auf, was `install`/`crontab`/`setup fstab` eingerichtet haben - systemd-
|
||||
Units (falls vorhanden), den Cron-Eintrag (falls vorhanden) und - nur bei `--system`, da
|
||||
`setup fstab` root-weit für alle Nutzer-Kontext-Paare gilt - den verwalteten `/etc/fstab`-
|
||||
Block. Jeder Teil wird unabhängig geprüft: fehlt etwas (z. B. weil nur Cron statt systemd
|
||||
installiert war), wird das ohne Fehler übersprungen; die Ausgabe listet, was tatsächlich
|
||||
entfernt wurde. Backing-Verzeichnisse, gemountete Daten und Gruppenmitgliedschaften (z. B. in
|
||||
der `davfs2`-Gruppe) werden dabei bewusst **nicht** angerührt - dafür gibt es keine
|
||||
automatische Umkehrung, da das ungewollte Nebenwirkungen haben könnte (siehe
|
||||
Architekturentscheidungen unten).
|
||||
|
||||
---
|
||||
|
||||
## Architekturentscheidungen
|
||||
|
||||
- **Verhalten bei nicht mehr erreichbarem Server (der ganze Sinn dieses Tools)**: Netzwerk-
|
||||
Dateisysteme können unter Linux "einfrieren", wenn der Server verschwindet - `umount`
|
||||
oder Schreibzugriffe hängen dann scheinbar unbegrenzt. Das wird gezielt adressiert:
|
||||
- **NFS**: Standardmäßig setzt `mount.nfs` `hard` (unbegrenzte Wiederholungsversuche bei
|
||||
Zeitüberschreitung) - genau das Einfrierverhalten. SmartMount setzt automatisch `soft`,
|
||||
sofern nicht bereits `hard`/`soft`/`softerr` explizit in `extra_options` gesetzt ist
|
||||
(`man 5 nfs`, Abschnitt "soft / softerr / hard" nennt einen dauerhaft nicht erreichbaren
|
||||
Server explizit als Zielszenario für `soft`).
|
||||
- **CIFS**: `mount.cifs` verwendet laut eigener Dokumentation bereits standardmäßig `soft` -
|
||||
kein Eingriff nötig.
|
||||
- **WebDAV (davfs2)**: `umount` ist laut davfs2-eigener FAQ *absichtlich* so lange
|
||||
blockierend, bis alle zwischengespeicherten Daten geschrieben sind - bei einem dauerhaft
|
||||
unerreichbaren Server kehrt das nie zurück. Dafür gibt es keine passende Mount-Option.
|
||||
`umount -l` (lazy) würde zwar sofort zurückkehren, macht laut `man umount` aber
|
||||
**künftige Remounts derselben Freigabe bis zum nächsten Neustart unmöglich** - für ein
|
||||
Tool, dessen ganzer Zweck automatisches Zurückschalten ist, wäre das schlimmer als das
|
||||
ursprüngliche Problem.
|
||||
- **Der eigentliche Fix**: jeder `mount`/`umount`-Aufruf läuft über das coreutils-Tool
|
||||
`timeout` (30s). Bei `umount` wird ein Abbruch wegen Zeitüberschreitung toleriert (nur
|
||||
geloggt, `Ok`) statt als Fehler behandelt - der nächste `watch`-Durchlauf versucht es
|
||||
erneut (idempotent). Dadurch kann `smart-mount watch`/`unmount` selbst dann nicht mehr
|
||||
unbegrenzt hängen bleiben, wenn eine einzelne Seite dauerhaft nicht antwortet - und weil
|
||||
beim Umschalten zuerst die neue Seite gemountet und der Symlink umgebogen wird, bevor die
|
||||
alte Seite (mit eben diesem tolerierten Timeout) ausgehängt wird, blockiert ein
|
||||
hängender Alt-Mount den sichtbaren Wechsel ohnehin nicht.
|
||||
- **Privilegienmodell für Nutzer-Mounts**: `sudo smart-mount setup fstab` schreibt einmalig
|
||||
`/etc/fstab`-Einträge mit `user,exec,noauto` sowie nötige Gruppenmitgliedschaften (z. B.
|
||||
`davfs2`-Gruppe). Jede Seite (lokal/Cloud) bekommt dabei ihr **eigenes, eindeutiges**
|
||||
verstecktes Backing-Verzeichnis (nicht denselben Mountpoint für beide) - das entspricht
|
||||
exakt dem einzigen in `man 8 mount` ("Non-superuser mounts") dokumentierten Fall, statt sich
|
||||
auf unspezifiziertes Verhalten bei zwei Zeilen mit demselben Ziel zu verlassen. Der
|
||||
konfigurierte, sichtbare Mountpoint selbst ist ein Symlink, den SmartMount zur Laufzeit
|
||||
zwischen den beiden Backing-Verzeichnissen umschaltet. `exec` wird explizit gesetzt, weil die
|
||||
`user`-Option laut `man 8 mount` sonst für jedes Dateisystem automatisch `noexec` erzwingt -
|
||||
ohne das könnten auf einem Nutzer-Kontext-Laufwerk liegende Skripte nicht ausgeführt werden.
|
||||
- **Voller Zugriff für einen bestimmten Nutzer (`owner_user`)**: Bei CIFS/WebDAV (keine
|
||||
nativen Unix-Rechte) setzt SmartMount automatisch `uid=`/`gid=`/`file_mode=0700`/
|
||||
`dir_mode=0700`, sobald ein Paar ein `owner_user` hat (Pflicht bei Nutzer-Kontext-Paaren,
|
||||
optional bei System-Kontext). Das ist nicht nur für vollen Zugriff (inkl. Skript-Ausführung)
|
||||
nötig, sondern bei WebDAV auch sicherheitsrelevant: `mount.davfs` erlaubt einem
|
||||
unprivilegierten Nutzer das Mounten einer `user`-Zeile laut `man mount.davfs` nur, wenn
|
||||
`uid=` auf ihn selbst zeigt - ohne das dürfte jedes Mitglied der Gruppe `davfs2` jedes
|
||||
konfigurierte Paar mounten, nicht nur sein eigenes (`setup fstab` verweigert daher Paare ohne
|
||||
`owner_user`).
|
||||
|
||||
**Bekannte, bewusst nicht behobene Lücke bei CIFS im Nutzerkontext:** `man mount.cifs`
|
||||
bestätigt, dass `uid=`/`gid=` dort **ausschließlich** die simulierte Datei-Ownership nach dem
|
||||
Mount betreffen - anders als bei davfs2 gibt es **keinen** Mechanismus, der das Mount-*Recht*
|
||||
einer `user`-fstab-Zeile auf eine bestimmte Person einschränkt. Jeder lokale Nutzer, der
|
||||
unprivilegiert mounten darf, kann daher aktuell jedes konfigurierte CIFS-Nutzer-Kontext-Paar
|
||||
mounten (nicht nur sein eigenes) und dabei dessen gespeicherte Zugangsdaten für die Dauer des
|
||||
Mounts mitbenutzen. Das ist eine strukturelle Grenze von `mount(8)`/`mount.cifs`, keine Lücke,
|
||||
die sich über Mount-Optionen schließen ließe. **Konsequenz:** CIFS-Nutzer-Kontext-Paare nur
|
||||
auf Einzelnutzer-Maschinen oder unter sich gegenseitig bereits vertrauenden lokalen Nutzern
|
||||
einsetzen. davfs2 ist von diesem Problem nicht betroffen (siehe oben); bei NFS gibt es keine
|
||||
clientseitige `uid=`/`gid=`-Option, Zugriff bestimmt dort ausschließlich der Server über die
|
||||
tatsächlichen Datei-Eigentümer/-Rechte des Exports - wer die Freigabe mounten kann, sieht
|
||||
dadurch nicht automatisch fremde Daten.
|
||||
- **davfs2-Konfiguration**: SmartMount setzt in `davfs2.conf` automatisch `gui_optimize 1`
|
||||
(bündelt PROPFIND-Anfragen, wichtig für grafische Dateimanager) sowie `buf_size 16384`
|
||||
(deutlich über dem Standard von 16 KiB) - Letzteres behebt ein bekanntes Praxisproblem, bei
|
||||
dem `ls` in Verzeichnissen mit vielen Dateien einen leeren/unvollständigen Inhalt zeigt,
|
||||
obwohl einzelne Dateien direkt geöffnet werden können (der FUSE-readdir-Puffer wird bei zu
|
||||
kleinem `buf_size` stillschweigend abgeschnitten).
|
||||
- **Zugangsdaten-Verschlüsselung**: Turso hat aktuell keine produktionsreife eingebaute
|
||||
Verschlüsselung, daher verschlüsselt SmartMount Passwörter selbst (AES-256-GCM) vor der
|
||||
Ablage. Der Master-Schlüssel wird bevorzugt im OS-Keyring (GNOME Keyring/KWallet über
|
||||
secret-service) abgelegt; ist keins verfügbar (typisch für den root/System-Dienst sowie
|
||||
Headless-Systeme), wird automatisch auf eine Schlüsseldatei (`chmod 600`, neben der
|
||||
Konfiguration) zurückgegriffen.
|
||||
- **`mac2ip`-Integration**: SmartMount ruft `mac2ip --json --auto-trust-networks <mac>` auf.
|
||||
`--auto-trust-networks` lässt mac2ip die eigene "nmap-Scan in diesem Netzwerk erlauben?"-
|
||||
Rückfrage automatisch bejahen und dauerhaft in seiner eigenen Cache-Datenbank merken - das
|
||||
erspart SmartMount, das Konfigurationsschema eines fremden Tools zu kennen oder dort direkt
|
||||
hineinzuschreiben.
|
||||
|
||||
---
|
||||
|
||||
## Struktur des Repositories
|
||||
|
||||
```text
|
||||
├── .cargo/
|
||||
│ └── config.toml # Cargo-Konfiguration (Linker für Cross-Compiling, Registry)
|
||||
├── .gitea/
|
||||
│ └── workflows/ # CI/CD-Pipelines (Build, Tests, Security-Scans, Releases)
|
||||
├── scripts/
|
||||
│ ├── get-build-number.py # Dynamische Ermittlung der nächsten Paket-Revisionsnummer
|
||||
│ ├── package-arch.py # Erstellung von Arch Linux .pkg.tar.zst Paketen
|
||||
│ └── report-security-issue.py # Security-Scan-Ergebnisse als Gitea-Issue melden
|
||||
├── src/
|
||||
│ ├── main.rs # Dünner Einstiegspunkt (CLI-Parsing, Dispatch)
|
||||
│ ├── lib.rs # Bibliotheks-Wurzel
|
||||
│ ├── cli/ # `clap`-Subcommands
|
||||
│ ├── config/ # Konfigurationsschema + CRUD (config-ctdra)
|
||||
│ ├── crypto/ # Verschlüsselung + Master-Key-Auflösung
|
||||
│ ├── db/ # Verschlüsselte Zugangsdaten (Turso)
|
||||
│ ├── mount/ # WebDAV/SMB/NFS-Backends, dynamischer Dispatch
|
||||
│ ├── network/ # Erreichbarkeit, mac2ip-Integration
|
||||
│ ├── reconcile/ # Watchdog-Entscheidungslogik (lokal/Cloud-Umschaltung)
|
||||
│ ├── systemd/ # systemd-Unit-Generierung/-Installation
|
||||
│ └── fstab/ # Einmaliges root-Setup für Nutzer-Mounts
|
||||
├── tests/ # Cross-Modul-Integrationstests
|
||||
├── Cargo.toml # Cargo Manifest & Paketierungsmetadaten (deb, rpm, arch)
|
||||
├── LICENSE # Lizenzdatei (GPL-3.0-or-later)
|
||||
├── AGENTS.md # Richtlinien und Leitfaden für KI-Coding-Agenten
|
||||
└── README.md # Diese Datei
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lokale Entwicklung
|
||||
|
||||
```bash
|
||||
cargo build
|
||||
cargo test
|
||||
cargo build --release
|
||||
|
||||
# Debian-Paket bauen (.deb)
|
||||
cargo deb
|
||||
|
||||
# RPM-Paket bauen (.rpm)
|
||||
cargo generate-rpm
|
||||
|
||||
# Arch Linux-Paket bauen (.pkg.tar.zst)
|
||||
python3 scripts/package-arch.py --arch x86_64 --pkgrel 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Workflow-Übersicht
|
||||
|
||||
| Branch | Workflow | Paket-Kanal | Release-Typ |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `main` | `.gitea/workflows/main.yaml` | `stable` | Offizielles Release (`v<VERSION>`) |
|
||||
| `testing` | `.gitea/workflows/testing.yaml` | `testing` | Pre-Release (`v<VERSION>-preview`) |
|
||||
|
||||
Zusätzlich: automatisierte Security-Scans (Trivy, OSV-Scanner, TruffleHog), Renovate für
|
||||
Abhängigkeits-Updates, sowie automatisiertes `cargo fmt`/`cargo clippy --fix` auf dem
|
||||
`dev`-Branch.
|
||||
|
||||
---
|
||||
|
||||
## Lizenz
|
||||
|
||||
[GPL-3.0-or-later](LICENSE).
|
||||
|
||||
Reference in New Issue
Block a user