This file provides guidance to Agents when working with code in this repository.
Pi-Apps is a shell-script-based app store for Raspberry Pi / ARM Linux (Debian/Ubuntu derivatives, plus Nintendo Switch Switchroot and Nvidia Jetson). There is no compiled code — the entire project is bash scripts plus a directory of per-app install/uninstall scripts and metadata. apps/ contains ~257 app subdirectories, each a self-contained package.
A full narrated reference for every script and every data/etc file is published at pi-apps.io under Development ("Documentation", "Directory Tree", "Creating an App"); source markdown is in src/wiki/development/ of github.com/Pi-Apps-Coders/website (a separate repo — check if it's cloned as a sibling directory before assuming it's available locally). Consult it for anything not covered below — it documents essentially every api function and every runtime file's exact meaning.
There is no build step and no automated test suite in the traditional sense. Validation is:
- Shellcheck — CI (
.github/workflows/shellcheck.yml) runsshellcheckagainst the core scripts and every app's install/install-32/install-64/uninstall script. Run it locally before committing:Only shellcheck errors (not warnings/info) fail CI — see theshellcheck api createapp gui install manage preload settings uninstall updater shellcheck "apps/<App Name>/install" "apps/<App Name>/uninstall"
sedfilter in that workflow for the exact severity cutoff. - Live install/uninstall test — always run this for app-script changes, don't just reason about correctness.
.github/workflows/test_build.ymlboots a real OS image in a chroot runner and does./manage install "<App>"then./manage uninstall "<App>". It's manually dispatched and works from a fork: push your branch to your own fork and trigger it from the Actions tab (test_build.yml→ "Run workflow") before considering any change to an app'sinstall/install-32/install-64/uninstallscript done. The currently-supported OS images (see Supported systems below) are pre-checked by default, so filling in the app name and running it as-is is normally all that's needed; unsupported/deprecated images (Bullseye, 32-bit Trixie) default off — leave them off unless you have a specific reason to test one. - This CI only proves the app installs/uninstalls cleanly — it does not prove the app runs or works. The chroot runner has no display/GPU, so it can't launch GUI apps or exercise real functionality. Before considering an app-script change (especially a new app) done, actually run the installed app on at least one real supported system you have physical/VM access to, and make a best-effort check (release notes, upstream changelog, reasoning about the change) for the supported systems you don't have access to.
- File permissions matter: install/install-32/install-64/uninstall scripts must be mode 775 (enforced/auto-fixed by
.github/workflows/check_PR.yml).
api— the shared function library (~4200 lines). Almost everything sources this (source "${DIRECTORY}/api"). Every function has a one-line#commentdescribing its purpose right after the{; grep those to find what's available before writing a new helper. Key groups: package management (install_packages,purge_packages,package_installed,apt_lock_wait,add_external_repo), app introspection (app_status,app_type,list_apps,script_name,will_reinstall), repo/PPA helpers (ubuntu_ppa_installer,debian_ppa_installer,repo_add/repo_refresh), UI helpers wrappingyad(userinput_func,multi_install_gui), system detection (is_supported_system,get_model,get_codename), and generic wrappers aroundwget/chmod/unzip/nprocthat add status output.manage— CLI/engine that actually performs install/uninstall/update/refresh for one or more apps (./manage install "App Name"). This is what CI's test workflow drives directly. Modes:install,uninstall,install-if-not-installed(for one app depending on another, e.g. Wine depending on Box86),multi-install/multi-uninstall(newline-separated app lists),check-all(diff localapps/against the freshly pulledupdate/pi-apps/apps/to list updatable apps),update/update-all.gui— the yad-based graphical app store front end (main window, categories, search, app details).preload— generates the yad-formatted app list shown in the GUI, with its own change-detection/caching (mktimestamps) so it doesn't regenerate needlessly.updater— checks theupdate/pi-appsclone (a git checkout of upstream master underupdate/pi-apps/) against the local copy to find app/script updates, both GUI (updater gui) and CLI (updater cli) modes.createapp— wizard for scaffolding a new app directory interactively (yad dialogs).settings— reads/writesdata/settings/<Setting Name>files based on definitions inetc/setting-params/.install/uninstall— bootstrap scripts for installing/removing Pi-Apps itself on a system (apt dependency install,~/pi-appssetup), not for individual apps.etc/— supporting data/scripts, kept up to date by updates (unlikedata/):categories(the global, canonical app→category mapping — see below),setting-params/(one file per setting: first uncommented line is the default, subsequent lines are other allowed values,#-prefixed line is the tooltip),runonce-entries(one-time migrations executed via therunonce()api function, hash-tracked indata/runonce_hashesso each entry runs at most once per user),categoryedit(CLI to move an app to a category, writes todata/category-overrides),import-app,terminal-run(runs a newline-separated command list in a new terminal window across all supported terminal emulators, blocking until the terminal closes — used instead of hand-rolling terminal detection),preload-daemon,genapplist-yad(.c)(a small C helperpreloadauto-compiles for speed, falling back to bash if compilation fails).data/— user-local runtime state that updates must never touch:data/status/<App>(installed/uninstalled/corrupted/disabled),data/settings/<Name>,data/category-overrides(per-user overrides layered on top ofetc/categories),data/update-status,data/preload,data/runonce_hashes.
Two kinds of apps, distinguished by app_type() in api:
- Standard app — has an
installscript, orinstall-32/install-64, or both, plus anuninstallscript.install(no suffix) means the same script handles both CPU architectures; it can read the$archvariable ("32"or"64") to branch internally.install-32/install-64are architecture-specific alternatives to that — if onlyinstall-64exists, the app is only ever shown/installable on 64-bit systems (and vice versa forinstall-32). Scripts are plain bash sourced with allapifunctions available (script_name_cpu()/script_name()inapidetermine which script applies). Seeapps/template/for the canonical skeleton and inline guidance. - Package app — has a
packagesfile instead of install scripts: one or more existing apt package names (supports|as an "any of these" separator, seepkgapp_packages_required()inapi). No install/uninstall scripts needed; Pi-Apps handles it generically as a thin wrapper. Per project convention, package-apps should only be added to complement an existing category of script-apps, not stand alone in a new category — Pi-Apps intentionally isn't "a GUI for apt".
Every app also has: description (first line = tooltip), credits, website, icon-24.png, icon-64.png.
Category membership lives in etc/categories (global, kept in sync with upstream) as App Name|Category[/Subcategory] lines; per-user overrides layer on top from data/category-overrides, written by etc/categoryedit. Don't hand-edit category assignment inside an app directory — there's no such file there.
- Never call
apt/apt-get/dpkgdirectly. Useinstall_packages pkg1 pkg2 ... || exit 1ininstall, andpurge_packages || exit 1inuninstall.install_packagesbuilds a per-app dummy.debthat depends on the requested packages, sopurge_packagescan safely autoremove exactly what this app pulled in — without touching packages the user already had or that another app also depends on. Calling apt directly breaks that accounting. - Use
error "message"(fromapi) after||to abort with a clear red message on any command that must succeed; skiperrorfor genuinely optional/best-effort cleanup commands. uninstallmust undo everythinginstalldid — except it must never delete user-generated data/config (e.g. don't wipe a Minecraft world just because the app is being reinstalled/updated).- For downloading a git repo, prefer the
git_clonewrapper over baregit clone(removes the destination dir first, suppresses noisy output, surfaces errors clearly).wgetis itself intercepted by anapifunction that transparently upgrades toaria2cwhen possible — no code changes needed to benefit from it. - Branch on OS/arch using
api-provided context ($__os_id,$__os_release,$arch,get_codename,get_model) rather than re-deriving it; seeapps/PrusaSlicer/install-32for the idiom.
The same codebase targets Raspberry Pi OS, Ubuntu on Pi, Nvidia Jetson, and Nintendo Switch Switchroot L4T (see Supported systems below). Check existing apps for the idiom before adding new OS/arch-conditional logic.
Fully supported: Raspberry Pi OS (32-bit Bookworm, 64-bit Bookworm, 64-bit Trixie) and Ubuntu Jammy/Noble on Raspberry Pi 2 v1.2+/Zero 2 W/3/4/5; Switchroot L4T Ubuntu Jammy/Noble on Nintendo Switch; Nvidia Jetpack 6 (Jammy)/7 (Noble) on Jetson. This list changes as distros age out (e.g. Bullseye and 32-bit Trixie have both been dropped) — check the "Supported systems"/"Unsupported systems" section of README.md for the current matrix, and is_supported_system() in api for the runtime enforcement logic.
.github/workflows/update_apps.yml runs per-app updater scripts in .github/workflows/updates/<App Name>.sh on a schedule to bump download URLs/versions in that app's install script — when bumping an app version by hand, check whether a corresponding updater script already exists in .github/workflows/updates/ and should also be kept in sync. These updater scripts have access to helpers not available to normal install scripts (e.g. get_release owner/repo to fetch a GitHub repo's latest release tag via update_github_script.sh, or update_debian_repo_script.sh for apps published to a Debian-style repo); the corresponding app's install/install-32/install-64 must define a version (or filepath/filepath_32/filepath_64) variable for the updater to substitute into. See Creating-an-app.md / DOCUMENTATION.md in the website docs for worked examples.