Build Pipeline
Shanios images are produced by a multi-stage build pipeline that takes a package list, an overlay filesystem, and a profile-specific customization script, and outputs a compressed Btrfs send-stream (.zst), an ISO, or both. The entire pipeline runs inside a privileged Docker container so the host system is never modified.
Overview
The pipeline is driven by build.sh in shani-install-media. It dispatches to specialized sub-scripts for each stage:
build.sh <command> -p <profile>
Commands
| Command | What it does | Output |
|---|---|---|
image | Pacstraps packages, applies overlay, runs customization, snapshots to .zst | <date>/<os>-<date>-<profile>.zst + .sha256 + .asc |
flatpak | Builds a Flatpak image from flatpak-packages.txt | flatpakfs.zst |
snap | Builds a Snap seed image from snap-packages.txt | snapfs.zst |
iso | Runs mkarchiso with base + optional Flatpak/Snap images | <date>/<os>-<date>-<profile>.iso |
repack | Signs the ISO for Secure Boot and generates torrent | signed_<os>-<date>-<profile>.iso + .torrent |
upload | Uploads artifacts to SourceForge and Cloudflare R2 | Remote artifacts |
release | Creates latest.txt or stable.txt channel manifests | Channel files |
promote-stable | Promotes current latest to stable after verification | Updated channel files |
verify | Checks remote artifacts against local checksums | Pass/fail |
test | Installs/boots/updates/rolls back on loop-mounted disks | Test report |
Compound Commands
| Command | Pipeline |
|---|---|
all | image → release latest → upload image |
full | image → flatpak → snap → iso → repack → release latest → upload all |
iso-only | Download from R2 → build ISO → repack → upload iso |
publish | release → upload (for promoting pre-released artifacts) |
Build Stages
1. Base Image (build-base-image.sh)
The core stage. Creates a Btrfs subvolume, pacstraps the package list from image_profiles/<profile>/Packages-{Base,Desktop,Extras}, applies the shared overlay (base configs, systemd units), then runs the profile-specific customization script:
gnome-customization.sh— enables GNOME services, installs GNOME-specific Flatpaksplasma-customization.sh— enables KDE services, Kvantum theme overridecosmic-customization.sh— enables COSMIC serviceskiosk-customization.sh— enables Cage + labwc, configures unattended GDM autologin, mounts tmpfs homeserver-customization.sh— enables cloud-init, sshd, firewalld, disables GUI services
The result is snapshotted as a Btrfs send-stream and compressed with Zstandard.
2. Flatpak Image (build-flatpak-image.sh)
If flatpak-packages.txt exists for the profile, this stage creates a separate Btrfs subvolume, installs all listed Flatpaks, and produces a flatpakfs.zst that is bundled into the ISO.
3. Snap Image (build-snap-image.sh)
Same pattern as Flatpak — if snap-packages.txt exists, builds a snapfs.zst with pre-installed snaps.
4. ISO (build-iso.sh)
Runs mkarchiso using the base image and optional Flatpak/Snap images as source. The --from-r2 flag allows building the ISO without the base image locally — it downloads from Cloudflare R2 instead.
5. Repack (repack-iso.sh)
Signs the ISO with sbsign for Secure Boot, generates .sha256 and .asc files, and creates a BitTorrent file for distribution.
6. Upload (upload.sh)
Pushes artifacts to SourceForge (primary download) and Cloudflare R2 (build cache and CDN). Uses rclone for R2 and rsync/sftp over SSH for SourceForge (there is no lftp in the upload script).
Image Profiles
Each profile has its own directory under image_profiles/:
| Profile | Directory | Desktop | Package count |
|---|---|---|---|
gnome | image_profiles/gnome/ | GNOME | 25 meta-packages + Flatpaks |
plasma | image_profiles/plasma/ | KDE Plasma | 25 meta-packages + Flatpaks |
cosmic | image_profiles/cosmic/ | COSMIC | 25 meta-packages + Flatpaks |
kiosk | image_profiles/kiosk/ | Cage + labwc (Firefox) | 17 packages |
server | image_profiles/server/ | None (headless) | 170 explicit packages |
Note: The "25 meta-packages" for gnome/plasma/cosmic are defined inimage_profiles/<profile>/Packages-{Base,Desktop,Extras}. Each meta-package (e.g.,shani-core,shani-desktop-gnome) pulls in many transitive dependencies viashani-pkgbuildsPKGBUILDs and.installscripts, resulting in hundreds of total packages installed on the final image.
Shared packages across all profiles are in image_profiles/shared/.
Docker Environment
All build steps run inside the shrinivasvkumbhar/shani-builder Docker image (built from shani-builder/docker/). The container is privileged because image assembly requires Btrfs operations, loop mounts, and chroot — all of which need host-level access.
See Fleet Deployment for how OEMs use this pipeline to build custom images.
CI/CD
Two GitHub Actions workflows in shani-builder automate the pipeline:
build-image.yml— builds the OS image on push tomainor manual dispatchpromote-stable.yml— promoteslatesttostableafter verification
Both use AWS OIDC for authentication (no long-lived keys).