Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 80 additions & 74 deletions Library/Homebrew/cmd/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class InstallCmd < AbstractCommand
"or a shell inside the temporary build directory."
switch "--display-times",
description: "Print install times for each package at the end of the run.",
env: :display_install_times
env:         :display_install_times
switch "-f", "--force",
description: "Install formulae without checking for previously installed keg-only or " \
"non-migrated versions. When installing casks, overwrite existing files " \
Expand All @@ -48,14 +48,14 @@ class InstallCmd < AbstractCommand
switch "--ask",
description: "Ask for confirmation before downloading and installing formulae. " \
"Print download and install sizes of bottles and dependencies.",
env: :ask
env:         :ask
[
[:switch, "--formula", "--formulae", {
description: "Treat all named arguments as formulae.",
}],
[:flag, "--env=", {
description: "Disabled other than for internal Homebrew use.",
hidden: true,
hidden:      true,
}],
[:switch, "--ignore-dependencies", {
description: "An unsupported Homebrew development option to skip installing any dependencies of any " \
Expand Down Expand Up @@ -97,7 +97,7 @@ class InstallCmd < AbstractCommand
description: "Retain the temporary files created during installation.",
}],
[:switch, "--debug-symbols", {
depends_on: "--build-from-source",
depends_on:  "--build-from-source",
description: "Generate debug symbols on build. Source will be retained in a cache directory.",
}],
[:switch, "--build-bottle", {
Expand All @@ -114,7 +114,7 @@ class InstallCmd < AbstractCommand
description: "Install but mark as installed as a dependency and not installed on request.",
}],
[:flag, "--bottle-arch=", {
depends_on: "--build-bottle",
depends_on:  "--build-bottle",
description: "Optimise bottles for the specified architecture rather than the oldest " \
"architecture supported by the version of macOS the bottles are built on.",
}],
Expand All @@ -129,6 +129,10 @@ class InstallCmd < AbstractCommand
[:switch, "--overwrite", {
description: "Delete files that already exist in the prefix while linking.",
}],
# ADDED FOR DEPENDENCY COOLDOWN
[:switch, "--no-cooldown", {
description: "Skip the dependency cooldown delay, installing immediately to bypass the security check.",
}],
].each do |args|
options = args.pop
send(*args, **options)
Expand All @@ -141,14 +145,14 @@ class InstallCmd < AbstractCommand
}],
[:switch, "--[no-]binaries", {
description: "Disable/enable linking of helper executables (default: enabled).",
env: :cask_opts_binaries,
env:         :cask_opts_binaries,
}],
[:switch, "--require-sha", {
description: "Require all casks to have a checksum.",
env: :cask_opts_require_sha,
env:         :cask_opts_require_sha,
}],
[:switch, "--[no-]quarantine", {
env: :cask_opts_quarantine,
env:         :cask_opts_quarantine,
odeprecated: true,
}],
[:switch, "--adopt", {
Expand Down Expand Up @@ -229,9 +233,9 @@ def run
end
casks.each do |cask|
dep_names = CaskDependent.new(cask)
.runtime_dependencies
.reject(&:installed?)
.map(&:name)
.runtime_dependencies
.reject(&:installed?)
.map(&:name)
next if dep_names.blank?

ohai "Would install #{::Utils.pluralize("dependency", dep_names.count, include_count: true)} " \
Expand Down Expand Up @@ -280,28 +284,28 @@ def run
new_casks.each do |cask|
Cask::Installer.new(
cask,
adopt: args.adopt?,
binaries: args.binaries?,
force: args.force?,
quarantine: args.quarantine?,
quiet: args.quiet?,
require_sha: args.require_sha?,
adopt:          args.adopt?,
binaries:       args.binaries?,
force:          args.force?,
quarantine:     args.quarantine?,
quiet:          args.quiet?,
require_sha:    args.require_sha?,
skip_cask_deps: args.skip_cask_deps?,
verbose: args.verbose?,
verbose:        args.verbose?,
).install
end

if !Homebrew::EnvConfig.no_install_upgrade? && installed_casks.any?
Cask::Upgrade.upgrade_casks!(
*installed_casks,
force: args.force?,
dry_run: args.dry_run?,
binaries: args.binaries?,
quarantine: args.quarantine?,
require_sha: args.require_sha?,
force:          args.force?,
dry_run:        args.dry_run?,
binaries:       args.binaries?,
quarantine:     args.quarantine?,
require_sha:    args.require_sha?,
skip_cask_deps: args.skip_cask_deps?,
verbose: args.verbose?,
quiet: args.quiet?,
verbose:        args.verbose?,
quiet:          args.quiet?,
args:,
)
end
Expand All @@ -328,13 +332,13 @@ def run
installed_formulae = formulae.select do |f|
Install.install_formula?(
f,
head: args.HEAD?,
fetch_head: args.fetch_HEAD?,
head:              args.HEAD?,
fetch_head:        args.fetch_HEAD?,
only_dependencies: args.only_dependencies?,
force: args.force?,
quiet: args.quiet?,
skip_link: args.skip_link?,
overwrite: args.overwrite?,
force:             args.force?,
quiet:             args.quiet?,
skip_link:         args.skip_link?,
overwrite:         args.overwrite?,
)
end

Expand All @@ -345,45 +349,47 @@ def run

formulae_installer = Install.formula_installers(
installed_formulae,
installed_on_request: !args.as_dependency?,
installed_as_dependency: args.as_dependency?,
build_bottle: args.build_bottle?,
force_bottle: args.force_bottle?,
bottle_arch: args.bottle_arch,
ignore_deps: args.ignore_dependencies?,
only_deps: args.only_dependencies?,
include_test_formulae: args.include_test_formulae,
installed_on_request:       !args.as_dependency?,
installed_as_dependency:    args.as_dependency?,
build_bottle:               args.build_bottle?,
force_bottle:               args.force_bottle?,
bottle_arch:                args.bottle_arch,
ignore_deps:                args.ignore_dependencies?,
only_deps:                  args.only_dependencies?,
include_test_formulae:      args.include_test_formulae,
build_from_source_formulae: args.build_from_source_formulae,
cc: args.cc,
git: args.git?,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
debug_symbols: args.debug_symbols?,
force: args.force?,
overwrite: args.overwrite?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
dry_run: args.dry_run?,
skip_post_install: args.skip_post_install?,
skip_link: args.skip_link?,
cc:                         args.cc,
git:                        args.git?,
interactive:                args.interactive?,
keep_tmp:                   args.keep_tmp?,
debug_symbols:              args.debug_symbols?,
force:                      args.force?,
overwrite:                  args.overwrite?,
debug:                      args.debug?,
quiet:                      args.quiet?,
verbose:                    args.verbose?,
dry_run:                    args.dry_run?,
skip_post_install:          args.skip_post_install?,
skip_link:                  args.skip_link?,
# ADDED for Dependency Cooldown: pass the flag down to the installer
no_cooldown: args.no_cooldown?,
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The no_cooldown parameter is being passed to Install.formula_installers but this method doesn't accept this parameter. The Install.formula_installers method in Library/Homebrew/install.rb needs to be updated to:

  1. Accept no_cooldown as a parameter in its signature
  2. Pass it through to the FormulaInstaller.new constructor

Without this change, the cooldown feature will not work because the parameter will be ignored, and formulae will always install with the default no_cooldown: false behavior.

Copilot uses AI. Check for mistakes.
)

dependants = Upgrade.dependants(
installed_formulae,
flags: args.flags_only,
ask: args.ask?,
installed_on_request: !args.as_dependency?,
force_bottle: args.force_bottle?,
flags:                      args.flags_only,
ask:                        args.ask?,
installed_on_request:       !args.as_dependency?,
force_bottle:               args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
debug_symbols: args.debug_symbols?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
dry_run: args.dry_run?,
interactive:                args.interactive?,
keep_tmp:                   args.keep_tmp?,
debug_symbols:              args.debug_symbols?,
force:                      args.force?,
debug:                      args.debug?,
quiet:                      args.quiet?,
verbose:                    args.verbose?,
dry_run:                    args.dry_run?,
)

# Main block: if asking the user is enabled, show dependency and size information.
Expand All @@ -399,17 +405,17 @@ def run

Upgrade.upgrade_dependents(
dependants, installed_formulae,
flags: args.flags_only,
dry_run: args.dry_run?,
force_bottle: args.force_bottle?,
flags:                      args.flags_only,
dry_run:                    args.dry_run?,
force_bottle:               args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
interactive: args.interactive?,
keep_tmp: args.keep_tmp?,
debug_symbols: args.debug_symbols?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?
interactive:                args.interactive?,
keep_tmp:                   args.keep_tmp?,
debug_symbols:              args.debug_symbols?,
force:                      args.force?,
debug:                      args.debug?,
quiet:                      args.quiet?,
verbose:                    args.verbose?
)

Cleanup.periodic_clean!(dry_run: args.dry_run?)
Expand Down
29 changes: 29 additions & 0 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
require "api_hashable"
require "utils/output"
require "pypi_packages"
# ... (around line 20-30 in formula.rb)
require "utils/shell"
require "utils/git" # <-- ADD THIS LINE
require "utils/git_repository"
require "build_environment"
# ...
Comment on lines +46 to +51
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect comment placement. The comment # ... (around line 20-30 in formula.rb) on line 46 is misleading since this is actually at line 46, not 20-30. Additionally, lines 47-51 show require statements that don't align with the actual file structure.

The comment suggests adding require "utils/git" but require "utils/shell", require "utils/git_repository", and require "build_environment" are already present in the diff. This is confusing. Either:

  1. Remove the misleading comment and keep only the necessary require statement
  2. Update the comment to accurately reflect what's being added
Suggested change
# ... (around line 20-30 in formula.rb)
require "utils/shell"
require "utils/git" # <-- ADD THIS LINE
require "utils/git_repository"
require "build_environment"
# ...
require "utils/git"

Copilot uses AI. Check for mistakes.

# A formula provides instructions and metadata for Homebrew to install a piece
# of software. Every Homebrew formula is a {Formula}.
Expand Down Expand Up @@ -681,7 +687,30 @@ def synced_with_other_formulae?

@tap.synced_versions_formulae.any? { |synced_formulae| synced_formulae.include?(name) }
end
# In Library/Homebrew/formula.rb (inside the class Formula block)
# ...

# ADDED for dependency cooldown check
# Retrieves the last Git commit time for the formula file.
# @api internal
sig { returns(T.nilable(Time)) }
def git_commit_time
# 1. Skip check if the formula is not associated with a Git-backed tap
# or if it was loaded from the API (API formulas don't have local Git history).
return unless tap&.git? && !loaded_from_api?

# 2. Use Homebrew's Git utility to get the last commit date for this file path.
# The `path` instance variable holds the location of the formula file.
Utils::Git.last_commit_time(path)
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method Utils::Git.last_commit_time does not exist in the codebase. After checking Library/Homebrew/utils/git.rb, this method is not defined. The closest existing methods are last_revision_commit_of_file and last_revision_of_file, but neither returns a Time object directly. You'll need to either:

  1. Implement Utils::Git.last_commit_time(path) in Library/Homebrew/utils/git.rb, or
  2. Use an existing method and parse the Git log output to extract the timestamp

Example implementation for the missing method:

sig { params(file: T.any(Pathname, String)).returns(T.nilable(Time)) }
def self.last_commit_time(file)
  return nil unless available?
  
  output = Utils.popen_read(git, "log", "-1", "--format=%ct", "--", file).chomp
  return nil if output.empty?
  
  Time.at(output.to_i)
end
Suggested change
Utils::Git.last_commit_time(path)
# Inline implementation of last_commit_time since Utils::Git.last_commit_time does not exist
return nil unless Utils::Git.available?
output = Utils.popen_read(Utils::Git.git, "log", "-1", "--format=%ct", "--", path.to_s).chomp
return nil if output.empty?
Time.at(output.to_i)

Copilot uses AI. Check for mistakes.
rescue StandardError => e
# 3. Handle errors gracefully (e.g., shallow clone, repository corruption, Git unavailable).
# If debug is enabled, log the error, but otherwise, silently return nil
# so the install proceeds without the cooldown delay (treating it as trusted/old).
odebug "Git commit time retrieval failed: #{e.message}" if debug?
nil
end

# ... (rest of the Formula class methods)
Comment on lines +690 to +713
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover comment artifacts should be removed. The comments on lines 690-691 and 713 appear to be temporary notes from development:

  • Line 690: # In Library/Homebrew/formula.rb (inside the class Formula block)
  • Line 691: # ...
  • Line 713: # ... (rest of the Formula class methods)

These don't add value and should be removed to keep the code clean.

Copilot uses AI. Check for mistakes.
# A named {Resource} for the currently active {SoftwareSpec}.
# Additional downloads can be defined as {#resource}s.
# {Resource#stage} will create a temporary directory and yield to a block.
Expand Down
Loading
Loading