diff --git a/Library/Homebrew/install_steps.rb b/Library/Homebrew/install_steps.rb index b75c5308ee7f2..07ccb808738ec 100644 --- a/Library/Homebrew/install_steps.rb +++ b/Library/Homebrew/install_steps.rb @@ -168,6 +168,36 @@ def ln_sf(source, target, source_base: nil, target_base: nil, source_formula: ni symlink(source, target, source_base:, target_base:, source_formula:, target_formula:, force: true, uninstall:) end + sig { void } + def compile_gsettings_schemas + add_rebuild_action("compile_gsettings_schemas", "share/glib-2.0/schemas") + end + + sig { void } + def gio_querymodules + add_rebuild_action("gio_querymodules", "lib/gio/modules") + end + + sig { void } + def gdk_pixbuf_query_loaders + add_step("gdk_pixbuf_query_loaders") + end + + sig { void } + def gtk_update_icon_cache + add_rebuild_action("gtk_update_icon_cache", "share/icons/hicolor") + end + + sig { void } + def update_mime_database + add_rebuild_action("update_mime_database", "share/mime") + end + + sig { void } + def update_desktop_database + add_rebuild_action("update_desktop_database", "share/applications") + end + private sig { params(type: ::String, fields: ::T.untyped).void } @@ -177,6 +207,11 @@ def add_step(type, **fields) @steps << ::T.cast(::Utils.deep_compact_blank(step), Step) end + sig { params(type: ::String, path: ::String).void } + def add_rebuild_action(type, path) + add_step(type, "path" => path_spec(path, base: :homebrew_prefix)) + end + sig { params( path: ::T.any(::String, ::Pathname), @@ -255,6 +290,18 @@ def run_install_step(step) target.dirname.mkpath FileUtils.rm_f target if step["force"] == true File.symlink link_source(step.fetch("source")), target + when "compile_gsettings_schemas" + run_formula_tool("glib", "glib-compile-schemas", resolve_path(step.fetch("path"))) + when "gio_querymodules" + run_formula_tool("glib", "gio-querymodules", resolve_path(step.fetch("path"))) + when "gdk_pixbuf_query_loaders" + run_formula_tool("gdk-pixbuf", "gdk-pixbuf-query-loaders", "--update-cache") + when "gtk_update_icon_cache" + run_formula_tool("gtk+3", "gtk3-update-icon-cache", "-q", "-t", "-f", resolve_path(step.fetch("path"))) + when "update_mime_database" + run_formula_tool("shared-mime-info", "update-mime-database", resolve_path(step.fetch("path"))) + when "update_desktop_database" + run_formula_tool("desktop-file-utils", "update-desktop-database", resolve_path(step.fetch("path"))) else raise ArgumentError, "unknown install step: #{step.fetch("type")}" end @@ -299,6 +346,11 @@ def normalise_path_spec(spec) end end + sig { params(formula: String, executable: String, args: T.untyped).void } + def run_formula_tool(formula, executable, *args) + @context.send(:safe_system, Formula[formula].opt_bin/executable, *args) + end + sig { params(base: String, formula: T.nilable(String)).returns(Pathname) } def root_path(base, formula) case base diff --git a/Library/Homebrew/json_api_postinstall_preflight_postflight_plan.md b/Library/Homebrew/json_api_postinstall_preflight_postflight_plan.md index 8792d6cd869b9..6ce37db00c0a9 100644 --- a/Library/Homebrew/json_api_postinstall_preflight_postflight_plan.md +++ b/Library/Homebrew/json_api_postinstall_preflight_postflight_plan.md @@ -29,12 +29,20 @@ future step types that invoke non-Homebrew code. Future cask work should sandbox all `*flight` run scripts from non-Homebrew and non-system sources, for example scripts shipped by upstream artifacts. +For each future operation type, check `homebrew/core` and `homebrew/cask` +separately and add formula support only when needed by `homebrew/core` or cask +support only when needed by `homebrew/cask`. + 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. +Before opening follow-up PRs, run `bundle exec rake lint` from `docs/` to catch +markdown lint issues and run `brew style homebrew/core homebrew/cask` to catch +tap-wide formula or cask opportunities exposed by the new DSLs. + ## Formula Patterns Local scan source: `homebrew/core` at `fb0ca6682b4`. @@ -161,15 +169,19 @@ be resolved before the download can be enqueued. `uninstall: true` symlink cleanup available for install-phase steps. Keep the tap-wide autocorrect audit in a follow-up commit so the implementation can land before converted casks. -- [ ] PR 4, desktop and cache rebuild actions. +- [x] PR 4, desktop and cache rebuild actions. Estimated existing formulae/casks affected: about `35` formulae run rebuild tools such as `glib-compile-schemas`, `gtk*-update-icon-cache`, `gio-querymodules`, `gdk-pixbuf-query-loaders`, `update-mime-database` and `update-desktop-database`; no cask count was identified in the initial scan. + Scope: shared named action types for GSettings schemas, GIO modules, + GDK Pixbuf loaders, GTK icon caches, MIME databases and desktop databases, + runner dispatch through Homebrew-owned tools and docs. 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. + define idempotence and failure handling; decide whether any action invokes + non-Homebrew code and should be ready for future sandboxing. Land RuboCop + autocorrection and tap-wide conversions in a separate follow-up after the + new DSL methods are available in a stable Homebrew release. - [ ] PR 5, default config and template writes. Estimated existing formulae/casks affected: about `71` formulae write or patch default configuration/data files, and a subset of the `78` file-prep diff --git a/Library/Homebrew/test/install_steps_spec.rb b/Library/Homebrew/test/install_steps_spec.rb index 5bf65298dbec2..f3e2a93122b5d 100644 --- a/Library/Homebrew/test/install_steps_spec.rb +++ b/Library/Homebrew/test/install_steps_spec.rb @@ -96,6 +96,37 @@ ) end + specify "runs named desktop and cache rebuild actions" do + steps = Homebrew::InstallSteps::DSL.build do + compile_gsettings_schemas + gio_querymodules + gdk_pixbuf_query_loaders + gtk_update_icon_cache + update_mime_database + update_desktop_database + end + + formula = instance_double(Formula, opt_bin: root/"opt/bin") + allow(Formula).to receive(:[]).with("glib").and_return(formula) + allow(Formula).to receive(:[]).with("gdk-pixbuf").and_return(formula) + allow(Formula).to receive(:[]).with("gtk+3").and_return(formula) + allow(Formula).to receive(:[]).with("shared-mime-info").and_return(formula) + allow(Formula).to receive(:[]).with("desktop-file-utils").and_return(formula) + expect(context).to receive(:safe_system).with(root/"opt/bin/glib-compile-schemas", + HOMEBREW_PREFIX/"share/glib-2.0/schemas").ordered + expect(context).to receive(:safe_system).with(root/"opt/bin/gio-querymodules", + HOMEBREW_PREFIX/"lib/gio/modules").ordered + expect(context).to receive(:safe_system).with(root/"opt/bin/gdk-pixbuf-query-loaders", "--update-cache").ordered + expect(context).to receive(:safe_system).with(root/"opt/bin/gtk3-update-icon-cache", "-q", "-t", "-f", + HOMEBREW_PREFIX/"share/icons/hicolor").ordered + expect(context).to receive(:safe_system).with(root/"opt/bin/update-mime-database", + HOMEBREW_PREFIX/"share/mime").ordered + expect(context).to receive(:safe_system).with(root/"opt/bin/update-desktop-database", + HOMEBREW_PREFIX/"share/applications").ordered + + Homebrew::InstallSteps::Runner.new(context:).run(steps) + end + specify "does not add the default base to home paths" do steps = Homebrew::InstallSteps::DSL.build(default_base: :var) do mkdir_p "~/example" diff --git a/docs/Cask-Cookbook.md b/docs/Cask-Cookbook.md index a8b02ebc64a58..244bd1ae4adcb 100644 --- a/docs/Cask-Cookbook.md +++ b/docs/Cask-Cookbook.md @@ -577,7 +577,21 @@ postflight_steps do end ``` -`mkdir`, `mkdir_p`, `touch`, `move`, `mv`, `move_children`, `symlink`, `ln_s` and `ln_sf` are available in steps blocks. Relative paths default to `staged_path` for `base:`, `source_base:` and `target_base:`. Symlink steps can use `uninstall: true` to remove the symlink during uninstall. A steps block may only contain those step calls with literal arguments; it cannot call the wider cask DSL or arbitrary Ruby code. Each phase may define either its Ruby flight block or its matching steps block, not both. +A steps block may only contain supported step calls with literal arguments; it cannot call the wider cask DSL or arbitrary Ruby code. Each phase may define either its Ruby flight block or its matching steps block, not both. + +#### File preparation steps + +Relative paths default to `staged_path` for `base:`, `source_base:` and `target_base:`. Symlink steps can use `uninstall: true` to remove the symlink during uninstall. + +* `mkdir`: create one directory; example: `mkdir "Shared"`. +* `mkdir_p`: create a directory and any missing parents; example: `mkdir_p "Shared"`. +* `touch`: create or update a file timestamp; example: `touch "Shared/state"`. +* `move`: move one file or directory; example: `move "payload", "Shared/payload"`. +* `mv`: alias for `move`; example: `mv "payload", "Shared/payload"`. +* `move_children`: move the contents of one directory into another; example: `move_children "payload", "Shared/payload"`. +* `symlink`: create a symlink; example: `symlink "Shared/payload", "Payload", source_base: :relative`. +* `ln_s`: alias for `symlink`; example: `ln_s "Shared/payload", "Payload", source_base: :relative`. +* `ln_sf`: create or replace a symlink; example: `ln_sf "Shared/payload", "Payload", source_base: :relative, uninstall: true`. Flight blocks are not currently run in the cask sandbox. They should be written as though they may be sandboxed in the future: prefer the mini-DSL helpers below and keep filesystem writes limited to paths owned by the cask. diff --git a/docs/Formula-Cookbook.md b/docs/Formula-Cookbook.md index ebc1d9d1f3f1e..10f4f97b58bc9 100644 --- a/docs/Formula-Cookbook.md +++ b/docs/Formula-Cookbook.md @@ -1116,7 +1116,32 @@ class Foo < Formula end ``` -`mkdir`, `mkdir_p` and `touch` default to paths relative to `var`. `move`, `mv`, `move_children`, `symlink`, `ln_s` and `ln_sf` default their source and target paths to `prefix`. Use `base:`, `source_base:` or `target_base:` when a step needs another formula path such as `pkgetc`; use `source_base: :relative` for relative symlink sources. A formula may define either `post_install_steps` or `post_install`, not both. +A formula may define either `post_install_steps` or `post_install`, not both. + +#### File preparation steps + +`mkdir`, `mkdir_p` and `touch` default to paths relative to `var`. `move`, `mv`, `move_children`, `symlink`, `ln_s` and `ln_sf` default their source and target paths to `prefix`. Use `base:`, `source_base:` or `target_base:` when a step needs another formula path such as `pkgetc`; use `source_base: :relative` for relative symlink sources. + +* `mkdir`: create one directory; example: `mkdir "log/foo"`. +* `mkdir_p`: create a directory and any missing parents; example: `mkdir_p "log/foo"`. +* `touch`: create or update a file timestamp; example: `touch "foo/state"`. +* `move`: move one file or directory; example: `move "default.conf", "foo/default.conf"`. +* `mv`: alias for `move`; example: `mv "default.conf", "foo/default.conf"`. +* `move_children`: move the contents of one directory into another; example: `move_children "defaults", "foo/defaults"`. +* `symlink`: create a symlink; example: `symlink "cert.pem", "foo/cert.pem", source_base: :relative`. +* `ln_s`: alias for `symlink`; example: `ln_s "cert.pem", "foo/cert.pem", source_base: :relative`. +* `ln_sf`: create or replace a symlink; example: `ln_sf "cert.pem", "foo/cert.pem", source_base: :relative`. + +#### Desktop and cache rebuild steps + +These steps rebuild shared desktop and cache state using Homebrew-owned tools. + +* `compile_gsettings_schemas`: compile GSettings schemas in `share/glib-2.0/schemas`. +* `gio_querymodules`: rebuild the GIO module cache in `lib/gio/modules`. +* `gdk_pixbuf_query_loaders`: update the GDK Pixbuf loader cache. +* `gtk_update_icon_cache`: refresh the `hicolor` GTK icon cache. +* `update_mime_database`: rebuild the shared MIME database in `share/mime`. +* `update_desktop_database`: rebuild the desktop entry database in `share/applications`. ```ruby class Foo < Formula