API Reference¶
How to extend, run, or edit the installer. Default flow:
guided.py:
guided() -> resolve_config (menu/save/confirm, or --silent) ->
FilesystemHandler (partition/format, before anything is mounted) ->
perform_installation (with Installer(...), steps in order).
Run a script standalone¶
python -m archinstoo --script <name> (or installed archinstoo --script
<name>). --script list prints what is available. guided is
DEFAULT_SCRIPT in
args.py.
Dispatch:
__init__.py
run_as_a_module() -> _prepare() (deps bootstrap) ->
main() (root check, host pacman.conf guard) ->
_run_script,
which just imports archinstoo.scripts.<name>. Rootless scripts skip
_prepare() and main(): run_as_a_module() calls _run_script directly.
Each script
(scripts/)
runs on import (calls its entry fn at file bottom, e.g. guided()), so
importing == running. Files starting with _ are helpers shared between
scripts, not scripts (_resolve.py).
--dry-run: build + save config, thenSystemExit(0).--config <file>/--config-url <url>: load saved selections, skip resume prompt.--silent: config is the whole input, no menu, no confirm. The script's_validate_silentfails loud on what the menu would have refused.- Only
--scriptpicks the code path.ArchConfig.scriptin the saved JSON is a label so a resume knows what produced it (get_script()). - Rootless (no root needed):
ROOTLESS_SCRIPTSincli.py.NO_DISK_SCRIPTS(same file) skip the disk deps and are refused under--script bootstrap: their target is/. - New script = new file in
scripts/that defines + calls an entry fn. Reuseget_arch_config_handler()from args.py for config/args andresolve_config(handler, show_menu)from configuration.py for the menu/save/confirm loop;show_menuis where a scriptset_enabled/set_mandatorys theGlobalMenufor its mode (live, packages, format all do this).
Add an application (audio/firewall/... category or cat/)¶
Worked example: media_codecs, a yes/no that installs a fixed set. Follow
it file by file; a single-pick or multi-select category swaps the enabled
flag for an enum, the way firewall/management do.
- models/applications.py:
MediaCodecsConfigSerialization(TypedDict, the JSON shape),MediaCodecsConfiguration(a_Categorydataclass with its one field: an enum, a bool, or a list of enums;json()/parse_arg()come from the base), then the field inApplicationSerializationandApplicationConfiguration. A choice category adds itsStrEnumfirst. - applications/cat/media_codecs.py:
packages(andservices) as@propertyliteral lists, theninstall(self, install_session, ...)using the Installer primitives (below). - application_handler.py:
if app_config.media_codecs_config and ...enabled: MediaCodecsApp().install(...). Order is install order (apps run before bootloader, after users). - application_menu.py:
a
MenuItemwithkey='media_codecs_config', its_prev_media_codecsandselect_media_codecs. Flags reuse_select_enabled/_enabled_text. The global menu's Applications preview reads these previews, nothing to add there. - schema_gen.py:
one
Sectionin install order, withpick=('media_codecs_config', 'enabled'), the key path the choice lives at underapp_config. The pick is what--script countwalks, so_resolve.pyneeds no edit. Then:
python -m archinstoo --script schema # regenerates schema.toml
./nvchecker/NVGEN gen # tracks the new packages
tests/test_schema.py fails until 5 is done: every _config_parsers key
needs a section with a pick, every pick path has to exist in the
serialization, and the committed schema.toml/nvchecker.toml must match.
Add the category to examples/config_sample_full.json so --config users
see it.
Add a profile (server or desktop)¶
No registration. Drop a file in
default_profiles/servers/
or
desktops/;
profiles_handler.py
auto-discovers via importlib over the dir.
Subclass Profile
(profile/base.py),
set ProfileType (ServerType / DesktopEnv / WindowMgr). Contract
(all optional except a type):
packages/services(@property) -> installed + enabled by the parent collector (server.py).install(install_session): extra install-time steps (e.g. sshd.py opens the firewall). Runs insideinstall_profile_config, before greeter setup; every flow.post_install(install_session): system-level finalize after packages land, no user context (e.g. mariadb runsmariadb-install-db). Runs afterinstall_profile_configin guided/live/packages; not minimal.provision(install_session, users): per-user wiring that needs the user list (e.g. docker/wayland add each user to a group,accounts.add_to_group). Runs right afterpost_installin guided/live/packages. Guided uses the configured users; live falls back to, and packages always uses, the sudo/doas invoking user (invoking_user()in models/users.py: running-system flows).do_on_select(): menu-time hook run when the profile is picked.default_greeter_type/display_servers(): graphical metadata (greeter default, Xorg/Wayland advertised).
Add a new install step (outside cat/ and profiles)¶
The general case: work that is neither a category app nor a profile. Two decoupled parts.
- The worker. A plain class/module anywhere sensible under
lib/, exposinginstall(install_session, <config it needs>)and using the Installer primitives below. Canonical example, user shells: shell.pyShellApp.install(install_session, users)installs non-bash shells thenchshper user. Nocat/entry, no menu of its own. - The hook. Call it from
perform_installationin guided.py, gated on the config it needs, at the right point in the order (ShellAppruns right aftercreate_users). Mirror into live.py if it applies to live mode, and packages.py if it concerns profiles/apps/packages/services. Each script holds its own copy of the order, there is no shared list. Either inlineWorker().install(...)(the ShellApp style) or build a handler once in the entry fn and thread it through the signature (theProfileHandler/ApplicationHandlerstyle).
Order of Operations: place the hook relative to the steps it depends on
(after users, before bootloader, ...). The Installer context isn't open
yet in the entry fn, so the hook must live inside perform_installation.
All steps that rely on a previous step must be thought of that way for exec flow...
Configure it: menu + config field¶
A menu entry only captures + persists a choice; it runs nothing at install time. To act on it you still need a step (above) that reads it.
global_menu.py:
add MenuItem(action=self._select_x, key='x') + the _select_x handler.
key must match a field on ArchConfig
(args.py)
so it round-trips through save/load. Toggle visibility with
set_enabled() / set_mandatory() (live mode disables several). Config
with no consuming step is a dead option; conversely a step can derive from
existing config and skip the menu entirely (shell is a field on each
User, set in the auth menu, never a top-level item).
The _select_x handler builds a SelectMenu
(curses_menu.py)
over a MenuItemGroup of MenuItems
(menu_item.py).
- single-select: returns
result.get_value(), e.g.select_kb_layout. - multi-select: pass
multi=True, collectresult.get_values()(often joined), e.g.select_xkb_options.
Edit an existing install step¶
Three layers. The script (perform_installation) owns step order and
gates each step on its config. Installer
(installer.py)
owns target, pacman, kernel params, fstab entries and teardown, and exposes
one thin method per step. The module named for the area does the work.
Edit the module, keep the entry, unless reordering.
Installer.__exit__ on a clean exit syncs log + config into the target
(sync_artifacts, guided also calls it before reboot) and runs
report_outcome over _helper_flags: base is set by
minimal_installation(), a flag still False is reported as a step not
reached. Scripts that never run it claim it with set_helper_flag('base',
True) (live/packages target /, format stops after mount).
| Step | Lives in |
|---|---|
| mount layout, key files, FIDO2 enrollment, fstab, snapshots | disk/ (mount, keyfiles, cryptenroll, fstab, snapshots) |
| initramfs, swap file, zram, sysctl | kernel/ |
| bootloaders, UKI, kernel cmdline | bootloader/install.py |
| users, sudo/doas, stash clone | authentication/accounts.py, authentication/stash.py |
| locale, vconsole, keyboard, timezone | localization/configure.py |
| services (enable/disable, user units, linger) | systemd.py |
| mirrors, AUR bootstrap | pm/mirrors.py, pm/aur.py |
| ISO network copy, nic files, resolved | network/network_handler.py |
Primitives reused inside any step / app / profile (on Installer):
add_additional_packages(pkgs),enable_service(name|list).arch_chroot(cmd, run_as=None): run argv list in the target.self.target(Pathof the new root),self.handler.config(fullArchConfig, e.g. readapp_config.firewall_config).- bootloader/keymap/fs:
add_bootloader(),enroll_fido2(),set_keyboard(),minimal_installation(),genfstab().