"""Command-line mode, for machines without a desktop (e.g. a Fedora Server box): SaraSuite list SaraSuite install [--set Id=Value ...] [--option Id=on|off ...] SaraSuite update SaraSuite options [--option Id=on|off ...] (no --option: list them) SaraSuite remove [--configs] [--keep-runtime] Install starts from your defaults (Settings, or the manifest's) unless set. """ import argparse from Installer import Apply, DefaultAnswers, DefaultOptions, FetchCommit, GetStatus, Remove from Manifests import LoadPrograms def _Pairs(Items: list[str]) -> dict[str, str]: Pairs = {} for Item in Items: Key, Separator, Value = Item.partition("=") if not Separator: raise SystemExit(f"Expected Id=Value, got {Item!r}") Pairs[Key] = Value return Pairs def _Switches(Items: list[str]) -> dict[str, bool]: return {Key: Value.lower() in ("on", "yes", "true", "1") for Key, Value in _Pairs(Items).items()} def Run(Arguments: list[str]) -> int: Parser = argparse.ArgumentParser(prog="SaraSuite") Commands = Parser.add_subparsers(dest="Command", required=True) Commands.add_parser("list") Install = Commands.add_parser("install") Install.add_argument("Name") Install.add_argument("--set", action="append", default=[], dest="Answers") Install.add_argument("--option", action="append", default=[], dest="Options") Commands.add_parser("update").add_argument("Name") OptionsCommand = Commands.add_parser("options") OptionsCommand.add_argument("Name") OptionsCommand.add_argument("--option", action="append", default=[], dest="Options") RemoveCommand = Commands.add_parser("remove") RemoveCommand.add_argument("Name") RemoveCommand.add_argument("--configs", action="store_true") RemoveCommand.add_argument("--keep-runtime", action="store_true") Args = Parser.parse_args(Arguments) Programs = {Program.Name.lower(): Program for Program in LoadPrograms()} if Args.Command == "list": for Program in Programs.values(): Current = GetStatus(Program) State = "update available" if Current.UpdateAvailable else "installed" if Current.Installed else "not installed" Name = f" {Program.Name}" if Program.Parent else Program.Name print(f"{Name:12} {State:16} {Current.Error}") return 0 Program = Programs.get(Args.Name.lower()) if Program is None: print(f"Unknown program {Args.Name!r}. Known: {', '.join(P.Name for P in Programs.values())}") return 1 Current = GetStatus(Program) Installed = Current.Installed if Args.Command == "install": if Installed: print(f"{Program.Name} is already installed; use update.") return 1 Options = DefaultOptions(Program) Options.update(_Switches(Args.Options)) Answers = DefaultAnswers(Program) Answers.update(_Pairs(Args.Answers)) Apply(Program, print, Options, Answers) elif Args.Command == "update": if not Installed: print(f"{Program.Name} isn't installed.") return 1 Apply(Program, print) elif Args.Command == "options": if not Installed: print(f"{Program.Name} isn't installed.") return 1 if not Args.Options: for Option in Program.Options: State = "on" if Current.Receipt.Options.get(Option.Id) else "off" print(f"{Option.Id:20} {State:4} {Option.Label}") return 0 Options = dict(Current.Receipt.Options) Options.update(_Switches(Args.Options)) Apply(Program, print, Options, Source=FetchCommit(Program, Current.Receipt.Commit, print)) elif Args.Command == "remove": Remove(Program, Args.configs, not Args.keep_runtime, print) return 0