JSON API Postinstall/Preflight/Postflight Plan
This plan tracks repeated Ruby-only install behaviours that can be expressed as structured DSL data and exposed through the JSON APIs.
Install step data is stored as an ordered array of step hashes. Ruby hashes preserve insertion order, but the outer array makes JSON ordering explicit for API consumers in any language.
The first implemented high-level DSL is named steps, exposed as
post_install_steps for formulae and as preflight_steps, postflight_steps,
uninstall_preflight_steps and uninstall_postflight_steps for casks. The
blocks are deliberately narrow: they may only contain literal calls to the
step DSL, with no wider Ruby execution and no access to the surrounding formula
or cask DSL.
The initial step methods shadow common FileUtils naming where practical:
mkdir/mkdir_p, touch, move/mv, move_children and
symlink/ln_s/ln_sf. Formula steps default mkdir and touch paths to
var, and source/target paths to prefix. Cask steps default base,
source_base and target_base to staged_path.
RuboCop checks reject formulae or casks that define both a legacy Ruby block and the matching steps block. Runtime handling follows the same precedence: steps run if present; the legacy block is ignored with a warning. Post-install or postflight steps are not sandboxed for this iteration because they run only Homebrew-owned structured operations. The runner shape leaves room to sandbox future step types that invoke non-Homebrew code.
RuboCop autocorrection converts the simplest existing post_install and
*flight Ruby blocks to steps blocks when every statement is a supported file
preparation operation with literal paths and known bases. Future post-install
and *flight DSLs should include the same style of conservative autocorrection
from the matching legacy Ruby pattern where possible.
Formula Patterns
Local scan source: homebrew/core at fb0ca6682b4.
178of8,359formulae definepost_install.73create shared directories invar,etcorHOMEBREW_PREFIX. Examples:glib,languagetool,mecab.71write or patch default configuration/data files. Examples:node@24,wemux, PHP formulae.35rebuild desktop/cache databases. Examples:gjs,geocode-glib,efl.27initialise service data directories. Examples:postgresql@14,mysql, MariaDB formulae.12update certificate/trust state. Examples:openssl@3,libressl,gnutls.9only touch marker or lock files. Examples:icecast,nethack,r.
Cask Patterns
Local scan source: homebrew/cask at 4ed4e04eaa5.
204of7,646casks currently require the Ruby source at install time:181because of flight blocks and23because of language blocks only.78flight blocks create directories, touch files or write small files. Examples:86box,autogram,dante-via.27move, copy or symlink files during install or uninstall. Examples:klayout,libcblite,docker-desktop.23change permissions and37change ownership. Examples:bitcoin-core,anaconda,parallels.16invoke/usr/bin/securityfor keychain certificate cleanup. Examples:charles,autofirma,betwixt.27casks use language blocks. Large examples includefirefox,libreoffice-language-packandthunderbird.
Install Step Examples
languagetool:post_install_steps { mkdir "log/languagetool", base: :var }.icecast:post_install_stepswith onemkdirand twotouchsteps undervar/"log/icecast".openssl@3:post_install_stepswith a forcedsymlinkfromca-certificatespkgetc/"cert.pem"into the formulapkgetc.86box:preflight_stepswith a home-directorymkdirfor the shared ROM directory.klayout:preflight_stepswithmove_childrenfrom the staged root into the nestedKLayoutdirectory.libcblite:postflight_stepswith relativesymlinksteps marked for uninstall cleanup.
Implementation Checklist
- [x] PR 1, shared install steps framework.
Commit:
Add install steps framework. Scope: shared ordered step data, a confined steps DSL, a shared runner, cask stanza ordering, RuboCop registration, conflict checks and the refactor plan. This PR does not wire formula or cask JSON API output or run steps from install phases. Estimated existing formulae/casks affected:0runtime behaviour changes. It creates the guardrails for the later178formulae withpost_installblocks and181casks with flight blocks, but no existing formula or cask opts into the new DSL yet. Notes for the next PRs: keep the step payload as an ordered array; keep_stepsblocks literal-only; when a phase gets wired in, add the runtime warning that steps win over the legacy Ruby block; add conservative autocorrection only where every legacy statement maps mechanically. - [ ] PR 2, formula
post_install_steps. Commit:Add formula install steps. Scope: formula DSL, formula JSON API data, API formula loading, installer andbrew postinstallexecution, formula cookbook docs, formula fixture and formula-specific autocorrection. Estimated existing formulae affected:178formulae currently definepost_install. The first useful conversion surface is roughly73formulae creating shared directories and9touching marker or lock files; parts of the27service data directory and12certificate/trust formulae may also move once their operations fit the supported step set. Runtime behaviour changes only for formulae that opt intopost_install_steps. Notes for implementation: defaultmkdir/touchtovarand source/target paths toprefix; expose the ordered array throughFormulaStruct; makepost_install_stepstake precedence overpost_install; document that the two forms must not be mixed. - [ ] PR 3, cask flight steps.
Commit:
Add cask install steps. Scope: cask artifacts forpreflight_steps,postflight_steps,uninstall_preflight_stepsanduninstall_postflight_steps, cask API serialisation through artifact data, installer casts, cask cookbook docs, cask fixture/API loader coverage and cask-specific autocorrection. Estimated existing casks affected:181casks currently use flight blocks. The first useful conversion surface is roughly78casks that create/touch files or directories and the supported subset of27casks that move or symlink files. Runtime behaviour changes only for casks that opt into the new*_stepsstanzas. Notes for implementation: default all relative cask paths tostaged_path; keep steps as normal cask artifacts so API loader round-trips work; make steps remove/override the matching Ruby flight artifact with a warning; keepuninstall: truesymlink cleanup available for install-phase steps. - [ ] PR 4, desktop and cache rebuild actions.
Estimated existing formulae/casks affected: about
35formulae run rebuild tools such asglib-compile-schemas,gtk*-update-icon-cache,gio-querymodules,gdk-pixbuf-query-loaders,update-mime-databaseandupdate-desktop-database; no cask count was identified in the initial scan. Notes for implementation: add named action types rather than raw commands; define idempotence and failure handling; include RuboCop autocorrection for exact known command patterns only; decide whether any action invokes non-Homebrew code and should be ready for future sandboxing. - [ ] PR 5, default config and template writes.
Estimated existing formulae/casks affected: about
71formulae write or patch default configuration/data files, and a subset of the78file-prep cask flight blocks write small files. Notes for implementation: use scoped token expansion instead of arbitrary Ruby interpolation; require literal templates or API-safe template data; define overwrite,unless_existsand upgrade semantics before adding autocorrection. - [ ] PR 6, database and service data directory initialisation.
Estimated existing formulae/casks affected: about
27formulae initialise service data directories. Notes for implementation: modelunless_exists, CI skip semantics, ownership/permission needs and service user assumptions explicitly. Keep shell-outs out of the DSL until the sandbox story is decided. - [ ] PR 7, certificate and trust store actions.
Estimated existing formulae/casks affected: about
12formulae update certificate/trust state and16casks invoke/usr/bin/securityfor keychain certificate cleanup. Notes for implementation: separate formula-owned symlinked certificate actions from keychain mutations; keychain work likely counts as non-Homebrew code and should be prepared for sandbox policy decisions. - [ ] PR 8, cask permission and ownership actions.
Estimated existing casks affected: about
23casks change permissions and37change ownership. Notes for implementation: match the existing flight mini-DSLset_permissionsandset_ownershipsemantics first; define sudo/root requirements and uninstall behaviour before adding API output. - [ ] PR 9, cask language variations in API data.
Estimated existing casks affected:
27casks use language blocks, with large examples includingfirefox,libreoffice-language-packandthunderbird. Notes for implementation: represent language-specific URLs, checksums and returned values without evaluating cask Ruby before fetch; keep the public API shape friendly to clients that need to choose one language deterministically.