Skip to content

Latest commit

 

History

History
69 lines (46 loc) · 11.2 KB

File metadata and controls

69 lines (46 loc) · 11.2 KB

AGENTS.md

This file provides guidance to Agents when working with code in this repository.

What this is

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.

Validating changes

There is no build step and no automated test suite in the traditional sense. Validation is:

  • Shellcheck — CI (.github/workflows/shellcheck.yml) runs shellcheck against the core scripts and every app's install/install-32/install-64/uninstall script. Run it locally before committing:
    shellcheck api createapp gui install manage preload settings uninstall updater
    shellcheck "apps/<App Name>/install" "apps/<App Name>/uninstall"
    Only shellcheck errors (not warnings/info) fail CI — see the sed filter 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.yml boots 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's install/install-32/install-64/uninstall script 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).

Architecture

Top-level scripts (all bash, run from repo root, expect DIRECTORY = repo root)

  • api — the shared function library (~4200 lines). Almost everything sources this (source "${DIRECTORY}/api"). Every function has a one-line #comment describing 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 wrapping yad (userinput_func, multi_install_gui), system detection (is_supported_system, get_model, get_codename), and generic wrappers around wget/chmod/unzip/nproc that 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 local apps/ against the freshly pulled update/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 the update/pi-apps clone (a git checkout of upstream master under update/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/writes data/settings/<Setting Name> files based on definitions in etc/setting-params/.
  • install / uninstall — bootstrap scripts for installing/removing Pi-Apps itself on a system (apt dependency install, ~/pi-apps setup), not for individual apps.
  • etc/ — supporting data/scripts, kept up to date by updates (unlike data/): 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 the runonce() api function, hash-tracked in data/runonce_hashes so each entry runs at most once per user), categoryedit (CLI to move an app to a category, writes to data/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 helper preload auto-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 of etc/categories), data/update-status, data/preload, data/runonce_hashes.

App directory format (apps/<App Name>/)

Two kinds of apps, distinguished by app_type() in api:

  • Standard app — has an install script, or install-32/install-64, or both, plus an uninstall script. install (no suffix) means the same script handles both CPU architectures; it can read the $arch variable ("32" or "64") to branch internally. install-32/install-64 are architecture-specific alternatives to that — if only install-64 exists, the app is only ever shown/installable on 64-bit systems (and vice versa for install-32). Scripts are plain bash sourced with all api functions available (script_name_cpu()/script_name() in api determine which script applies). See apps/template/ for the canonical skeleton and inline guidance.
  • Package app — has a packages file instead of install scripts: one or more existing apt package names (supports | as an "any of these" separator, see pkgapp_packages_required() in api). 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.

Writing install/uninstall scripts — required conventions

  • Never call apt/apt-get/dpkg directly. Use install_packages pkg1 pkg2 ... || exit 1 in install, and purge_packages || exit 1 in uninstall. install_packages builds a per-app dummy .deb that depends on the requested packages, so purge_packages can 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" (from api) after || to abort with a clear red message on any command that must succeed; skip error for genuinely optional/best-effort cleanup commands.
  • uninstall must undo everything install did — 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_clone wrapper over bare git clone (removes the destination dir first, suppresses noisy output, surfaces errors clearly). wget is itself intercepted by an api function that transparently upgrades to aria2c when 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; see apps/PrusaSlicer/install-32 for the idiom.

Multi-target support

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.

Supported systems

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.

Automated app-version updates

.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.