Skip to content

Commit 043898d

Browse files
authored
Merge pull request #23836 from Homebrew/install-steps-arch-token
install steps: add `{{arch}}` template token
2 parents 4e7c6a3 + 8572fb5 commit 043898d

7 files changed

Lines changed: 87 additions & 7 deletions

File tree

Library/Homebrew/cask/artifact/install_steps.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def run_steps(command, phase: :install)
5454
"context" => {
5555
"name" => cask.name,
5656
"token" => cask.token,
57+
"arch" => cask.arch,
5758
"version" => cask.version.to_s,
5859
"staged_path" => cask.staged_path.to_s,
5960
"caskroom_path" => cask.caskroom_path.to_s,

Library/Homebrew/cask_artifact.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class InstallStepsContext
2323
sig { returns(String) }
2424
attr_reader :token
2525

26+
sig { returns(T.nilable(String)) }
27+
attr_reader :arch
28+
2629
sig { returns(String) }
2730
attr_reader :version
2831

@@ -42,6 +45,7 @@ class InstallStepsContext
4245
def initialize(context)
4346
@name = T.let(context.fetch("name"), T.any(String, T::Array[String]))
4447
@token = T.let(context.fetch("token"), String)
48+
@arch = T.let(context["arch"], T.nilable(String))
4549
@version = T.let(context.fetch("version"), String)
4650
@staged_path = T.let(Pathname(context.fetch("staged_path")), Pathname)
4751
@caskroom_path = T.let(Pathname(context.fetch("caskroom_path")), Pathname)

Library/Homebrew/install_steps.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ def token
104104
"{{token}}"
105105
end
106106

107+
sig { returns(String) }
108+
def arch
109+
"{{arch}}"
110+
end
111+
107112
sig { returns(TemplateVersion) }
108113
def version
109114
TEMPLATE_VERSION
@@ -1412,6 +1417,8 @@ def template_token_value(token)
14121417
context_value(:name)&.to_s
14131418
when "name"
14141419
context_name
1420+
when "arch"
1421+
context_arch
14151422
when "token"
14161423
context_value(:token)&.to_s
14171424
when "user"
@@ -1496,6 +1503,15 @@ def context_name
14961503
value&.to_s
14971504
end
14981505

1506+
sig { returns(T.nilable(String)) }
1507+
def context_arch
1508+
# Casks without a matching `arch` stanza value expand to an empty
1509+
# string, matching the deprecated `postflight` block.
1510+
return unless @context.respond_to?(:arch)
1511+
1512+
context_value(:arch).to_s
1513+
end
1514+
14991515
sig { returns(T.nilable(String)) }
15001516
def context_version
15011517
context_value(:version)&.to_s

Library/Homebrew/rubocops/shared/install_steps_helper.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,9 @@ def allowed_step_template_node?(node)
439439

440440
send_node = T.cast(node, RuboCop::AST::SendNode)
441441
return false if send_node.arguments.present?
442-
return [:formula_name, :name, :token, :version].include?(send_node.method_name) if send_node.receiver.nil?
442+
if send_node.receiver.nil?
443+
return [:arch, :formula_name, :name, :token, :version].include?(send_node.method_name)
444+
end
443445

444446
return false unless (receiver = send_node.receiver)&.send_type?
445447

@@ -749,9 +751,9 @@ def dstr_permission_path(node)
749751
source << content.dump.delete_prefix('"').delete_suffix('"')
750752
true
751753
elsif child.begin_type? && allowed_step_template_node?(child)
752-
interpolation = "\#{#{child.source}}"
753-
path << interpolation
754-
source << interpolation
754+
# `child.source` already carries the `#{}` delimiters.
755+
path << child.source
756+
source << child.source
755757
true
756758
else
757759
false

Library/Homebrew/test/install_steps_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,38 @@ def create_executable(path)
323323
expect(written).to include("literal = {{unknown}} {single}")
324324
end
325325

326+
specify "expands the cask arch token" do
327+
context.define_singleton_method(:arch) { "-m1" }
328+
steps = Homebrew::InstallSteps::DSL.build(default_base: :var) do
329+
write_file "arch", "example#{arch}.app", append_newline: true
330+
end
331+
332+
Homebrew::InstallSteps::Runner.new(context:).run(steps)
333+
334+
expect((root/"var/arch").read).to eq("example-m1.app\n")
335+
end
336+
337+
specify "expands the cask arch token to an empty string without a matching value" do
338+
context.define_singleton_method(:arch) { nil }
339+
steps = Homebrew::InstallSteps::DSL.build(default_base: :var) do
340+
write_file "arch", "example{{arch}}.app", append_newline: true
341+
end
342+
343+
Homebrew::InstallSteps::Runner.new(context:).run(steps)
344+
345+
expect((root/"var/arch").read).to eq("example.app\n")
346+
end
347+
348+
specify "leaves the arch token verbatim for a context without an arch" do
349+
steps = Homebrew::InstallSteps::DSL.build(default_base: :var) do
350+
write_file "arch", "example{{arch}}.app", append_newline: true
351+
end
352+
353+
Homebrew::InstallSteps::Runner.new(context:).run(steps)
354+
355+
expect((root/"var/arch").read).to eq("example{{arch}}.app\n")
356+
end
357+
326358
specify "expands formula and cask identity tokens" do
327359
context.define_singleton_method(:name) { "example-formula" }
328360
context.define_singleton_method(:token) { "example-cask" }

Library/Homebrew/test/rubocops/cask/install_steps_spec.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,31 @@
492492
CASK
493493
end
494494

495+
it "autocorrects permission work using the arch template helper" do
496+
expect_offense <<~'CASK'
497+
cask "foo" do
498+
version :latest
499+
sha256 :no_check
500+
501+
postflight do
502+
^^^^^^^^^^^^^ Use `postflight_steps` for simple file preparation.
503+
set_ownership "#{staged_path}/foo-#{arch}"
504+
end
505+
end
506+
CASK
507+
508+
expect_correction <<~'CASK'
509+
cask "foo" do
510+
version :latest
511+
sha256 :no_check
512+
513+
postflight_steps do
514+
set_ownership "foo-#{arch}"
515+
end
516+
end
517+
CASK
518+
end
519+
495520
it "does not autocorrect dynamic, unsupported or mixed permission work" do
496521
expect_offense <<~'CASK'
497522
cask "foo" do
@@ -500,7 +525,7 @@
500525
501526
postflight do
502527
^^^^^^^^^^^^^ Casks must use `postflight_steps` instead of `postflight`.
503-
set_ownership "#{staged_path}/foo-#{arch}"
528+
set_ownership "#{staged_path}/foo-#{language}"
504529
end
505530
end
506531
CASK

docs/Cask-Cookbook.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,9 +670,9 @@ Ruby `#{...}` interpolation is normally evaluated before structured steps are se
670670

671671
`{{...}}` is not Ruby interpolation. It remains literal in the JSON API and the install-step runner expands supported tokens at install time. Use this form for install-time values inside `preflight_steps`, `postflight_steps`, `uninstall_preflight_steps` and `uninstall_postflight_steps`. When a path argument supports `base:`, `source_base:` or `target_base:`, prefer those options to embedding a path token.
672672

673-
The runtime steps DSL retains compatibility helpers for `token`, `name`, `version`, `version.major` and `version.major_minor`. Interpolating these helpers is safe and permitted by RuboCop because they return the corresponding `{{...}}` token text rather than a concrete value. Other Ruby interpolation is rejected. Prefer explicit `{{...}}` tokens in new steps so it is clear that expansion is deferred until installation.
673+
The runtime steps DSL retains compatibility helpers for `token`, `name`, `arch`, `version`, `version.major` and `version.major_minor`. Interpolating these helpers is safe and permitted by RuboCop because they return the corresponding `{{...}}` token text rather than a concrete value. Other Ruby interpolation is rejected. Prefer explicit `{{...}}` tokens in new steps so it is clear that expansion is deferred until installation.
674674

675-
Content, replacements, command arguments and command environments may use fixed install-time tokens. These include `{{HOMEBREW_BREW_FILE}}`, `{{HOMEBREW_CELLAR}}`, `{{HOMEBREW_PREFIX}}`, `{{token}}`, `{{name}}`, `{{user}}`, `{{staged_path}}`, `{{appdir}}`, `{{caskroom_path}}`, `{{temp}}`, `{{version}}`, `{{version.major}}` and `{{version.major_minor}}`. `{{name}}` is retained for compatibility; prefer `{{token}}`. Any other `{{...}}` is left verbatim. For example: `write_file "settings.conf", "application = {{appdir}}/Example.app"`.
675+
Content, replacements, command arguments and command environments may use fixed install-time tokens. These include `{{HOMEBREW_BREW_FILE}}`, `{{HOMEBREW_CELLAR}}`, `{{HOMEBREW_PREFIX}}`, `{{token}}`, `{{name}}`, `{{arch}}`, `{{user}}`, `{{staged_path}}`, `{{appdir}}`, `{{caskroom_path}}`, `{{temp}}`, `{{version}}`, `{{version.major}}` and `{{version.major_minor}}`. `{{name}}` is retained for compatibility; prefer `{{token}}`. `{{arch}}` expands to the value the cask's `arch` stanza selected for the current architecture, and to an empty string when the cask has no matching value. Any other `{{...}}` is left verbatim. For example: `write_file "settings.conf", "application = {{appdir}}/Example.app"`.
676676

677677
{% endraw %}
678678

0 commit comments

Comments
 (0)