From 23f020a5b303f5f90e37a7bcce6edadbe4e8032a Mon Sep 17 00:00:00 2001 From: fiftydinar <65243233+fiftydinar@users.noreply.github.com> Date: Sat, 26 Jul 2025 16:10:21 +0200 Subject: [PATCH] feat: `soar` module (#413) * feat: `soar` module * fix(soar): Inverted `unlock-repos` logic * chore(soar): Add `soar` to modules.json * fix(soar): Logic for detecting if `unlock-repos` is disabled * fix(docs): Wrong `/usr/` systemd path reference * chore(soar): Add log for `unlock_repos` * chore(soar): Add `finished=false` at the beggining of `soar` shell profile * fix(soar): `/root` being `$HOME` in config * fix(soar): Wrong `PATH` I forgot to add `bin` directory * docs(soar): Clarify that `PATH` needs to be exported only outside of standard config dir * chore(soar): Unlock extra repos if `unlock-repos` is enabled * docs(soar): Mention of external repos * chore(soar): Simplify getting `XDG` directories * chore(soar): Add removing config in uninstallation script * chore(soar): Remove whole config directory instead of file in uninstallation script * fix(soar): Missing quote in uninstallation script * chore(soar): Make uninstallation script POSIX * fix(soar): Bad syntax for `soar-upgrade-packages` service `exec` It's `ExecStart`, not `Exec`. * fix(soar): Set `auto-upgrade` timer as `--user` instead of `--system` * chore(soar): Shorten the boot delay & randomize it for `auto-upgrades` * chore(soar): Add `network-online.target` in `Wants` to systemd service for `auto-upgrades` * chore(soar): Remove `upgrade-wait-after-boot`, as good defaults are set with no need for changing * chore(soar): Add option for unlocking the `PATH` in all conditions * chore(soar): Add `unlock-path` option to typespec * docs(soar): Add `unlock-path` to `module.yml` example * chore(soar): Remove condition for checking the `soar.sh` file If another `soar` module overwrites the previous one, it should be able to overwrite `soar.sh` shell profile too with custom option if set. * chore(soar): Remove `unlock-path`, as it's too dangerous * chore(soar): Don't copy update timer & service if `auto-upgrade` is false * chore(soar): Style fix * chore(soar): Don't execute `upgrade` when soar is already running * chore(soar): Use environment variable for `SOAR_CONFIG` to simplify things * docs(sour): Fix typo * chore(soar): Use native `soar` command for setting `bincache` repo only * fix(soar): Make `soar` condition check for auto-upgrades more reliable * chore(soar): Some more improvements to auto-update condition check False positive can happen if some text editor edits some file like `soar-something.txt` or similar. This assures more that only executed binary is detected. * chore(soar): Remove duplicate code for setting config * docs(soar): Add full example of options in `module.yml` * chore(soar): Rename `unlock-repos` to `additional-repos` & add docs about repo info * chore(soar): Enable `pkforge-cargo` & `pkgforge-go` repos * docs: rewordings, run through languagetool * chore(soar): Add condition in shell profile to check if `soar` PATH already exists * chore(soar): Disable redundant shellcheck SC2076 in shell profile * docs: final reword --------- Co-authored-by: xyny Co-authored-by: xyny <60004820+xynydev@users.noreply.github.com> --- modules.json | 1 + modules/soar/README.md | 64 ++++++++++++++++++++++ modules/soar/module.yml | 7 +++ modules/soar/soar-profile.sh | 8 +++ modules/soar/soar-upgrade-packages.service | 9 +++ modules/soar/soar-upgrade-packages.timer | 11 ++++ modules/soar/soar.sh | 61 +++++++++++++++++++++ modules/soar/soar.tsp | 25 +++++++++ 8 files changed, 186 insertions(+) create mode 100644 modules/soar/README.md create mode 100644 modules/soar/module.yml create mode 100644 modules/soar/soar-profile.sh create mode 100644 modules/soar/soar-upgrade-packages.service create mode 100644 modules/soar/soar-upgrade-packages.timer create mode 100644 modules/soar/soar.sh create mode 100644 modules/soar/soar.tsp diff --git a/modules.json b/modules.json index 2a27d51..be662e0 100644 --- a/modules.json +++ b/modules.json @@ -4,6 +4,7 @@ "https://raw.githubusercontent.com/blue-build/modules/main/modules/chezmoi/module.yml", "https://raw.githubusercontent.com/blue-build/modules/main/modules/bling/module.yml", "https://raw.githubusercontent.com/blue-build/modules/main/modules/brew/module.yml", + "https://raw.githubusercontent.com/blue-build/modules/main/modules/soar/module.yml", "https://raw.githubusercontent.com/blue-build/modules/main/modules/default-flatpaks/module.yml", "https://raw.githubusercontent.com/blue-build/modules/main/modules/fonts/module.yml", "https://raw.githubusercontent.com/blue-build/modules/main/modules/gnome-extensions/module.yml", diff --git a/modules/soar/README.md b/modules/soar/README.md new file mode 100644 index 0000000..44a69f8 --- /dev/null +++ b/modules/soar/README.md @@ -0,0 +1,64 @@ +# soar + +The `soar` module installs & integrates the [`soar`](https://github.com/pkgforge/soar) package manager as an alternative to [Homebrew / Linuxbrew](https://brew.sh/). + +[`soar`](https://github.com/pkgforge/soar) is a package manager, which manages the installation of portable & static binaries. +[PkgForge's](https://github.com/pkgforge) `bincache`, `pkgforge-cargo` & `pkgforge-go` repos are used by default for the binaries. +Other default & external repos that contain AppImages & other similar formats are disabled to make `soar` focused on CLI binaries only. +This is configurable if you wish to have a package manager for GUI applications, see [`Configuration options`](#configuration-options). + +The repositories with prebuilt binaries use the GitHub Container registry as their backend and all their packages are published there. + +Compared to [Homebrew / Linuxbrew](https://brew.sh/): +- there are no managed dependencies for packages by design (single package = single binary). +- no conflicting system packages in the repo (like `systemd`, `dbus` or similar). +- it's simpler in design, with respect for Linux folder structuring + +For more information, please see the [official documentation of `soar`](https://soar.qaidvoid.dev/). + +## Features + +- Downloads & installs `soar`. +- Sets up systemd timer for auto-upgrading `soar` packages. +- Sets up shell profile for automatically adding the directory containing `soar` binaries to `PATH`. + +## Repos + +To see the useful information about source, reliability, trust & security of all `soar` repos, including external ones, you can open the links below: +- https://docs.pkgforge.dev/repositories +- https://docs.pkgforge.dev/repositories/external + +## Local modification + +By default, `soar` utilizes BlueBuild's config (`/usr/share/bluebuild/soar/config.toml`). + +End-users can use custom a `soar` configuration by creating it at `~/.config/soar/config.toml`, or in a custom directory while making sure to supply it to `soar` by providing `SOAR_CONFIG` the environment variable in shell profile. +If you specify the custom `bin_path` directory for `soar` packages, you also need to export that directory to `PATH` manually in the shell profile. + +## Uninstallation + +Removing the `soar` module from the recipe is not enough to get it completely removed. +On a booted system, it's also necessary to run the `soar` uninstallation script to uninstall config & installed packages in the `${HOME}` directory. + +Either a local-user can execute this script manually, or the image-maintainer may make it automatic through a custom systemd service. + +
+ Uninstallation script + +```sh +#!/bin/sh +if [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/soar/config.toml" ]; then + echo "Removing soar config in '${XDG_CONFIG_HOME:-$HOME/.config}/soar/' directory" + rm -r "${XDG_CONFIG_HOME:-$HOME/.config}/soar/" +else + echo "'${XDG_CONFIG_HOME:-$HOME/.config}/soar/config.toml' file is already removed" +fi +if [ -d "${XDG_DATA_HOME:-$HOME/.local/share}/soar/" ]; then + echo "Removing '${XDG_DATA_HOME:-$HOME/.local/share}/soar/' directory" + rm -r "${XDG_DATA_HOME:-$HOME/.local/share}/soar/" +else + echo "'${XDG_DATA_HOME:-$HOME/.local/share}/soar/' directory is already removed" +fi +``` + +
diff --git a/modules/soar/module.yml b/modules/soar/module.yml new file mode 100644 index 0000000..14f6292 --- /dev/null +++ b/modules/soar/module.yml @@ -0,0 +1,7 @@ +name: soar +shortdesc: The soar module installs & integrates the soar package manager, as an alternative to Homebrew / Linuxbrew. +example: | + type: soar + additional-repos: true + auto-upgrade: true + upgrade-interval: '3h' diff --git a/modules/soar/soar-profile.sh b/modules/soar/soar-profile.sh new file mode 100644 index 0000000..0b5ef0d --- /dev/null +++ b/modules/soar/soar-profile.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Don't source PATH for soar packages when it's not an interactive terminal session & when it's a root user +if [[ ${-} == *i* && "$(/bin/id -u)" != 0 ]]; then + # shellcheck disable=SC2076 + if ! [[ "$PATH" =~ "${XDG_DATA_HOME:-$HOME/.local/share}/soar/bin:" ]]; then + export PATH="${XDG_DATA_HOME:-$HOME/.local/share}/soar/bin:${PATH}" + fi +fi diff --git a/modules/soar/soar-upgrade-packages.service b/modules/soar/soar-upgrade-packages.service new file mode 100644 index 0000000..8a923ea --- /dev/null +++ b/modules/soar/soar-upgrade-packages.service @@ -0,0 +1,9 @@ +[Unit] +Description=Auto-upgrade 'soar' packages +Wants=network-online.target +After=network-online.target + +[Service] +Type=oneshot +ExecCondition=/bin/bash -c 'if ps aux | grep -v grep | grep -E -q " /sbin/soar | /bin/soar | /usr/sbin/soar | /usr/bin/soar | soar "; then exit 1; else exit 0; fi' +ExecStart=/bin/soar update diff --git a/modules/soar/soar-upgrade-packages.timer b/modules/soar/soar-upgrade-packages.timer new file mode 100644 index 0000000..f84897f --- /dev/null +++ b/modules/soar/soar-upgrade-packages.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Timer for upgrading 'soar' packages + +[Timer] +RandomizedDelaySec=10m +OnBootSec=2min +OnUnitInactiveSec=8h +Persistent=true + +[Install] +WantedBy=timers.target \ No newline at end of file diff --git a/modules/soar/soar.sh b/modules/soar/soar.sh new file mode 100644 index 0000000..b71ddc8 --- /dev/null +++ b/modules/soar/soar.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Install 'soar' +echo "Downloading & installing 'soar' package manager" +REPO="pkgforge/soar" +LATEST_VER="$(basename $(curl -Ls -o /dev/null -w %{url_effective} https://github.com/${REPO}/releases/latest))" +# Assuming that ARM64 custom images will be built from ARM64 runners for this working detection +ARCH="$(uname -m)" +curl -fLs --create-dirs "https://github.com/${REPO}/releases/download/${LATEST_VER}/soar-${ARCH}-linux" -o "/usr/bin/soar" +chmod +x "/usr/bin/soar" + +# Configuration values for package auto-upgrades (using upgrade term here from brew) +AUTO_UPGRADE=$(echo "${1}" | jq -r 'try .["auto-upgrade"]') +if [[ -z "${AUTO_UPGRADE}" || "${AUTO_UPGRADE}" == "null" ]]; then + AUTO_UPGRADE=true +fi + +UPGRADE_INTERVAL=$(echo "${1}" | jq -r 'try .["upgrade-interval"]') +if [[ -z "${UPGRADE_INTERVAL}" || "${UPGRADE_INTERVAL}" == "null" ]]; then + UPGRADE_INTERVAL="8h" +fi + +# Configuration for enabling additional repos (outside of 'bincache') +ADDITIONAL_REPOS=$(echo "${1}" | jq -r 'try .["additional-repos"]') +mkdir -p "/usr/share/bluebuild/soar" +if [[ "${ADDITIONAL_REPOS}" == "true" ]]; then + echo "Enabling all additional 'soar' repos in config, including external ones" + soar defconfig --external -c "/usr/share/bluebuild/soar/config.toml" +else + echo "Using the default 'bincache', 'pkgforge-cargo' & 'pkgforge-go' repositories in config" + soar -c /usr/share/bluebuild/soar/config.toml defconfig -r bincache -r pkgforge-cargo -r pkgforge-go +fi +# Fix /root being ${HOME} +sed -i 's|/root|~|g' "/usr/share/bluebuild/soar/config.toml" +# Add soar config to environment +echo "SOAR_CONFIG=/usr/share/bluebuild/soar/config.toml" >> /etc/environment + +if [[ "${AUTO_UPGRADE}" == true ]]; then + echo "Configuring auto-upgrades of 'soar' packages" + echo "Copying soar-upgrade-packages service" + cp "${MODULE_DIRECTORY}/soar/soar-upgrade-packages.service" "/usr/lib/systemd/user/soar-upgrade-packages.service" + if [[ -n "${UPGRADE_INTERVAL}" ]] && [[ "${UPGRADE_INTERVAL}" != "8h" ]]; then + echo "Applying custom 'upgrade-interval' value in '${UPGRADE_INTERVAL}' time interval for soar-upgrade-packages timer" + sed -i "s/^OnUnitInactiveSec=.*/OnUnitInactiveSec=${UPGRADE_INTERVAL}/" "${MODULE_DIRECTORY}/soar/soar-upgrade-packages.timer" + fi + echo "Copying soar-upgrade-packages timer" + cp "${MODULE_DIRECTORY}/soar/soar-upgrade-packages.timer" "/usr/lib/systemd/user/soar-upgrade-packages.timer" + echo "Enabling auto-upgrades for 'soar' packages" + systemctl --global enable soar-upgrade-packages.timer +else + echo "Auto-upgrades for 'soar' packages are disabled" +fi + +# Add 'soar' packages to path only when it's interactive terminal session & non-root user, similar to brew +if [[ ! -d "/etc/profile.d/" ]]; then + mkdir -p "/etc/profile.d/" +fi +echo "Applying shell profile for exporting 'soar' packages directory to PATH" +cp "${MODULE_DIRECTORY}/soar/soar-profile.sh" "/etc/profile.d/soar.sh" diff --git a/modules/soar/soar.tsp b/modules/soar/soar.tsp new file mode 100644 index 0000000..114da0e --- /dev/null +++ b/modules/soar/soar.tsp @@ -0,0 +1,25 @@ +import "@typespec/json-schema"; +using TypeSpec.JsonSchema; + +@jsonSchema("/modules/soar-latest.json") +model SoarModuleLatest { + ...SoarModuleV1; +} + +@jsonSchema("/modules/soar-v1.json") +model SoarModuleV1 { + /** The soar module installs & integrates soar package manager, as an alternative to Homebrew / Linuxbrew. + * https://blue-build.org/reference/modules/soar/ + */ + type: "soar" | "soar@v1" | "soar@latest"; + + /** Whether to auto-upgrade all installed `soar` packages using a systemd service. */ + `auto-upgrade`?: boolean = true; + + /** Defines how often the `soar` upgrade service should run. The string is passed directly to `OnUnitInactiveSec` in systemd timer. (Syntax: ['1d', '6h', '10m']). */ + `upgrade-interval`?: string = "8h"; + + /** Whether to enable all additional repos, including official `soar` & external repos like `AM`, for installing portable AppImages & other similar formats. */ + `additional-repos`?: boolean = false; + +}