12 KiB
SaraSuite
Installs, updates and removes Sara programs (SaraSortd, SaraSort, SaraFem, SaraTag and the SaraView desktop client). It's one AppImage file: download it, run it, pick a program.
- Install: downloads the program's latest commit from its git repo, copies it into
~/.local/share/SaraSuite/Programs/<Name>/with its own Python venv, places its configs in~/.config/SaraSuite/<Name>/, and writes its desktop entries, systemd user units, D-Bus services and so on. Without systemd (runit on Void, OpenRC, s6, dinit, ...), background services run the way they do on Windows: see the table below. - Update: shown when the repo has a newer commit. Program files and integration files are
replaced; configs are merged: your edits always win, defaults you never touched follow the
new version, new keys are added (see
ConfigMerge.py). - Remove: takes out everything the install placed (it's all recorded in a receipt) plus everything the program made by itself while running. Configs are only removed when "Remove configs" is ticked. Your own files are never removed: folders that might hold them are only removed when empty. Files that existed before SaraSuite installed over them are put back.
Sources
Programs come straight from git over HTTPS, no git binary needed: the latest commit is read
from <repo>.git/info/refs and the files from <repo>/archive/<commit>.tar.gz, which GitHub and
Gitea both serve the same way. Repos must be public (no login support yet).
The host is one setting in ~/.config/SaraSuite/SaraSuite/Config.toml; manifests refer to it as
{GitHost}. It defaults to the Gitea on SpineS:
GitHost = "https://git.srhtnin.garden/sarah"
The only thing a program needs from the system is python3 (for its venv).
Updates
- Programs update from their own repos (see Sources above).
- The program list (
Programs/: manifests, templates, icons) is fetched from this repo whenever it has a new commit, so adding a program or fixing a recipe only takes a push. It's cached in~/.cache/SaraSuite/Catalog/; the AppImage's bundled copy is the fallback. Running from source always uses the localPrograms/. - SaraSuite itself checks this repo's latest Gitea release. When it's newer than its own
AppVersion, an "Update SaraSuite" button downloads the new AppImage, swaps it in place (same path, so the app menu entry keeps working) and restarts. From a terminal:SaraSuite-x86_64.AppImage self-update.
To publish a new SaraSuite: bump AppVersion in Core.py, commit and push, then run
Packaging/Release.py (optionally --notes "..."). It builds the AppImage, creates the release
v<AppVersion> with the AppImage attached, and copies it to the Shared folder. The Gitea token
comes from git's credential store, the same one git push uses.
Shared settings
Settings every SaraSuite program shares live in ~/.config/SaraSuite/Config.toml:
Theme = "CatppuccinMocha" # a file in ~/.config/SaraSuite/Themes/
FontFamily = "Fixedsys Excelsior" # the bundled font, or any installed font family
HideWindowTitleBar = false
Each program's own ~/.config/SaraSuite/<Program>/Config.toml has the same three keys plus
UseSharedSettings (default true). With it on, the program reads the shared file; with it
off, it uses its own values instead. Both SaraSuite (the gear button, Shared and SaraSuite
tabs) and SaraFem (its Settings, SaraSuite and General tabs) can edit them. Whichever program
starts first creates the shared file from its own values.
SaraSuite's own settings
The gear button also holds the git host and, in one tab per program, what that program's
install dialog starts with: which option boxes are ticked and the default answers (e.g.
SaraSortd's root folder). They're stored in ~/.config/SaraSuite/SaraSuite/Config.toml as
[OptionDefaults.<Program>] and [AnswerDefaults.<Program>], and override the manifest's
defaults.
Changing options after installing
Installed programs with options (e.g. SaraFem's file picker) get an Options button. Turning an option off takes out what it placed and puts back any file it had replaced; turning it on places it. It reapplies the installed commit, so the program itself isn't updated. The Update button only shows up when there's an update (or a broken install to repair).
Windows
SaraSuite.exe is the Windows version. Each build only lists the programs for its own system
(a manifest's Platforms), and does everything the Windows way:
| Linux | Windows |
|---|---|
~/.config/SaraSuite/ (settings) |
%APPDATA%\SaraSuite\ |
~/.local/share/SaraSuite/ (programs, receipts) |
%LOCALAPPDATA%\SaraSuite\ |
~/.cache/... |
%LOCALAPPDATA%\... |
| desktop entry | Start Menu shortcut ([[Shortcuts]]) |
| systemd user service | ServiceRun.py, started at login by a shortcut in the Startup folder; its PID is in %LOCALAPPDATA%\SaraSuite\Services\<Name>.pid |
Linux without systemd (no /run/systemd/system: runit on Void, OpenRC, s6, dinit, ...) runs
services like Windows does (Services.py): ServiceRun.py runs them, started at login by an XDG
autostart entry, ~/.config/autostart/<Name>.desktop, which desktop sessions start whatever the
init system is. It restarts a service 10 seconds after it fails, like the unit's
Restart=on-failure, and keeps its output in ~/.local/share/SaraSuite/Services/<Name>.log (its
PID next to it, in <Name>.pid). Units aren't written there, and RestartUnits stops the process
instead (D-Bus starts xdg-desktop-portal again when it's needed). A window manager that doesn't
run XDG autostart entries needs to start them itself (e.g. dex -a in its startup config).
| system python3 | the py launcher / python.exe; if there's none, SaraSuite installs Python for the user with winget |
| AppImage self-update (rename over it) | .exe self-update (the running one is renamed to SaraSuite.old.exe, deleted on the next start) |
Programs run on Windows with pythonw.exe -X utf8 (no console window; UTF-8 mode, since Python
on Windows otherwise reads text files in the old ANSI code page). Nothing needs admin rights.
Linux-only features (SaraFem's file picker portal and default-file-manager option) simply
aren't offered there.
Running and building
-
AppImage:
Packaging/BuildAppImage.shbuildsdist/SaraSuite-x86_64.AppImage(PyInstaller one-file build +appimagetool, like SaraFem's). When run as an AppImage, the header has an "Add to app menu" button. -
Windows:
Packaging/BuildExe.shbuildsdist/SaraSuite.exewith the Windows Python inside a Wine prefix (build/WinBuild/, set up on its first run), since PyInstaller can't build for Windows from Linux directly. The .exe's header has an "Add to Start menu" button. -
From source:
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt, thenPackaging/Launch.sh. -
Without a desktop (e.g. a server), the same file works from a terminal:
SaraSuite-x86_64.AppImage list SaraSuite-x86_64.AppImage install SaraSortd --set RootDir=~/Sorting --option Autostart=on SaraSuite-x86_64.AppImage update SaraSortd SaraSuite-x86_64.AppImage options SaraFem # list them SaraSuite-x86_64.AppImage options SaraFem --option FilePicker=off SaraSuite-x86_64.AppImage remove SaraSortd [--configs] [--keep-runtime]
Where things live
On Linux (on Windows, the same layout under %LOCALAPPDATA% and %APPDATA%; see Windows above):
| Path | What |
|---|---|
~/.local/share/SaraSuite/Programs/<Name>/ |
installed program (App/) and its .venv/ |
~/.local/share/SaraSuite/Receipts/<Name>.toml |
everything the install placed |
~/.local/share/SaraSuite/Pristine/<Name>/ |
the default configs as last installed (the merge base) |
~/.local/share/SaraSuite/Backups/<Name>/ |
files that were moved aside, and configs from before a merge |
~/.cache/SaraSuite/Downloads/ |
downloaded source, removed after installing |
~/.config/SaraSuite/<Name>/ |
the program's configs |
Adding a program
Make Programs/<Name>/Manifest.toml. Templates go in Programs/<Name>/Templates/.
Manifests are bundled into the AppImage, so a new or changed manifest means rebuilding it
(installed programs then show an update, even without a new commit).
Anything the manifest can't describe goes in an optional Programs/<Name>/Hooks.py.
Targets, templates and Set values can use {Variables}: Name, GitHost, Home, ConfigHome,
DataHome, CacheHome, ProgramDir, AppDir, Python (the venv's python), PythonW
(Windows: its console-less pythonw.exe), ConfigDir, DesktopName (e.g. COSMIC),
Desktop (e.g. cosmic), plus every Question's Id. Linux only: SystemdUserDir,
ApplicationsDir, DBusServicesDir. Windows only: StartMenuDir, StartupDir.
Every list item ([[Options]], [[Templates]], [[Services]], ...) can carry
Platform = "Linux" or "Windows" to only apply on that system; items without one apply on both.
In TOML templates, write paths as literal strings ('{ConfigDir}'): Windows paths are full of
backslashes, which a "..." string would read as escapes.
Name = "Example"
Description = "Shown on the program's card."
Platforms = ["Linux", "Windows"] # where it runs (default ["Linux"])
Parent = "SaraSortd" # optional: listed (indented) under that program
RestartUnits = ["xdg-desktop-portal.service"] # try-restarted after install/update/remove
[Version] # optional, shown on the card
File = "Example.py"
Pattern = 'Version = "([^"]+)"'
[Source]
Git = "{GitHost}/Example"
Branch = "main" # optional, default: the repo's default branch
Include = ["*.py", "assets/**"] # default: everything
Exclude = ["Packaging"] # .git, .venv, build, ... are always excluded
[Python]
RequirementsFile = "requirements.txt" # or: Requirements = ["toml"]
[[Questions]] # asked at install time, usable as {RootDir}
Id = "RootDir"
Prompt = "Folder to use"
Default = "~/Somewhere"
Kind = "Path" # expanded to an absolute path
[[Options]] # checkboxes at install time
Id = "Autostart"
Label = "Start on login"
Default = true
[[Configs]] # placed once, merged on updates
Source = "Conf.toml" # in the source checkout
Target = "{ConfigDir}/Conf.toml"
Set = { "Paths.RootDir" = "{RootDir}" }
[[Templates]] # owned by SaraSuite, rewritten on every update
Source = "Example.service" # in Templates/
Target = "{SystemdUserDir}/Example.service"
Option = "Autostart" # optional: only while the option is ticked
Root = false # true: outside $HOME, written through pkexec
Executable = false
[[Services]] # Linux: a systemd --user unit, enabled + started; without
Platform = "Linux" # systemd, Name/Script/WorkingDir run by ServiceRun.py instead,
Unit = "Example.service" # started at login by an autostart entry
Name = "Example"
Script = "Example.py"
WorkingDir = "{ConfigDir}"
Option = "Autostart"
[[Services]] # Windows: run by ServiceRun.py from a Startup-folder shortcut
Platform = "Windows" # (both: add ../../ServiceRun.py as a Template to {ProgramDir})
Name = "Example"
Script = "Example.py" # relative to the app folder
WorkingDir = "{ConfigDir}"
Option = "Autostart"
[[Shortcuts]] # Windows: a Start Menu shortcut (pythonw.exe -X utf8 Script)
Platform = "Windows"
Name = "Example"
Script = "Main.py" # relative to the app folder, which is also its working folder
Icon = "assets/Icon.png" # turned into an .ico
[[IniEdits]] # one key in a file SaraSuite doesn't own; restored on removal
File = "{ConfigHome}/mimeapps.list"
Section = "Default Applications"
Key = "inode/directory"
Value = "Example.desktop"
[[Leftovers]] # made by the program itself, only touched on removal
Path = "{CacheHome}/Example" # globs allowed
Kind = "Runtime" # or "Config"
Mode = "Remove" # or "IfEmpty"
Note = "thumbnail cache"
Hooks.py can define Leftovers(Context: dict) -> list[Manifests.Leftover] for leftovers
that depend on the program's own config (see Programs/SaraSortd/Hooks.py).