Class: Homebrew::TestBot::TestFormulae Private

Inherits:
Test show all
Includes:
OS::Linux::TestBot::TestFormulae, OS::Mac::TestBot::TestFormulae
Defined in:
test_bot/test_formulae.rb

This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.

Direct Known Subclasses

BottlesFetch, Formulae, FormulaeDependents

Instance Attribute Summary collapse

Attributes inherited from Test

#steps

Instance Method Summary collapse

Methods inherited from Test

#failed_steps, #ignored_steps, #test

Methods included from Utils::Output::Mixin

#issue_reporting_message, #odebug, #odeprecated, #odie, #odisabled, #ofail, #oh1, #oh1_title, #ohai, #ohai_title, #onoe, #opoo, #opoo_outside_github_actions, #opoo_without_github_actions_annotation, #pretty_cannot_install, #pretty_deprecated, #pretty_disabled, #pretty_duration, #pretty_install_status, #pretty_installed, #pretty_uninstalled, #pretty_unmarked, #pretty_upgradable, #pretty_warning

Constructor Details

#initialize(tap:, git:, dry_run:, fail_fast:, verbose:) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • tap (Tap, nil)
  • git (String, nil)
  • dry_run (Boolean)
  • fail_fast (Boolean)
  • verbose (Boolean)


28
29
30
31
32
33
34
35
36
37
38
# File 'test_bot/test_formulae.rb', line 28

def initialize(tap:, git:, dry_run:, fail_fast:, verbose:)
  super

  @skipped_or_failed_formulae = T.let([], T::Array[String])
  @artifact_cache = T.let(Pathname.new("artifact-cache"), Pathname)
  # Let's keep track of the artifacts we've already downloaded
  # to avoid repeatedly trying to download the same thing.
  @downloaded_artifacts = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[String, T::Array[String]])
  @testing_formulae = T.let([], T::Array[String])
  @tested_formulae = T.let([], T::Array[String])
end

Instance Attribute Details

#artifact_cachePathname (readonly)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



14
15
16
# File 'test_bot/test_formulae.rb', line 14

def artifact_cache
  @artifact_cache
end

#downloaded_artifactsHash{String => Array<String>} (readonly)

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



17
18
19
# File 'test_bot/test_formulae.rb', line 17

def downloaded_artifacts
  @downloaded_artifacts
end

#skipped_or_failed_formulaeArray<String>

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



11
12
13
# File 'test_bot/test_formulae.rb', line 11

def skipped_or_failed_formulae
  @skipped_or_failed_formulae
end

Instance Method Details

#cleanup_package_manager_cachesvoid

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

A build could poison the package manager caches for the builds after it, so start each one with only the caches that verify their contents.



115
116
117
118
119
120
121
122
123
124
# File 'test_bot/test_formulae.rb', line 115

def cleanup_package_manager_caches
  return if ENV["HOMEBREW_GITHUB_ACTIONS"].blank?

  paths = Homebrew::PackageManagerCache.paths.select(&:exist?).reject do |path|
    Homebrew::PackageManagerCache::CONTENT_ADDRESSED_DIRECTORIES.include?(path.basename.to_s)
  end
  # Go makes its module cache read-only.
  paths.each { |path| FileUtils.chmod_R("u+w", path, force: true) }
  FileUtils.rm_rf paths
end

#download_artifacts_from_previous_run!(artifact_pattern, dry_run:) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:

  • artifact_pattern (String)
  • dry_run (Boolean)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'test_bot/test_formulae.rb', line 41

def download_artifacts_from_previous_run!(artifact_pattern, dry_run:)
  return if dry_run
  return if GitHub::API.credentials_type == :none
  return if (sha = previous_github_sha).blank?

  pull_number = github_event_payload&.dig("pull_request", "number")
  return if pull_number.blank?

  github_repository = ENV.fetch("GITHUB_REPOSITORY")
  owner, repo = *github_repository.split("/")
  raise "github_repository #{github_repository} is invalid" if owner.nil? || repo.nil?

  pr_labels = GitHub.pull_request_labels(owner, repo, pull_number)
  # Also disable bottle cache for PRs modifying workflows to avoid cache poisoning.
  return if pr_labels.include?("CI-no-bottle-cache") || pr_labels.include?("workflows")

  variables = {
    owner:,
    repo:,
    commit: sha,
  }

  response = GitHub::API.open_graphql(GRAPHQL_QUERY, variables:)
  check_suite_nodes = response.dig("repository", "object", "checkSuites", "nodes")
  return if check_suite_nodes.blank?

  wanted_artifacts = (check_suite_nodes, github_repository, "pull_request",
                                       "CI", "conclusion", artifact_pattern)
  wanted_artifacts_pattern = artifact_pattern
  if wanted_artifacts.empty?
    # If we didn't find the artifacts that we wanted, fall back to the `event_payload` artifact.
    wanted_artifacts = (check_suite_nodes, github_repository, "pull_request_target",
                                         "Triage tasks", "upload-metadata", "event_payload")
    wanted_artifacts_pattern = "event_payload"
  end
  return if wanted_artifacts.empty?

  if (attempted_artifact = wanted_artifacts.find do |artifact|
        @downloaded_artifacts.fetch(sha, []).include?(artifact.fetch("name"))
      end)
    opoo "Already tried #{attempted_artifact.fetch("name")} from #{sha}, giving up"
    return
  end

  cached_event_json&.unlink if File.fnmatch?(wanted_artifacts_pattern, "event_payload", File::FNM_EXTGLOB)

  require "utils/github/artifacts"

  ohai "Downloading artifacts matching pattern #{wanted_artifacts_pattern} from #{sha}"
  artifact_cache.mkpath
  artifact_cache.cd do
    wanted_artifacts.each do |artifact|
      name = artifact.fetch("name")
      ohai "Downloading artifact #{name} from #{sha}"
      (@downloaded_artifacts[sha] ||= []) << name

      download_url = artifact.fetch("archive_download_url")
      artifact_id = artifact.fetch("id")
      GitHub.download_artifact(download_url, artifact_id.to_s)
    end
  end

  return if wanted_artifacts_pattern == artifact_pattern

  # If we made it here, then we downloaded an `event_payload` artifact.
  # We can now use this `event_payload` artifact to attempt to download the artifact we wanted.
  download_artifacts_from_previous_run!(artifact_pattern, dry_run:)
rescue GitHub::API::AuthenticationFailedError => e
  opoo e
end