Feature: Shell-Completion wird automatisch mitpaketiert statt über eigenen Befehl

Entfernt den 'smart-mount completions'-Befehl komplett - Shell-Completion
(bash/zsh/fish) wird stattdessen beim Bauen des .deb/.rpm/.pkg.tar.zst-Pakets
generiert und als normales Paket-Asset ausgeliefert, sodass sie beim
Installieren automatisch mit installiert und beim Entfernen automatisch mit
entfernt wird, ganz ohne Zutun des Nutzers.

Das 'cli'-Modul ist dafür Teil der Library geworden (pub mod cli), damit ein
neues, separates Build-Hilfsprogramm (src/bin/generate-completions.rs) die
bestehende Cli-Definition als einzige Quelle wiederverwenden kann, statt sie
zu duplizieren. Die Gitea-Workflows rufen es nach dem Release-Build auf;
Cargo.toml und scripts/package-arch.py bündeln die erzeugten Dateien für
alle drei Paketformate an den jeweils distributionsüblichen Pfaden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 17:07:11 +02:00
co-authored by Claude Sonnet 5
parent eff6dafa77
commit abcc11f078
15 changed files with 120 additions and 51 deletions
+6 -1
View File
@@ -59,7 +59,7 @@ jobs:
cargo-${{ runner.os }}- cargo-${{ runner.os }}-
- name: Alte Paketierungs-Ausgaben aus dem Cache entfernen - name: Alte Paketierungs-Ausgaben aus dem Cache entfernen
run: rm -rf target/debian target/generate-rpm target/arch run: rm -rf target/debian target/generate-rpm target/arch target/completions
- name: Install Cross-Compilation Toolchains (apt) - name: Install Cross-Compilation Toolchains (apt)
run: | run: |
@@ -111,6 +111,11 @@ jobs:
cargo build --release --target x86_64-unknown-linux-gnu cargo build --release --target x86_64-unknown-linux-gnu
cargo build --release --target aarch64-unknown-linux-gnu cargo build --release --target aarch64-unknown-linux-gnu
- name: Generate Shell Completions
run: |
cargo build --release --bin generate-completions
./target/release/generate-completions target/completions
- name: Determine Build Number - name: Determine Build Number
id: build_num id: build_num
env: env:
+6 -1
View File
@@ -59,7 +59,7 @@ jobs:
cargo-${{ runner.os }}- cargo-${{ runner.os }}-
- name: Alte Paketierungs-Ausgaben aus dem Cache entfernen - name: Alte Paketierungs-Ausgaben aus dem Cache entfernen
run: rm -rf target/debian target/generate-rpm target/arch run: rm -rf target/debian target/generate-rpm target/arch target/completions
- name: Install Cross-Compilation Toolchains (apt) - name: Install Cross-Compilation Toolchains (apt)
run: | run: |
@@ -111,6 +111,11 @@ jobs:
cargo build --release --target x86_64-unknown-linux-gnu cargo build --release --target x86_64-unknown-linux-gnu
cargo build --release --target aarch64-unknown-linux-gnu cargo build --release --target aarch64-unknown-linux-gnu
- name: Generate Shell Completions
run: |
cargo build --release --bin generate-completions
./target/release/generate-completions target/completions
- name: Determine Build Number - name: Determine Build Number
id: build_num id: build_num
env: env:
+12
View File
@@ -68,6 +68,13 @@ assets = [
["packaging/systemd/smart-mount-mount.service", "usr/lib/systemd/system/smart-mount-mount.service", "644"], ["packaging/systemd/smart-mount-mount.service", "usr/lib/systemd/system/smart-mount-mount.service", "644"],
["packaging/systemd/smart-mount-watch.service", "usr/lib/systemd/system/smart-mount-watch.service", "644"], ["packaging/systemd/smart-mount-watch.service", "usr/lib/systemd/system/smart-mount-watch.service", "644"],
["packaging/systemd/smart-mount-watch.timer", "usr/lib/systemd/system/smart-mount-watch.timer", "644"], ["packaging/systemd/smart-mount-watch.timer", "usr/lib/systemd/system/smart-mount-watch.timer", "644"],
# Von 'cargo build --bin generate-completions' erzeugt (siehe .gitea/workflows/main.yaml) -
# kein 'smart-mount completions'-Subcommand mehr, Shell-Completion wird stattdessen wie jedes
# andere Paket-Asset automatisch mit installiert/entfernt. Debian/Ubuntu laden Zsh-Completions
# aus 'vendor-completions' (anders als Fedora/Arch, siehe [package.metadata.generate-rpm]).
["target/completions/smart-mount.bash", "usr/share/bash-completion/completions/smart-mount", "644"],
["target/completions/_smart-mount", "usr/share/zsh/vendor-completions/_smart-mount", "644"],
["target/completions/smart-mount.fish", "usr/share/fish/vendor_completions.d/smart-mount.fish", "644"],
] ]
[package.metadata.generate-rpm] [package.metadata.generate-rpm]
@@ -78,6 +85,11 @@ assets = [
{ source = "packaging/systemd/smart-mount-mount.service", dest = "/usr/lib/systemd/system/smart-mount-mount.service", mode = "644" }, { source = "packaging/systemd/smart-mount-mount.service", dest = "/usr/lib/systemd/system/smart-mount-mount.service", mode = "644" },
{ source = "packaging/systemd/smart-mount-watch.service", dest = "/usr/lib/systemd/system/smart-mount-watch.service", mode = "644" }, { source = "packaging/systemd/smart-mount-watch.service", dest = "/usr/lib/systemd/system/smart-mount-watch.service", mode = "644" },
{ source = "packaging/systemd/smart-mount-watch.timer", dest = "/usr/lib/systemd/system/smart-mount-watch.timer", mode = "644" }, { source = "packaging/systemd/smart-mount-watch.timer", dest = "/usr/lib/systemd/system/smart-mount-watch.timer", mode = "644" },
# Fedora/RHEL (wie Arch, siehe scripts/package-arch.py) laden Zsh-Completions aus
# 'site-functions' statt Debians 'vendor-completions' (siehe [package.metadata.deb]).
{ source = "target/completions/smart-mount.bash", dest = "/usr/share/bash-completion/completions/smart-mount", mode = "644" },
{ source = "target/completions/_smart-mount", dest = "/usr/share/zsh/site-functions/_smart-mount", mode = "644" },
{ source = "target/completions/smart-mount.fish", dest = "/usr/share/fish/vendor_completions.d/smart-mount.fish", mode = "644" },
] ]
requires = { "mac2ip" = "*" } requires = { "mac2ip" = "*" }
suggests = { "davfs2" = "*", "cifs-utils" = "*", "nfs-utils" = "*" } suggests = { "davfs2" = "*", "cifs-utils" = "*", "nfs-utils" = "*" }
+4 -4
View File
@@ -88,12 +88,12 @@ sudo smart-mount service crontab --remove
# einzeln beim Mount-Fehlschlag entdecken würde # einzeln beim Mount-Fehlschlag entdecken würde
smart-mount doctor smart-mount doctor
smart-mount doctor --json 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"
``` ```
Shell-Completion (bash/zsh/fish) wird beim Installieren des .deb/.rpm/.pkg.tar.zst-Pakets
automatisch mit installiert (und beim Entfernen des Pakets automatisch mit entfernt) - dafür ist
kein eigenes `smart-mount`-Subcommand nötig.
Die Konfiguration liegt immer unter `/etc/smart-mount/config.toml`, unabhängig davon, ob Die Konfiguration liegt immer unter `/etc/smart-mount/config.toml`, unabhängig davon, ob
`smart-mount` selbst mit oder ohne Root-Rechte aufgerufen wird (siehe oben). Die verschlüsselte `smart-mount` selbst mit oder ohne Root-Rechte aufgerufen wird (siehe oben). Die verschlüsselte
Zugangsdaten-Datenbank (`smart-mount.db`) liegt im selben Verzeichnis. Zugangsdaten-Datenbank (`smart-mount.db`) liegt im selben Verzeichnis.
+29
View File
@@ -61,6 +61,27 @@ def collect_systemd_units(systemd_src_dir="packaging/systemd"):
return unit_files, installable return unit_files, installable
def collect_completions(completions_dir="target/completions"):
"""Findet die von 'cargo build --bin generate-completions' erzeugten Shell-Completion-
Skripte (siehe src/bin/generate-completions.rs), falls vorhanden, und ordnet sie generisch -
ohne den Anwendungsnamen zu kennen - allein anhand ihrer clap_complete-Namenskonvention
(Bash: '<name>.bash', Zsh: '_<name>', Fish: '<name>.fish') dem jeweiligen
System-Verzeichnis zu. Gibt eine Liste aus (Quellpfad, Zielverzeichnis, Zieldateiname) zurück."""
if not os.path.isdir(completions_dir):
return []
mapping = []
for f in sorted(os.listdir(completions_dir)):
src = os.path.join(completions_dir, f)
if f.endswith(".bash"):
mapping.append((src, "usr/share/bash-completion/completions", f[: -len(".bash")]))
elif f.endswith(".fish"):
mapping.append((src, "usr/share/fish/vendor_completions.d", f))
elif f.startswith("_"):
mapping.append((src, "usr/share/zsh/site-functions", f))
return mapping
def build_install_scriptlet(installable, all_units): def build_install_scriptlet(installable, all_units):
"""Erzeugt den Inhalt einer Arch-'.INSTALL'-Datei (siehe `man PKGBUILD`, Abschnitt """Erzeugt den Inhalt einer Arch-'.INSTALL'-Datei (siehe `man PKGBUILD`, Abschnitt
'install'), die den paketierten systemd-Dienst beim Installieren aktiviert/startet und beim 'install'), die den paketierten systemd-Dienst beim Installieren aktiviert/startet und beim
@@ -155,6 +176,14 @@ def build_package(target_triple=None, target_arch=None, pkgrel=None):
check=True, check=True,
) )
for src, dest_dir, dest_name in collect_completions():
target_dir = os.path.join(build_dir, dest_dir)
os.makedirs(target_dir, exist_ok=True)
subprocess.run(
["install", "-m", "644", src, os.path.join(target_dir, dest_name)],
check=True,
)
installed_size = subprocess.check_output(["du", "-sb", build_dir]).decode().split()[0] installed_size = subprocess.check_output(["du", "-sb", build_dir]).decode().split()[0]
builddate = str(int(time.time())) builddate = str(int(time.time()))
+34
View File
@@ -0,0 +1,34 @@
//! Erzeugt die Shell-Completion-Skripte (bash/zsh/fish) für `smart-mount` als Dateien in einem
//! Ausgabeverzeichnis - kein `smart-mount`-Subcommand mehr (siehe `smart_mount::cli::Commands`),
//! sondern ein separates Build-Hilfsprogramm, das ausschließlich während der CI-Paketierung
//! aufgerufen wird (siehe `.gitea/workflows/main.yaml`/`testing.yaml`): die erzeugten Dateien
//! werden dort als normale Paket-Assets gebündelt, sodass sie beim Installieren des .deb/.rpm/
//! .pkg.tar.zst-Pakets automatisch mit installiert und beim Entfernen automatisch mit entfernt
//! werden - ganz ohne Zutun des Nutzers.
//!
//! Nutzt `smart_mount::cli::Cli` als einzige Quelle der CLI-Definition (kein separat gepflegtes
//! Duplikat), da `cli` als `pub mod` Teil der Library ist und so auch von diesem zusätzlichen
//! Binary-Target aus der Cargo-eigenen `src/bin/`-Autodiscovery erreichbar ist.
use std::path::PathBuf;
use clap::CommandFactory;
use clap_complete::Shell;
use smart_mount::cli::Cli;
fn main() {
let Some(out_dir) = std::env::args_os().nth(1).map(PathBuf::from) else {
eprintln!("usage: generate-completions <output-dir>");
std::process::exit(1);
};
std::fs::create_dir_all(&out_dir)
.unwrap_or_else(|e| panic!("could not create '{}': {e}", out_dir.display()));
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
for shell in [Shell::Bash, Shell::Zsh, Shell::Fish] {
let path = clap_complete::generate_to(shell, &mut cmd, &bin_name, &out_dir)
.unwrap_or_else(|e| panic!("could not generate {shell} completions: {e}"));
println!("generated: {}", path.display());
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
//! `smart-mount doctor` - prüft die im Laufe der Entwicklung angesammelten Voraussetzungen //! `smart-mount doctor` - prüft die im Laufe der Entwicklung angesammelten Voraussetzungen
//! (Binaries, Gruppenmitgliedschaft, fstab-Setup, Scheduler) gebündelt an einer Stelle. //! (Binaries, Gruppenmitgliedschaft, fstab-Setup, Scheduler) gebündelt an einer Stelle.
use smart_mount::config; use crate::config;
use smart_mount::doctor::{self, CheckStatus}; use crate::doctor::{self, CheckStatus};
pub async fn run(json: bool) -> anyhow::Result<()> { pub async fn run(json: bool) -> anyhow::Result<()> {
let cfg = config::pairs::load()?; let cfg = config::pairs::load()?;
+8 -10
View File
@@ -12,10 +12,8 @@ use std::str::FromStr;
use clap::{Args, Subcommand}; use clap::{Args, Subcommand};
use dialoguer::{Confirm, Input, Password, Select}; use dialoguer::{Confirm, Input, Password, Select};
use smart_mount::config::{ use crate::config::{self, AppConfig, CloudSide, DrivePair, LocalAddress, LocalSide, MountKind};
self, AppConfig, CloudSide, DrivePair, LocalAddress, LocalSide, MountKind, use crate::db::credentials::{Credential, CredentialStore, Side};
};
use smart_mount::db::credentials::{Credential, CredentialStore, Side};
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum DriveAction { pub enum DriveAction {
@@ -145,7 +143,7 @@ async fn remove(id: &str) -> anyhow::Result<()> {
// Vor dem Entfernen aus der Config nachschlagen, damit wir hinterher noch wissen, welche // Vor dem Entfernen aus der Config nachschlagen, damit wir hinterher noch wissen, welche
// Mount-Typen/Adressen betroffen sind - nötig, um die passenden Klartext-Zugangsdaten // Mount-Typen/Adressen betroffen sind - nötig, um die passenden Klartext-Zugangsdaten
// (davfs2 secrets, .cred-Datei) aufzuräumen, siehe smart_mount::mount::cleanup_credentials. // (davfs2 secrets, .cred-Datei) aufzuräumen, siehe crate::mount::cleanup_credentials.
let cfg = config::pairs::load()?; let cfg = config::pairs::load()?;
let pair = config::pairs::find_pair(&cfg, id)?; let pair = config::pairs::find_pair(&cfg, id)?;
@@ -154,7 +152,7 @@ async fn remove(id: &str) -> anyhow::Result<()> {
// darf das Entfernen aus der Konfiguration nicht blockieren - sonst käme der Nutzer nicht // darf das Entfernen aus der Konfiguration nicht blockieren - sonst käme der Nutzer nicht
// mehr an sein eigenes `drive remove` heran, ohne vorher erst als root/mit den richtigen // mehr an sein eigenes `drive remove` heran, ohne vorher erst als root/mit den richtigen
// Rechten manuell auszuhängen. // Rechten manuell auszuhängen.
if let Err(e) = smart_mount::reconcile::unmount_pair(&pair, &cfg.settings).await { if let Err(e) = crate::reconcile::unmount_pair(&pair, &cfg.settings).await {
logger_ctdra::warn( logger_ctdra::warn(
"drive", "drive",
&format!("could not unmount drive pair '{id}' before removal: {e}"), &format!("could not unmount drive pair '{id}' before removal: {e}"),
@@ -163,7 +161,7 @@ async fn remove(id: &str) -> anyhow::Result<()> {
config::pairs::remove_pair(id)?; config::pairs::remove_pair(id)?;
let creds = CredentialStore::open().await?; let creds = CredentialStore::open().await?;
creds.delete(id, None).await?; creds.delete(id, None).await?;
smart_mount::mount::cleanup_credentials(&pair, &cfg.settings); crate::mount::cleanup_credentials(&pair, &cfg.settings);
logger_ctdra::info("drive", &format!("drive pair '{id}' removed")); logger_ctdra::info("drive", &format!("drive pair '{id}' removed"));
println!("Drive pair '{id}' removed."); println!("Drive pair '{id}' removed.");
@@ -356,7 +354,7 @@ async fn edit(id: &str, args: DriveArgs) -> anyhow::Result<()> {
// würde ein aktiver Mount verwaisen. Nur eine explizite '--mount-point'-Angabe ändert ihn. // würde ein aktiver Mount verwaisen. Nur eine explizite '--mount-point'-Angabe ändert ihn.
let mount_point = match args.mount_point { let mount_point = match args.mount_point {
Some(p) => { Some(p) => {
if smart_mount::mount::target::active_side(&existing).is_some() { if crate::mount::target::active_side(&existing).is_some() {
println!( println!(
"Warning: pair '{id}' appears to be actively mounted - the old backing \ "Warning: pair '{id}' appears to be actively mounted - the old backing \
directories will be orphaned. Run 'sudo smart-mount unmount --name {id}' \ directories will be orphaned. Run 'sudo smart-mount unmount --name {id}' \
@@ -515,7 +513,7 @@ fn invoking_user() -> Option<String> {
/// Optional: falls gesetzt, bekommt dieser Nutzer bei CIFS/WebDAV vollen Zugriff /// Optional: falls gesetzt, bekommt dieser Nutzer bei CIFS/WebDAV vollen Zugriff
/// (uid=/gid=/file_mode=0700/dir_mode=0700) statt der sonst üblichen root-Ownership - der /// (uid=/gid=/file_mode=0700/dir_mode=0700) statt der sonst üblichen root-Ownership - der
/// Mount selbst läuft immer als root (siehe [`smart_mount::systemd`]). `default_owner` ist der /// Mount selbst läuft immer als root (siehe [`crate::systemd`]). `default_owner` ist der
/// vorbelegte/vorgeschlagene Wert (bei `add`: der einrichtende Nutzer, siehe `invoking_user`; /// vorbelegte/vorgeschlagene Wert (bei `add`: der einrichtende Nutzer, siehe `invoking_user`;
/// bei `edit`: der bisherige `owner_user`) - immer überschreibbar per Flag, im interaktiven /// bei `edit`: der bisherige `owner_user`) - immer überschreibbar per Flag, im interaktiven
/// Prompt auch durch Ablehnen oder einen anderen Namen. /// Prompt auch durch Ablehnen oder einen anderen Namen.
@@ -693,7 +691,7 @@ fn resolve_credentials(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use smart_mount::config::LocalAddress; use crate::config::LocalAddress;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
fn sample_pair(mount_point: &str) -> DrivePair { fn sample_pair(mount_point: &str) -> DrivePair {
+2 -16
View File
@@ -7,12 +7,11 @@ pub mod service;
pub mod status; pub mod status;
pub mod watch; pub mod watch;
use clap::{CommandFactory, Parser, Subcommand}; use clap::{Parser, Subcommand};
use clap_complete::Shell;
/// Startet den Prozess bei Bedarf transparent über `sudo` neu, falls nicht bereits als root /// Startet den Prozess bei Bedarf transparent über `sudo` neu, falls nicht bereits als root
/// aufgerufen. Mounten läuft ausschließlich als root/System-Dienst (siehe /// aufgerufen. Mounten läuft ausschließlich als root/System-Dienst (siehe
/// [`smart_mount::systemd`]) - es gibt seit dem Wegfall des Nutzerkontexts keine /// [`crate::systemd`]) - es gibt seit dem Wegfall des Nutzerkontexts keine
/// unprivilegierte Mount-Variante mehr, für die ein Rechte-Check hier zu früh käme. /// unprivilegierte Mount-Variante mehr, für die ein Rechte-Check hier zu früh käme.
/// ///
/// `run_as_root()` ersetzt den aktuellen Prozess per `execve` und kehrt bei Erfolg nie zurück /// `run_as_root()` ersetzt den aktuellen Prozess per `execve` und kehrt bei Erfolg nie zurück
@@ -88,9 +87,6 @@ pub enum Commands {
#[arg(long)] #[arg(long)]
json: bool, json: bool,
}, },
/// Prints a shell completion script, e.g.:
/// `smart-mount completions bash > /etc/bash_completion.d/smart-mount`.
Completions { shell: Shell },
} }
/// Kurzname eines Subcommands fürs Logging (siehe [`dispatch`]) - kein `Debug`-Derive auf /// Kurzname eines Subcommands fürs Logging (siehe [`dispatch`]) - kein `Debug`-Derive auf
@@ -104,7 +100,6 @@ fn command_label(cmd: &Commands) -> &'static str {
Commands::Watch => "watch", Commands::Watch => "watch",
Commands::Service { .. } => "service", Commands::Service { .. } => "service",
Commands::Doctor { .. } => "doctor", Commands::Doctor { .. } => "doctor",
Commands::Completions { .. } => "completions",
} }
} }
@@ -122,14 +117,5 @@ pub async fn dispatch(cli: Cli) -> anyhow::Result<()> {
Commands::Watch => watch::run().await, Commands::Watch => watch::run().await,
Commands::Service { action } => service::run(action), Commands::Service { action } => service::run(action),
Commands::Doctor { json } => doctor::run(json).await, Commands::Doctor { json } => doctor::run(json).await,
Commands::Completions { shell } => {
clap_complete::generate(
shell,
&mut Cli::command(),
"smart-mount",
&mut std::io::stdout(),
);
Ok(())
}
} }
} }
+4 -4
View File
@@ -1,11 +1,11 @@
//! `smart-mount mount` / `smart-mount unmount`. //! `smart-mount mount` / `smart-mount unmount`.
use smart_mount::config::{self, DrivePair}; use crate::config::{self, DrivePair};
use smart_mount::db::credentials::CredentialStore; use crate::db::credentials::CredentialStore;
use smart_mount::reconcile; use crate::reconcile;
fn select_pairs( fn select_pairs(
cfg: &smart_mount::config::AppConfig, cfg: &crate::config::AppConfig,
name: Option<&str>, name: Option<&str>,
all: bool, all: bool,
) -> anyhow::Result<Vec<DrivePair>> { ) -> anyhow::Result<Vec<DrivePair>> {
+3 -2
View File
@@ -1,8 +1,9 @@
//! `smart-mount service crontab [--remove]`. //! `smart-mount service crontab [--remove]`.
use clap::Subcommand; use clap::Subcommand;
use smart_mount::config;
use smart_mount::systemd; use crate::config;
use crate::systemd;
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum ServiceAction { pub enum ServiceAction {
+5 -5
View File
@@ -2,10 +2,10 @@
use serde::Serialize; use serde::Serialize;
use smart_mount::config; use crate::config;
use smart_mount::db::credentials::Side; use crate::db::credentials::Side;
use smart_mount::mount::target; use crate::mount::target;
use smart_mount::network; use crate::network;
#[derive(Serialize)] #[derive(Serialize)]
struct PairStatus { struct PairStatus {
@@ -48,7 +48,7 @@ pub async fn run(name: Option<String>, json: bool) -> anyhow::Result<()> {
// wiederverwendet, statt sie für beide Zwecke unabhängig voneinander ein zweites // wiederverwendet, statt sie für beide Zwecke unabhängig voneinander ein zweites
// Mal aufzulösen. // Mal aufzulösen.
let resolved_local_ip = let resolved_local_ip =
smart_mount::network::address::resolve_ip(&pair.local.address, &cfg.settings); crate::network::address::resolve_ip(&pair.local.address, &cfg.settings);
let local_source = resolved_local_ip let local_source = resolved_local_ip
.as_ref() .as_ref()
.map(|ip| target::format_local_source(&pair.local, *ip)) .map(|ip| target::format_local_source(&pair.local, *ip))
+3 -4
View File
@@ -1,10 +1,9 @@
//! `smart-mount watch` - ein einzelner Reconcile-Durchlauf, gedacht für systemd-Timer/Cron. //! `smart-mount watch` - ein einzelner Reconcile-Durchlauf, gedacht für systemd-Timer/Cron.
use smart_mount::config;
use smart_mount::db::credentials::CredentialStore;
use smart_mount::reconcile::{self, Action};
use crate::cli::mount_cmd::print_outcome; use crate::cli::mount_cmd::print_outcome;
use crate::config;
use crate::db::credentials::CredentialStore;
use crate::reconcile::{self, Action};
pub async fn run() -> anyhow::Result<()> { pub async fn run() -> anyhow::Result<()> {
crate::cli::require_root("watch")?; crate::cli::require_root("watch")?;
+1
View File
@@ -1,6 +1,7 @@
//! smart-mount: bindet lokale/Cloud-Laufwerkspaare (WebDAV/SMB/NFS) dynamisch ein und //! smart-mount: bindet lokale/Cloud-Laufwerkspaare (WebDAV/SMB/NFS) dynamisch ein und
//! schaltet automatisch zwischen LAN und Cloud um. //! schaltet automatisch zwischen LAN und Cloud um.
pub mod cli;
pub mod config; pub mod config;
pub mod crypto; pub mod crypto;
pub mod db; pub mod db;
+1 -2
View File
@@ -1,8 +1,7 @@
mod cli;
use std::process::ExitCode; use std::process::ExitCode;
use clap::Parser; use clap::Parser;
use smart_mount::cli;
#[tokio::main] #[tokio::main]
async fn main() -> ExitCode { async fn main() -> ExitCode {