extend/kernel: make opoo/odie/etc. print GitHub Actions notes.

We already do this for deprecations but these may make warnings
and errors from Homebrew easier to spot in GitHub Actions logs.

While we're here, cleanup other cases that should have used
`GitHub::Actions::Annotation` but didn't and provide some helpers and
tweaks there necessary for our use case here.
This commit is contained in:
Mike McQuaid 2024-05-09 13:19:14 +01:00
parent 6ba84044b0
commit 16901a674f
No known key found for this signature in database
10 changed files with 54 additions and 26 deletions

View File

@ -136,10 +136,9 @@ on_request: true)
case deprecate_disable_type case deprecate_disable_type
when :deprecated when :deprecated
puts "::warning::#{message_full}" if ENV["GITHUB_ACTIONS"]
opoo message_full opoo message_full
when :disabled when :disabled
puts "::error::#{message_full}" if ENV["GITHUB_ACTIONS"] GitHub::Actions.puts_annotation_if_env_set(:error, message)
raise CaskCannotBeInstalledError.new(@cask, message) raise CaskCannotBeInstalledError.new(@cask, message)
end end
end end

View File

@ -308,7 +308,7 @@ module Homebrew
ofail "#{errors_summary}." ofail "#{errors_summary}."
end end
return unless ENV["GITHUB_ACTIONS"] return unless GitHub::Actions.env_set?
annotations = formula_problems.merge(cask_problems).flat_map do |(_, path), problems| annotations = formula_problems.merge(cask_problems).flat_map do |(_, path), problems|
problems.map do |problem| problems.map do |problem|

View File

@ -170,7 +170,7 @@ module Homebrew
safe_system HOMEBREW_BREW_FILE, *upload_args safe_system HOMEBREW_BREW_FILE, *upload_args
end end
ensure ensure
if args.retain_bottle_dir? && ENV["GITHUB_ACTIONS"] if args.retain_bottle_dir? && GitHub::Actions.env_set?
ohai "Bottle files retained at:", dir ohai "Bottle files retained at:", dir
File.open(ENV.fetch("GITHUB_OUTPUT"), "a") do |f| File.open(ENV.fetch("GITHUB_OUTPUT"), "a") do |f|
f.puts "bottle_path=#{dir}" f.puts "bottle_path=#{dir}"

View File

@ -925,7 +925,7 @@ module Homebrew
def check_cask_staging_location def check_cask_staging_location
# Skip this check when running CI since the staging path is not writable for security reasons # Skip this check when running CI since the staging path is not writable for security reasons
return if ENV["GITHUB_ACTIONS"] return if GitHub::Actions.env_set?
path = Cask::Caskroom.path path = Cask::Caskroom.path

View File

@ -64,6 +64,7 @@ module Kernel
def opoo(message) def opoo(message)
Tty.with($stderr) do |stderr| Tty.with($stderr) do |stderr|
stderr.puts Formatter.warning(message, label: "Warning") stderr.puts Formatter.warning(message, label: "Warning")
GitHub::Actions.puts_annotation_if_env_set(:warning, message)
end end
end end
@ -73,6 +74,7 @@ module Kernel
def onoe(message) def onoe(message)
Tty.with($stderr) do |stderr| Tty.with($stderr) do |stderr|
stderr.puts Formatter.error(message, label: "Error") stderr.puts Formatter.error(message, label: "Error")
GitHub::Actions.puts_annotation_if_env_set(:error, message)
end end
end end
@ -146,11 +148,14 @@ module Kernel
next unless (match = line.match(HOMEBREW_TAP_PATH_REGEX)) next unless (match = line.match(HOMEBREW_TAP_PATH_REGEX))
tap = Tap.fetch(match[:user], match[:repo]) tap = Tap.fetch(match[:user], match[:repo])
tap_message = +"\nPlease report this issue to the #{tap} tap (not Homebrew/brew or Homebrew/homebrew-core)" tap_message = +"\nPlease report this issue to the #{tap.full_name} tap"
tap_message += " (not Homebrew/brew or Homebrew/homebrew-core)" unless tap.official?
tap_message += ", or even better, submit a PR to fix it" if replacement tap_message += ", or even better, submit a PR to fix it" if replacement
tap_message << ":\n #{line.sub(/^(.*:\d+):.*$/, '\1')}\n\n" tap_message << ":\n #{line.sub(/^(.*:\d+):.*$/, '\1')}\n\n"
break break
end end
file, line, = backtrace.first.split(":")
line = line.to_i if line.present?
message = +"Calling #{method} is #{verb}! #{replacement_message}" message = +"Calling #{method} is #{verb}! #{replacement_message}"
message << tap_message if tap_message message << tap_message if tap_message
@ -158,12 +163,13 @@ module Kernel
disable = true if disable_for_developers && Homebrew::EnvConfig.developer? disable = true if disable_for_developers && Homebrew::EnvConfig.developer?
if disable || Homebrew.raise_deprecation_exceptions? if disable || Homebrew.raise_deprecation_exceptions?
puts "::error::#{message}" if ENV["GITHUB_ACTIONS"] puts GitHub::Actions::Annotation.new(:error, message, file:, line:) if GitHub::Actions.env_set?
GitHub::Actions.puts_annotation_if_env_set(:error, message, file:, line:)
exception = MethodDeprecatedError.new(message) exception = MethodDeprecatedError.new(message)
exception.set_backtrace(backtrace) exception.set_backtrace(backtrace)
raise exception raise exception
elsif !Homebrew.auditing? elsif !Homebrew.auditing?
puts "::warning::#{message}" if ENV["GITHUB_ACTIONS"] GitHub::Actions.puts_annotation_if_env_set(:warning, message, file:, line:)
opoo message opoo message
end end
end end

View File

@ -134,7 +134,7 @@ module Homebrew
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew # `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
# repository. This only needs to support whatever CI providers # repository. This only needs to support whatever CI providers
# Homebrew/brew is currently using. # Homebrew/brew is currently using.
return if ENV["GITHUB_ACTIONS"] return if GitHub::Actions.env_set?
# With fake El Capitan for Portable Ruby, we are intentionally not using Xcode 8. # With fake El Capitan for Portable Ruby, we are intentionally not using Xcode 8.
# This is because we are not using the CLT and Xcode 8 has the 10.12 SDK. # This is because we are not using the CLT and Xcode 8 has the 10.12 SDK.
@ -165,7 +165,7 @@ module Homebrew
# `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew # `brew test-bot` runs `brew doctor` in the CI for the Homebrew/brew
# repository. This only needs to support whatever CI providers # repository. This only needs to support whatever CI providers
# Homebrew/brew is currently using. # Homebrew/brew is currently using.
return if ENV["GITHUB_ACTIONS"] return if GitHub::Actions.env_set?
<<~EOS <<~EOS
A newer Command Line Tools release is available. A newer Command Line Tools release is available.

View File

@ -206,10 +206,9 @@ class FormulaInstaller
case deprecate_disable_type case deprecate_disable_type
when :deprecated when :deprecated
puts "::warning::#{message}" if ENV["GITHUB_ACTIONS"]
opoo message opoo message
when :disabled when :disabled
puts "::error::#{message}" if ENV["GITHUB_ACTIONS"] GitHub::Actions.puts_annotation_if_env_set(:error, message)
raise CannotInstallFormulaError, message raise CannotInstallFormulaError, message
end end
end end
@ -505,7 +504,8 @@ on_request: installed_on_request?, options:)
raise if Homebrew::EnvConfig.developer? raise if Homebrew::EnvConfig.developer?
$stderr.puts "Please report this issue to the #{formula.tap} tap (not Homebrew/brew or Homebrew/homebrew-core)!" $stderr.puts "Please report this issue to the #{formula.tap&.full_name} tap".squeeze(" ")
$stderr.puts " (not Homebrew/brew or Homebrew/homebrew-core)!" unless formula.core_formula?
false false
else else
f.linked_keg.exist? && f.opt_prefix.exist? f.linked_keg.exist? && f.opt_prefix.exist?

View File

@ -15,7 +15,7 @@ module Homebrew
def self.check_style_and_print(files, **options) def self.check_style_and_print(files, **options)
success = check_style_impl(files, :print, **options) success = check_style_impl(files, :print, **options)
if ENV["GITHUB_ACTIONS"] && !success if GitHub::Actions.env_set? && !success
check_style_json(files, **options).each do |path, offenses| check_style_json(files, **options).each do |path, offenses|
offenses.each do |o| offenses.each do |o|
line = o.location.line line = o.location.line

View File

@ -230,12 +230,12 @@ RSpec.describe Kernel do
expect do expect do
odeprecated( odeprecated(
"method", "replacement", "method", "replacement",
caller: ["#{HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core/"], caller: ["#{HOMEBREW_LIBRARY}/Taps/playbrew/homebrew-play/"],
disable: true disable: true
) )
end.to raise_error( end.to raise_error(
MethodDeprecatedError, MethodDeprecatedError,
%r{method.*replacement.*homebrew/core.*/Taps/homebrew/homebrew-core/}m, %r{method.*replacement.*playbrew/homebrew-play.*/Taps/playbrew/homebrew-play/}m,
) )
end end
end end

View File

@ -35,6 +35,25 @@ module GitHub
EOS EOS
end end
sig { returns(T::Boolean) }
def self.env_set?
ENV.fetch("GITHUB_ACTIONS", false).present?
end
sig {
params(
type: Symbol, message: String,
file: T.nilable(T.any(String, Pathname)),
line: T.nilable(Integer)
).void
}
def self.puts_annotation_if_env_set(type, message, file: nil, line: nil)
# Don't print annotations during tests, too messy to handle these.
return if ENV.fetch("HOMEBREW_TESTS", false)
puts Annotation.new(type, message) if env_set?
end
# Helper class for formatting annotations on GitHub Actions. # Helper class for formatting annotations on GitHub Actions.
class Annotation class Annotation
ANNOTATION_TYPES = [:notice, :warning, :error].freeze ANNOTATION_TYPES = [:notice, :warning, :error].freeze
@ -52,7 +71,7 @@ module GitHub
params( params(
type: Symbol, type: Symbol,
message: String, message: String,
file: T.any(String, Pathname), file: T.nilable(T.any(String, Pathname)),
title: T.nilable(String), title: T.nilable(String),
line: T.nilable(Integer), line: T.nilable(Integer),
end_line: T.nilable(Integer), end_line: T.nilable(Integer),
@ -60,12 +79,12 @@ module GitHub
end_column: T.nilable(Integer), end_column: T.nilable(Integer),
).void ).void
} }
def initialize(type, message, file:, title: nil, line: nil, end_line: nil, column: nil, end_column: nil) def initialize(type, message, file: nil, title: nil, line: nil, end_line: nil, column: nil, end_column: nil)
raise ArgumentError, "Unsupported type: #{type.inspect}" if ANNOTATION_TYPES.exclude?(type) raise ArgumentError, "Unsupported type: #{type.inspect}" if ANNOTATION_TYPES.exclude?(type)
@type = type @type = type
@message = Tty.strip_ansi(message) @message = Tty.strip_ansi(message)
@file = self.class.path_relative_to_workspace(file) @file = self.class.path_relative_to_workspace(file) if file.present?
@title = Tty.strip_ansi(title) if title @title = Tty.strip_ansi(title) if title
@line = Integer(line) if line @line = Integer(line) if line
@end_line = Integer(end_line) if end_line @end_line = Integer(end_line) if end_line
@ -76,15 +95,17 @@ module GitHub
sig { returns(String) } sig { returns(String) }
def to_s def to_s
metadata = @type.to_s metadata = @type.to_s
metadata << " file=#{Actions.escape(@file.to_s)}" if @file
metadata << " file=#{Actions.escape(@file.to_s)}"
if @line if @line
metadata << ",line=#{@line}" metadata << ",line=#{@line}"
metadata << ",endLine=#{@end_line}" if @end_line metadata << ",endLine=#{@end_line}" if @end_line
if @column if @column
metadata << ",col=#{@column}" metadata << ",col=#{@column}"
metadata << ",endColumn=#{@end_column}" if @end_column metadata << ",endColumn=#{@end_column}" if @end_column
end
end end
end end
@ -97,6 +118,8 @@ module GitHub
# the `GITHUB_WORKSPACE` directory or if no `file` is specified. # the `GITHUB_WORKSPACE` directory or if no `file` is specified.
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def relevant? def relevant?
return true if @file.blank?
@file.descend.next.to_s != ".." @file.descend.next.to_s != ".."
end end
end end