Merge remote-tracking branch 'origin/master' into test-runners
This commit is contained in:
commit
a754b6d343
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@ -64,4 +64,4 @@ jobs:
|
|||||||
|
|
||||||
- name: Process rubydoc comments
|
- name: Process rubydoc comments
|
||||||
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}/Library/Homebrew
|
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}/Library/Homebrew
|
||||||
run: bundle exec yard doc --plugin sorbet --no-output --fail-on-warning
|
run: bundle exec yard doc --no-output --fail-on-warning
|
||||||
|
|||||||
10
.github/workflows/tests.yml
vendored
10
.github/workflows/tests.yml
vendored
@ -151,7 +151,7 @@ jobs:
|
|||||||
run: brew readall --aliases homebrew/core
|
run: brew readall --aliases homebrew/core
|
||||||
|
|
||||||
- name: Run brew audit --skip-style on homebrew/core
|
- name: Run brew audit --skip-style on homebrew/core
|
||||||
run: brew audit --skip-style --except=version --display-failures-only --tap=homebrew/core
|
run: brew audit --skip-style --except=version --tap=homebrew/core
|
||||||
|
|
||||||
cask-audit:
|
cask-audit:
|
||||||
name: cask audit
|
name: cask audit
|
||||||
@ -187,10 +187,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Run brew audit --skip-style on casks
|
- name: Run brew audit --skip-style on casks
|
||||||
run: |
|
run: |
|
||||||
brew audit --skip-style --except=version --display-failures-only --tap=homebrew/cask
|
brew audit --skip-style --except=version --tap=homebrew/cask
|
||||||
brew audit --skip-style --except=version --display-failures-only --tap=homebrew/cask-drivers
|
brew audit --skip-style --except=version --tap=homebrew/cask-drivers
|
||||||
brew audit --skip-style --except=version --display-failures-only --tap=homebrew/cask-fonts
|
brew audit --skip-style --except=version --tap=homebrew/cask-fonts
|
||||||
brew audit --skip-style --except=version --display-failures-only --tap=homebrew/cask-versions
|
brew audit --skip-style --except=version --tap=homebrew/cask-versions
|
||||||
|
|
||||||
vendored-gems:
|
vendored-gems:
|
||||||
name: vendored gems
|
name: vendored gems
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
--main README.md
|
--main README.md
|
||||||
--markup markdown
|
--markup markdown
|
||||||
--no-private
|
--no-private
|
||||||
|
--plugin sorbet
|
||||||
--load yard/ignore_directives.rb
|
--load yard/ignore_directives.rb
|
||||||
--template-path yard/templates
|
--template-path yard/templates
|
||||||
--exclude test/
|
--exclude test/
|
||||||
|
|||||||
@ -7,7 +7,7 @@ GEM
|
|||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
tzinfo (~> 2.0)
|
tzinfo (~> 2.0)
|
||||||
zeitwerk (~> 2.3)
|
zeitwerk (~> 2.3)
|
||||||
addressable (2.8.2)
|
addressable (2.8.3)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
bindata (2.4.15)
|
bindata (2.4.15)
|
||||||
|
|||||||
@ -71,53 +71,31 @@ module Cask
|
|||||||
@errors ||= []
|
@errors ||= []
|
||||||
end
|
end
|
||||||
|
|
||||||
def warnings
|
|
||||||
@warnings ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def errors?
|
def errors?
|
||||||
errors.any?
|
errors.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { returns(T::Boolean) }
|
|
||||||
def warnings?
|
|
||||||
warnings.any?
|
|
||||||
end
|
|
||||||
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def success?
|
def success?
|
||||||
!(errors? || warnings?)
|
!errors?
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(message: T.nilable(String), location: T.nilable(String)).void }
|
sig { params(message: T.nilable(String), location: T.nilable(String), strict_only: T::Boolean).void }
|
||||||
def add_error(message, location: nil)
|
def add_error(message, location: nil, strict_only: false)
|
||||||
|
# Only raise non-critical audits if the user specified `--strict`.
|
||||||
|
return if strict_only && !@strict
|
||||||
|
|
||||||
errors << ({ message: message, location: location })
|
errors << ({ message: message, location: location })
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(message: T.nilable(String), location: T.nilable(String)).void }
|
|
||||||
def add_warning(message, location: nil)
|
|
||||||
if strict?
|
|
||||||
add_error message, location: location
|
|
||||||
else
|
|
||||||
warnings << ({ message: message, location: location })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def result
|
def result
|
||||||
if errors?
|
Formatter.error("failed") if errors?
|
||||||
Formatter.error("failed")
|
|
||||||
elsif warnings?
|
|
||||||
Formatter.warning("warning")
|
|
||||||
else
|
|
||||||
Formatter.success("passed")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(include_passed: T::Boolean, include_warnings: T::Boolean).returns(T.nilable(String)) }
|
sig { returns(T.nilable(String)) }
|
||||||
def summary(include_passed: false, include_warnings: true)
|
def summary
|
||||||
return if success? && !include_passed
|
return if success?
|
||||||
return if warnings? && !errors? && !include_warnings
|
|
||||||
|
|
||||||
summary = ["audit for #{cask}: #{result}"]
|
summary = ["audit for #{cask}: #{result}"]
|
||||||
|
|
||||||
@ -125,12 +103,6 @@ module Cask
|
|||||||
summary << " #{Formatter.error("-")} #{error[:message]}"
|
summary << " #{Formatter.error("-")} #{error[:message]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
if include_warnings
|
|
||||||
warnings.each do |warning|
|
|
||||||
summary << " #{Formatter.warning("-")} #{warning[:message]}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
summary.join("\n")
|
summary.join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -220,7 +192,7 @@ module Cask
|
|||||||
# increases the maintenance burden.
|
# increases the maintenance burden.
|
||||||
return if cask.tap == "homebrew/cask-fonts"
|
return if cask.tap == "homebrew/cask-fonts"
|
||||||
|
|
||||||
add_warning "Cask should have a description. Please add a `desc` stanza." if cask.desc.blank?
|
add_error("Cask should have a description. Please add a `desc` stanza.", strict_only: true) if cask.desc.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
@ -408,8 +380,10 @@ module Cask
|
|||||||
return unless token_conflicts?
|
return unless token_conflicts?
|
||||||
return unless core_formula_names.include?(cask.token)
|
return unless core_formula_names.include?(cask.token)
|
||||||
|
|
||||||
add_warning "possible duplicate, cask token conflicts with Homebrew core formula: " \
|
add_error(
|
||||||
"#{Formatter.url(core_formula_url)}"
|
"possible duplicate, cask token conflicts with Homebrew core formula: #{Formatter.url(core_formula_url)}",
|
||||||
|
strict_only: true,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
@ -443,18 +417,19 @@ module Cask
|
|||||||
add_error "cask token contains version designation '#{match_data[:designation]}'"
|
add_error "cask token contains version designation '#{match_data[:designation]}'"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_warning "cask token mentions launcher" if token.end_with? "launcher"
|
add_error("cask token mentions launcher", strict_only: true) if token.end_with? "launcher"
|
||||||
|
|
||||||
add_warning "cask token mentions desktop" if token.end_with? "desktop"
|
add_error("cask token mentions desktop", strict_only: true) if token.end_with? "desktop"
|
||||||
|
|
||||||
add_warning "cask token mentions platform" if token.end_with? "mac", "osx", "macos"
|
add_error("cask token mentions platform", strict_only: true) if token.end_with? "mac", "osx", "macos"
|
||||||
|
|
||||||
add_warning "cask token mentions architecture" if token.end_with? "x86", "32_bit", "x86_64", "64_bit"
|
add_error("cask token mentions architecture", strict_only: true) if token.end_with? "x86", "32_bit", "x86_64",
|
||||||
|
"64_bit"
|
||||||
|
|
||||||
frameworks = %w[cocoa qt gtk wx java]
|
frameworks = %w[cocoa qt gtk wx java]
|
||||||
return if frameworks.include?(token) || !token.end_with?(*frameworks)
|
return if frameworks.include?(token) || !token.end_with?(*frameworks)
|
||||||
|
|
||||||
add_warning "cask token mentions framework"
|
add_error("cask token mentions framework", strict_only: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
@ -474,7 +449,10 @@ module Cask
|
|||||||
return if cask.url.to_s.include? cask.version.csv.second
|
return if cask.url.to_s.include? cask.version.csv.second
|
||||||
return if cask.version.csv.third.present? && cask.url.to_s.include?(cask.version.csv.third)
|
return if cask.version.csv.third.present? && cask.url.to_s.include?(cask.version.csv.third)
|
||||||
|
|
||||||
add_warning "Download does not require additional version components. Use `&:short_version` in the livecheck"
|
add_error(
|
||||||
|
"Download does not require additional version components. Use `&:short_version` in the livecheck",
|
||||||
|
strict_only: true,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
@ -518,7 +496,7 @@ module Cask
|
|||||||
"#{message} fix the signature of their app."
|
"#{message} fix the signature of their app."
|
||||||
end
|
end
|
||||||
|
|
||||||
add_warning message
|
add_error(message, strict_only: true)
|
||||||
when Artifact::Pkg
|
when Artifact::Pkg
|
||||||
path = downloaded_path
|
path = downloaded_path
|
||||||
next unless path.exist?
|
next unless path.exist?
|
||||||
@ -526,7 +504,7 @@ module Cask
|
|||||||
result = system_command("pkgutil", args: ["--check-signature", path], print_stderr: false)
|
result = system_command("pkgutil", args: ["--check-signature", path], print_stderr: false)
|
||||||
|
|
||||||
unless result.success?
|
unless result.success?
|
||||||
add_warning <<~EOS
|
add_error(<<~EOS, strict_only: true)
|
||||||
Signature verification failed:
|
Signature verification failed:
|
||||||
#{result.merged_output}
|
#{result.merged_output}
|
||||||
macOS on ARM requires applications to be signed.
|
macOS on ARM requires applications to be signed.
|
||||||
@ -538,7 +516,7 @@ module Cask
|
|||||||
result = system_command("stapler", args: ["validate", path], print_stderr: false)
|
result = system_command("stapler", args: ["validate", path], print_stderr: false)
|
||||||
next if result.success?
|
next if result.success?
|
||||||
|
|
||||||
add_warning <<~EOS
|
add_error(<<~EOS, strict_only: true)
|
||||||
Signature verification failed:
|
Signature verification failed:
|
||||||
#{result.merged_output}
|
#{result.merged_output}
|
||||||
macOS on ARM requires applications to be signed.
|
macOS on ARM requires applications to be signed.
|
||||||
@ -663,16 +641,10 @@ module Cask
|
|||||||
|
|
||||||
metadata = SharedAudits.github_repo_data(user, repo)
|
metadata = SharedAudits.github_repo_data(user, repo)
|
||||||
return if metadata.nil?
|
return if metadata.nil?
|
||||||
|
|
||||||
return unless metadata["archived"]
|
return unless metadata["archived"]
|
||||||
|
|
||||||
message = "GitHub repo is archived"
|
# Discontinued casks shouldn't show up as errors.
|
||||||
|
add_error("GitHub repo is archived", strict_only: !cask.discontinued?)
|
||||||
if cask.discontinued?
|
|
||||||
add_warning message
|
|
||||||
else
|
|
||||||
add_error message
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
@ -684,16 +656,10 @@ module Cask
|
|||||||
|
|
||||||
metadata = SharedAudits.gitlab_repo_data(user, repo)
|
metadata = SharedAudits.gitlab_repo_data(user, repo)
|
||||||
return if metadata.nil?
|
return if metadata.nil?
|
||||||
|
|
||||||
return unless metadata["archived"]
|
return unless metadata["archived"]
|
||||||
|
|
||||||
message = "GitLab repo is archived"
|
# Discontinued casks shouldn't show up as errors.
|
||||||
|
add_error("GitLab repo is archived") unless cask.discontinued?
|
||||||
if cask.discontinued?
|
|
||||||
add_warning message
|
|
||||||
else
|
|
||||||
add_error message
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
|
|||||||
@ -25,8 +25,6 @@ module Cask
|
|||||||
quarantine: nil,
|
quarantine: nil,
|
||||||
any_named_args: nil,
|
any_named_args: nil,
|
||||||
language: nil,
|
language: nil,
|
||||||
display_passes: nil,
|
|
||||||
display_failures_only: nil,
|
|
||||||
only: [],
|
only: [],
|
||||||
except: []
|
except: []
|
||||||
)
|
)
|
||||||
@ -40,8 +38,6 @@ module Cask
|
|||||||
@audit_token_conflicts = audit_token_conflicts
|
@audit_token_conflicts = audit_token_conflicts
|
||||||
@any_named_args = any_named_args
|
@any_named_args = any_named_args
|
||||||
@language = language
|
@language = language
|
||||||
@display_passes = display_passes
|
|
||||||
@display_failures_only = display_failures_only
|
|
||||||
@only = only
|
@only = only
|
||||||
@except = except
|
@except = except
|
||||||
end
|
end
|
||||||
@ -49,7 +45,6 @@ module Cask
|
|||||||
LANGUAGE_BLOCK_LIMIT = 10
|
LANGUAGE_BLOCK_LIMIT = 10
|
||||||
|
|
||||||
def audit
|
def audit
|
||||||
warnings = Set.new
|
|
||||||
errors = Set.new
|
errors = Set.new
|
||||||
|
|
||||||
if !language && language_blocks
|
if !language && language_blocks
|
||||||
@ -63,23 +58,19 @@ module Cask
|
|||||||
|
|
||||||
sample_languages.each_key do |l|
|
sample_languages.each_key do |l|
|
||||||
audit = audit_languages(l)
|
audit = audit_languages(l)
|
||||||
summary = audit.summary(include_passed: output_passed?, include_warnings: output_warnings?)
|
if audit.summary.present? && output_summary?(audit)
|
||||||
if summary.present? && output_summary?(audit)
|
|
||||||
ohai "Auditing language: #{l.map { |lang| "'#{lang}'" }.to_sentence}" if output_summary?
|
ohai "Auditing language: #{l.map { |lang| "'#{lang}'" }.to_sentence}" if output_summary?
|
||||||
puts summary
|
puts audit.summary
|
||||||
end
|
end
|
||||||
warnings += audit.warnings
|
|
||||||
errors += audit.errors
|
errors += audit.errors
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
audit = audit_cask_instance(cask)
|
audit = audit_cask_instance(cask)
|
||||||
summary = audit.summary(include_passed: output_passed?, include_warnings: output_warnings?)
|
puts audit.summary if audit.summary.present? && output_summary?(audit)
|
||||||
puts summary if summary.present? && output_summary?(audit)
|
|
||||||
warnings += audit.warnings
|
|
||||||
errors += audit.errors
|
errors += audit.errors
|
||||||
end
|
end
|
||||||
|
|
||||||
{ warnings: warnings, errors: errors }
|
errors
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -92,19 +83,6 @@ module Cask
|
|||||||
audit.errors?
|
audit.errors?
|
||||||
end
|
end
|
||||||
|
|
||||||
def output_passed?
|
|
||||||
return false if @display_failures_only.present?
|
|
||||||
return true if @display_passes.present?
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def output_warnings?
|
|
||||||
return false if @display_failures_only.present?
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def audit_languages(languages)
|
def audit_languages(languages)
|
||||||
original_config = cask.config
|
original_config = cask.config
|
||||||
localized_config = original_config.merge(Config.new(explicit: { languages: languages }))
|
localized_config = original_config.merge(Config.new(explicit: { languages: languages }))
|
||||||
|
|||||||
@ -30,8 +30,6 @@ module Cask
|
|||||||
description: "Run various additional style checks to determine if a new cask is eligible " \
|
description: "Run various additional style checks to determine if a new cask is eligible " \
|
||||||
"for Homebrew. This should be used when creating new casks and implies " \
|
"for Homebrew. This should be used when creating new casks and implies " \
|
||||||
"`--strict` and `--online`"
|
"`--strict` and `--online`"
|
||||||
switch "--display-failures-only",
|
|
||||||
description: "Only display casks that fail the audit. This is the default for formulae."
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -49,19 +47,17 @@ module Cask
|
|||||||
|
|
||||||
results = self.class.audit_casks(
|
results = self.class.audit_casks(
|
||||||
*casks,
|
*casks,
|
||||||
download: args.download?,
|
download: args.download?,
|
||||||
online: args.online?,
|
online: args.online?,
|
||||||
strict: args.strict?,
|
strict: args.strict?,
|
||||||
signing: args.signing?,
|
signing: args.signing?,
|
||||||
new_cask: args.new_cask?,
|
new_cask: args.new_cask?,
|
||||||
token_conflicts: args.token_conflicts?,
|
token_conflicts: args.token_conflicts?,
|
||||||
quarantine: args.quarantine?,
|
quarantine: args.quarantine?,
|
||||||
any_named_args: any_named_args,
|
any_named_args: any_named_args,
|
||||||
language: args.language,
|
language: args.language,
|
||||||
display_passes: args.verbose? || args.named.count == 1,
|
only: [],
|
||||||
display_failures_only: args.display_failures_only?,
|
except: [],
|
||||||
only: [],
|
|
||||||
except: [],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
failed_casks = results.reject { |_, result| result[:errors].empty? }.map(&:first)
|
failed_casks = results.reject { |_, result| result[:errors].empty? }.map(&:first)
|
||||||
@ -81,8 +77,6 @@ module Cask
|
|||||||
quarantine:,
|
quarantine:,
|
||||||
any_named_args:,
|
any_named_args:,
|
||||||
language:,
|
language:,
|
||||||
display_passes:,
|
|
||||||
display_failures_only:,
|
|
||||||
only:,
|
only:,
|
||||||
except:
|
except:
|
||||||
)
|
)
|
||||||
@ -96,8 +90,6 @@ module Cask
|
|||||||
quarantine: quarantine,
|
quarantine: quarantine,
|
||||||
language: language,
|
language: language,
|
||||||
any_named_args: any_named_args,
|
any_named_args: any_named_args,
|
||||||
display_passes: display_passes,
|
|
||||||
display_failures_only: display_failures_only,
|
|
||||||
only: only,
|
only: only,
|
||||||
except: except,
|
except: except,
|
||||||
}.compact
|
}.compact
|
||||||
@ -110,7 +102,7 @@ module Cask
|
|||||||
|
|
||||||
casks.to_h do |cask|
|
casks.to_h do |cask|
|
||||||
odebug "Auditing Cask #{cask}"
|
odebug "Auditing Cask #{cask}"
|
||||||
[cask.sourcefile_path, Auditor.audit(cask, **options)]
|
[cask.sourcefile_path, { errors: Auditor.audit(cask, **options), warnings: [] }]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -42,6 +42,8 @@ module Cask
|
|||||||
|
|
||||||
quarantine = true if quarantine.nil?
|
quarantine = true if quarantine.nil?
|
||||||
|
|
||||||
|
greedy = true if Homebrew::EnvConfig.upgrade_greedy?
|
||||||
|
|
||||||
outdated_casks = if casks.empty?
|
outdated_casks = if casks.empty?
|
||||||
Caskroom.casks(config: Config.from_args(args)).select do |cask|
|
Caskroom.casks(config: Config.from_args(args)).select do |cask|
|
||||||
cask.outdated?(greedy: greedy, greedy_latest: greedy_latest,
|
cask.outdated?(greedy: greedy, greedy_latest: greedy_latest,
|
||||||
|
|||||||
@ -65,7 +65,8 @@ module Homebrew
|
|||||||
description: "Prefix every line of output with the file or formula name being audited, to " \
|
description: "Prefix every line of output with the file or formula name being audited, to " \
|
||||||
"make output easy to grep."
|
"make output easy to grep."
|
||||||
switch "--display-failures-only",
|
switch "--display-failures-only",
|
||||||
description: "Only display casks that fail the audit. This is the default for formulae."
|
description: "Only display casks that fail the audit. This is the default for formulae and casks.",
|
||||||
|
hidden: true
|
||||||
switch "--skip-style",
|
switch "--skip-style",
|
||||||
description: "Skip running non-RuboCop style checks. Useful if you plan on running " \
|
description: "Skip running non-RuboCop style checks. Useful if you plan on running " \
|
||||||
"`brew style` separately. Enabled by default unless a formula is specified by name."
|
"`brew style` separately. Enabled by default unless a formula is specified by name."
|
||||||
@ -242,24 +243,26 @@ module Homebrew
|
|||||||
require "cask/cmd/abstract_command"
|
require "cask/cmd/abstract_command"
|
||||||
require "cask/cmd/audit"
|
require "cask/cmd/audit"
|
||||||
|
|
||||||
|
if args.display_failures_only?
|
||||||
|
odeprecated "`brew audit <cask> --display-failures-only`", "`brew audit <cask>` without the argument"
|
||||||
|
end
|
||||||
|
|
||||||
# For switches, we add `|| nil` so that `nil` will be passed instead of `false` if they aren't set.
|
# For switches, we add `|| nil` so that `nil` will be passed instead of `false` if they aren't set.
|
||||||
# This way, we can distinguish between "not set" and "set to false".
|
# This way, we can distinguish between "not set" and "set to false".
|
||||||
Cask::Cmd::Audit.audit_casks(
|
Cask::Cmd::Audit.audit_casks(
|
||||||
*audit_casks,
|
*audit_casks,
|
||||||
download: nil,
|
download: nil,
|
||||||
# No need for `|| nil` for `--[no-]signing` because boolean switches are already `nil` if not passed
|
# No need for `|| nil` for `--[no-]signing` because boolean switches are already `nil` if not passed
|
||||||
signing: args.signing?,
|
signing: args.signing?,
|
||||||
online: args.online? || nil,
|
online: args.online? || nil,
|
||||||
strict: args.strict? || nil,
|
strict: args.strict? || nil,
|
||||||
new_cask: args.new_cask? || nil,
|
new_cask: args.new_cask? || nil,
|
||||||
token_conflicts: args.token_conflicts? || nil,
|
token_conflicts: args.token_conflicts? || nil,
|
||||||
quarantine: nil,
|
quarantine: nil,
|
||||||
any_named_args: !no_named_args,
|
any_named_args: !no_named_args,
|
||||||
language: nil,
|
language: nil,
|
||||||
display_passes: args.verbose? || args.named.count == 1,
|
only: args.only,
|
||||||
display_failures_only: args.display_failures_only?,
|
except: args.except,
|
||||||
only: args.only,
|
|
||||||
except: args.except,
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -267,7 +270,7 @@ module Homebrew
|
|||||||
|
|
||||||
cask_count = failed_casks.count
|
cask_count = failed_casks.count
|
||||||
|
|
||||||
cask_problem_count = failed_casks.sum { |_, result| result[:warnings].count + result[:errors].count }
|
cask_problem_count = failed_casks.sum { |_, result| result.count }
|
||||||
new_formula_problem_count += new_formula_problem_lines.count
|
new_formula_problem_count += new_formula_problem_lines.count
|
||||||
total_problems_count = problem_count + new_formula_problem_count + cask_problem_count + tap_problem_count
|
total_problems_count = problem_count + new_formula_problem_count + cask_problem_count + tap_problem_count
|
||||||
|
|
||||||
|
|||||||
@ -463,15 +463,10 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
|
|||||||
url = url.sub(%r{^https?://#{GitHubPackages::URL_DOMAIN}/}o, "#{domain.chomp("/")}/")
|
url = url.sub(%r{^https?://#{GitHubPackages::URL_DOMAIN}/}o, "#{domain.chomp("/")}/")
|
||||||
end
|
end
|
||||||
|
|
||||||
output, _, _status = curl_output(
|
parsed_output = curl_head(url.to_s, timeout: timeout)
|
||||||
"--location", "--silent", "--head", "--request", "GET", url.to_s,
|
parsed_headers = parsed_output.fetch(:responses).map { |r| r.fetch(:headers) }
|
||||||
timeout: timeout
|
|
||||||
)
|
|
||||||
parsed_output = parse_curl_output(output)
|
|
||||||
|
|
||||||
lines = output.to_s.lines.map(&:chomp)
|
final_url = curl_response_follow_redirections(parsed_output.fetch(:responses), url)
|
||||||
|
|
||||||
final_url = curl_response_follow_redirections(parsed_output[:responses], url)
|
|
||||||
|
|
||||||
content_disposition_parser = Mechanize::HTTP::ContentDispositionParser.new
|
content_disposition_parser = Mechanize::HTTP::ContentDispositionParser.new
|
||||||
|
|
||||||
@ -500,19 +495,20 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
|
|||||||
File.basename(filename)
|
File.basename(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
filenames = lines.map(&parse_content_disposition).compact
|
filenames = parsed_headers.flat_map do |headers|
|
||||||
|
next [] unless (header = headers["content-disposition"])
|
||||||
|
|
||||||
time =
|
[*parse_content_disposition.call("Content-Disposition: #{header}")]
|
||||||
lines.map { |line| line[/^Last-Modified:\s*(.+)/i, 1] }
|
end
|
||||||
.compact
|
|
||||||
|
time = parsed_headers
|
||||||
|
.flat_map { |headers| [*headers["last-modified"]] }
|
||||||
.map { |t| t.match?(/^\d+$/) ? Time.at(t.to_i) : Time.parse(t) }
|
.map { |t| t.match?(/^\d+$/) ? Time.at(t.to_i) : Time.parse(t) }
|
||||||
.last
|
.last
|
||||||
|
|
||||||
file_size =
|
file_size = parsed_headers
|
||||||
lines.map { |line| line[/^Content-Length:\s*(\d+)/i, 1] }
|
.flat_map { |headers| [*headers["content-length"]&.to_i] }
|
||||||
.compact
|
.last
|
||||||
.map(&:to_i)
|
|
||||||
.last
|
|
||||||
|
|
||||||
is_redirection = url != final_url
|
is_redirection = url != final_url
|
||||||
basename = filenames.last || parse_basename(final_url, search_query: !is_redirection)
|
basename = filenames.last || parse_basename(final_url, search_query: !is_redirection)
|
||||||
|
|||||||
@ -344,6 +344,10 @@ module Homebrew
|
|||||||
description: "If set, use Pry for the `brew irb` command.",
|
description: "If set, use Pry for the `brew irb` command.",
|
||||||
boolean: true,
|
boolean: true,
|
||||||
},
|
},
|
||||||
|
HOMEBREW_UPGRADE_GREEDY: {
|
||||||
|
description: "If set, pass `--greedy` to all cask upgrade commands.",
|
||||||
|
boolean: true,
|
||||||
|
},
|
||||||
HOMEBREW_SIMULATE_MACOS_ON_LINUX: {
|
HOMEBREW_SIMULATE_MACOS_ON_LINUX: {
|
||||||
description: "If set, running Homebrew on Linux will simulate certain macOS code paths. This is useful " \
|
description: "If set, running Homebrew on Linux will simulate certain macOS code paths. This is useful " \
|
||||||
"when auditing macOS formulae while on Linux.",
|
"when auditing macOS formulae while on Linux.",
|
||||||
|
|||||||
@ -229,6 +229,9 @@ module Homebrew::EnvConfig
|
|||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def self.update_to_tag?; end
|
def self.update_to_tag?; end
|
||||||
|
|
||||||
|
sig { returns(T::Boolean) }
|
||||||
|
def self.upgrade_greedy?; end
|
||||||
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def self.verbose?; end
|
def self.verbose?; end
|
||||||
|
|
||||||
|
|||||||
@ -498,7 +498,7 @@ class Tap
|
|||||||
formula_dir.find
|
formula_dir.find
|
||||||
else
|
else
|
||||||
formula_dir.children
|
formula_dir.children
|
||||||
end.select(&method(:ruby_file?))
|
end.select(&method(:formula_file?))
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
@ -561,7 +561,7 @@ class Tap
|
|||||||
file.extname == ".rb"
|
file.extname == ".rb"
|
||||||
end
|
end
|
||||||
|
|
||||||
# return true if given path would present a {Formula} file in this {Tap}.
|
# returns true if given path would present a {Formula} file in this {Tap}.
|
||||||
# accepts both absolute path and relative path (relative to this {Tap}'s path)
|
# accepts both absolute path and relative path (relative to this {Tap}'s path)
|
||||||
# @private
|
# @private
|
||||||
sig { params(file: T.any(String, Pathname)).returns(T::Boolean) }
|
sig { params(file: T.any(String, Pathname)).returns(T::Boolean) }
|
||||||
@ -569,11 +569,12 @@ class Tap
|
|||||||
file = Pathname.new(file) unless file.is_a? Pathname
|
file = Pathname.new(file) unless file.is_a? Pathname
|
||||||
file = file.expand_path(path)
|
file = file.expand_path(path)
|
||||||
return false unless ruby_file?(file)
|
return false unless ruby_file?(file)
|
||||||
|
return false if cask_file?(file)
|
||||||
|
|
||||||
file.to_s.start_with?("#{formula_dir}/")
|
file.to_s.start_with?("#{formula_dir}/")
|
||||||
end
|
end
|
||||||
|
|
||||||
# return true if given path would present a {Cask} file in this {Tap}.
|
# returns true if given path would present a {Cask} file in this {Tap}.
|
||||||
# accepts both absolute path and relative path (relative to this {Tap}'s path)
|
# accepts both absolute path and relative path (relative to this {Tap}'s path)
|
||||||
# @private
|
# @private
|
||||||
sig { params(file: T.any(String, Pathname)).returns(T::Boolean) }
|
sig { params(file: T.any(String, Pathname)).returns(T::Boolean) }
|
||||||
|
|||||||
@ -13,23 +13,14 @@ describe Cask::Audit, :cask do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def passed?(audit)
|
def passed?(audit)
|
||||||
!audit.errors? && !audit.warnings?
|
!audit.errors?
|
||||||
end
|
end
|
||||||
|
|
||||||
def outcome(audit)
|
def outcome(audit)
|
||||||
if passed?(audit)
|
if passed?(audit)
|
||||||
"passed"
|
"passed"
|
||||||
else
|
else
|
||||||
message = ""
|
"errored with #{audit.errors.map { |e| e.fetch(:message).inspect }.join(",")}"
|
||||||
|
|
||||||
message += "warned with #{audit.warnings.map { |e| e.fetch(:message).inspect }.join(",")}" if audit.warnings?
|
|
||||||
|
|
||||||
if audit.errors?
|
|
||||||
message += " and " if audit.warnings?
|
|
||||||
message += "errored with #{audit.errors.map { |e| e.fetch(:message).inspect }.join(",")}"
|
|
||||||
end
|
|
||||||
|
|
||||||
message
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -53,16 +44,6 @@ describe Cask::Audit, :cask do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
matcher :warn_with do |message|
|
|
||||||
match do |audit|
|
|
||||||
include_msg?(audit.warnings, message)
|
|
||||||
end
|
|
||||||
|
|
||||||
failure_message do |audit|
|
|
||||||
"expected to warn with message #{message.inspect} but #{outcome(audit)}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:cask) { instance_double(Cask::Cask) }
|
let(:cask) { instance_double(Cask::Cask) }
|
||||||
let(:new_cask) { nil }
|
let(:new_cask) { nil }
|
||||||
let(:online) { nil }
|
let(:online) { nil }
|
||||||
@ -118,6 +99,14 @@ describe Cask::Audit, :cask do
|
|||||||
describe "#result" do
|
describe "#result" do
|
||||||
subject { audit.result }
|
subject { audit.result }
|
||||||
|
|
||||||
|
context "when there are no errors and `--strict` is not passed so we should not show anything" do
|
||||||
|
before do
|
||||||
|
audit.add_error("eh", strict_only: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.not_to match(/failed/) }
|
||||||
|
end
|
||||||
|
|
||||||
context "when there are errors" do
|
context "when there are errors" do
|
||||||
before do
|
before do
|
||||||
audit.add_error "bad"
|
audit.add_error "bad"
|
||||||
@ -126,25 +115,42 @@ describe Cask::Audit, :cask do
|
|||||||
it { is_expected.to match(/failed/) }
|
it { is_expected.to match(/failed/) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when there are warnings" do
|
|
||||||
before do
|
|
||||||
audit.add_warning "eh"
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to match(/warning/) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when there are errors and warnings" do
|
context "when there are errors and warnings" do
|
||||||
before do
|
before do
|
||||||
audit.add_error "bad"
|
audit.add_error "bad"
|
||||||
audit.add_warning "eh"
|
audit.add_error("eh", strict_only: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to match(/failed/) }
|
it { is_expected.to match(/failed/) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when there are no errors or warnings" do
|
context "when there are errors and warnings and `--strict` is passed" do
|
||||||
it { is_expected.to match(/passed/) }
|
let(:strict) { true }
|
||||||
|
|
||||||
|
before do
|
||||||
|
audit.add_error "very bad"
|
||||||
|
audit.add_error("a little bit bad", strict_only: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to match(/failed/) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when there are warnings and `--strict` is not passed" do
|
||||||
|
before do
|
||||||
|
audit.add_error("a little bit bad", strict_only: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.not_to match(/failed/) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when there are warnings and `--strict` is passed" do
|
||||||
|
let(:strict) { true }
|
||||||
|
|
||||||
|
before do
|
||||||
|
audit.add_error("a little bit bad", strict_only: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to match(/failed/) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -485,7 +491,7 @@ describe Cask::Audit, :cask do
|
|||||||
it "does not fail" do
|
it "does not fail" do
|
||||||
expect(download_double).not_to receive(:fetch)
|
expect(download_double).not_to receive(:fetch)
|
||||||
expect(UnpackStrategy).not_to receive(:detect)
|
expect(UnpackStrategy).not_to receive(:detect)
|
||||||
expect(run).not_to warn_with(/Audit\.app/)
|
expect(run).not_to error_with(/Audit\.app/)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -503,7 +509,7 @@ describe Cask::Audit, :cask do
|
|||||||
it "does not fail since no extract" do
|
it "does not fail since no extract" do
|
||||||
allow(download_double).to receive(:fetch).and_return(Pathname.new("/tmp/test.zip"))
|
allow(download_double).to receive(:fetch).and_return(Pathname.new("/tmp/test.zip"))
|
||||||
allow(UnpackStrategy).to receive(:detect).and_return(nil)
|
allow(UnpackStrategy).to receive(:detect).and_return(nil)
|
||||||
expect(run).not_to warn_with(/Audit\.app/)
|
expect(run).not_to error_with(/Audit\.app/)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -984,9 +990,20 @@ describe Cask::Audit, :cask do
|
|||||||
context "when cask token conflicts with a core formula" do
|
context "when cask token conflicts with a core formula" do
|
||||||
let(:formula_names) { %w[with-binary other-formula] }
|
let(:formula_names) { %w[with-binary other-formula] }
|
||||||
|
|
||||||
it "warns about duplicates" do
|
context "when `--strict` is passed" do
|
||||||
expect(audit).to receive(:core_formula_names).and_return(formula_names)
|
let(:strict) { true }
|
||||||
expect(run).to warn_with(/possible duplicate/)
|
|
||||||
|
it "warns about duplicates" do
|
||||||
|
expect(audit).to receive(:core_formula_names).and_return(formula_names)
|
||||||
|
expect(run).to error_with(/possible duplicate/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when `--strict` is not passed" do
|
||||||
|
it "does not warn about duplicates" do
|
||||||
|
expect(audit).to receive(:core_formula_names).and_return(formula_names)
|
||||||
|
expect(run).not_to error_with(/possible duplicate/)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1058,8 +1075,8 @@ describe Cask::Audit, :cask do
|
|||||||
context "when `new_cask` is false" do
|
context "when `new_cask` is false" do
|
||||||
let(:new_cask) { false }
|
let(:new_cask) { false }
|
||||||
|
|
||||||
it "warns" do
|
it "does not warn" do
|
||||||
expect(run).to warn_with(/should have a description/)
|
expect(run).not_to error_with(/should have a description/)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ require "cask/auditor"
|
|||||||
describe Cask::Cmd::Audit, :cask do
|
describe Cask::Cmd::Audit, :cask do
|
||||||
let(:cask) { Cask::Cask.new("cask") }
|
let(:cask) { Cask::Cask.new("cask") }
|
||||||
let(:cask_with_many_languages) { Cask::CaskLoader.load(cask_path("with-many-languages")) }
|
let(:cask_with_many_languages) { Cask::CaskLoader.load(cask_path("with-many-languages")) }
|
||||||
let(:result) { { warnings: Set.new, errors: Set.new } }
|
let(:result) { Set.new }
|
||||||
|
|
||||||
describe "selection of Casks to audit" do
|
describe "selection of Casks to audit" do
|
||||||
it "audits all Casks if no tokens are given" do
|
it "audits all Casks if no tokens are given" do
|
||||||
@ -25,7 +25,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_new_cask: false, quarantine: true, any_named_args: true,
|
audit_new_cask: false, quarantine: true, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -40,7 +39,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_new_cask: false, quarantine: true, any_named_args: true,
|
audit_new_cask: false, quarantine: true, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -54,7 +52,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_download: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
audit_download: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -68,7 +65,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_token_conflicts: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
audit_token_conflicts: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -82,7 +78,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_strict: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
audit_strict: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -96,7 +91,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_online: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
audit_online: true, audit_new_cask: false, quarantine: true, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -110,7 +104,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_new_cask: true, quarantine: true, any_named_args: true,
|
audit_new_cask: true, quarantine: true, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -124,7 +117,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_new_cask: false, quarantine: true, language: ["de-AT"], any_named_args: true,
|
audit_new_cask: false, quarantine: true, language: ["de-AT"], any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -138,7 +130,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_new_cask: false, quarantine: false, any_named_args: true,
|
audit_new_cask: false, quarantine: false, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
@ -154,7 +145,6 @@ describe Cask::Cmd::Audit, :cask do
|
|||||||
.with(
|
.with(
|
||||||
cask,
|
cask,
|
||||||
audit_new_cask: false, quarantine: false, any_named_args: true,
|
audit_new_cask: false, quarantine: false, any_named_args: true,
|
||||||
display_failures_only: false, display_passes: true,
|
|
||||||
only: [], except: []
|
only: [], except: []
|
||||||
)
|
)
|
||||||
.and_return(result)
|
.and_return(result)
|
||||||
|
|||||||
@ -39,9 +39,7 @@ describe Cask::Quarantine, :cask do
|
|||||||
it "quarantines Cask audits" do
|
it "quarantines Cask audits" do
|
||||||
expect do
|
expect do
|
||||||
Cask::Cmd::Audit.run("local-transmission", "--download")
|
Cask::Cmd::Audit.run("local-transmission", "--download")
|
||||||
end.to not_raise_error
|
end.to not_raise_error.and not_to_output.to_stderr
|
||||||
.and output(/audit for local-transmission: passed/).to_stdout
|
|
||||||
.and not_to_output.to_stderr
|
|
||||||
|
|
||||||
local_transmission = Cask::CaskLoader.load(cask_path("local-transmission"))
|
local_transmission = Cask::CaskLoader.load(cask_path("local-transmission"))
|
||||||
cached_location = Cask::Download.new(local_transmission).fetch
|
cached_location = Cask::Download.new(local_transmission).fetch
|
||||||
@ -156,9 +154,7 @@ describe Cask::Quarantine, :cask do
|
|||||||
it "does not quarantine Cask audits" do
|
it "does not quarantine Cask audits" do
|
||||||
expect do
|
expect do
|
||||||
Cask::Cmd::Audit.run("local-transmission", "--download", "--no-quarantine")
|
Cask::Cmd::Audit.run("local-transmission", "--download", "--no-quarantine")
|
||||||
end.to not_raise_error
|
end.to not_raise_error.and not_to_output.to_stderr
|
||||||
.and output(/audit for local-transmission: passed/).to_stdout
|
|
||||||
.and not_to_output.to_stderr
|
|
||||||
|
|
||||||
local_transmission = Cask::CaskLoader.load(cask_path("local-transmission"))
|
local_transmission = Cask::CaskLoader.load(cask_path("local-transmission"))
|
||||||
cached_location = Cask::Download.new(local_transmission).fetch
|
cached_location = Cask::Download.new(local_transmission).fetch
|
||||||
|
|||||||
@ -9,12 +9,38 @@ describe CurlGitHubPackagesDownloadStrategy do
|
|||||||
let(:name) { "foo" }
|
let(:name) { "foo" }
|
||||||
let(:url) { "https://#{GitHubPackages::URL_DOMAIN}/v2/homebrew/core/spec_test/manifests/1.2.3" }
|
let(:url) { "https://#{GitHubPackages::URL_DOMAIN}/v2/homebrew/core/spec_test/manifests/1.2.3" }
|
||||||
let(:version) { "1.2.3" }
|
let(:version) { "1.2.3" }
|
||||||
let(:specs) { {} }
|
let(:specs) { { headers: ["Accept: application/vnd.oci.image.index.v1+json"] } }
|
||||||
let(:authorization) { nil }
|
let(:authorization) { nil }
|
||||||
|
let(:head_response) do
|
||||||
|
<<~HTTP
|
||||||
|
HTTP/2 200\r
|
||||||
|
content-length: 12671\r
|
||||||
|
content-type: application/vnd.oci.image.index.v1+json\r
|
||||||
|
docker-content-digest: sha256:7d752ee92d9120e3884b452dce15328536a60d468023ea8e9f4b09839a5442e5\r
|
||||||
|
docker-distribution-api-version: registry/2.0\r
|
||||||
|
etag: "sha256:7d752ee92d9120e3884b452dce15328536a60d468023ea8e9f4b09839a5442e5"\r
|
||||||
|
date: Sun, 02 Apr 2023 22:45:08 GMT\r
|
||||||
|
x-github-request-id: 8814:FA5A:14DAFB5:158D7A2:642A0574\r
|
||||||
|
HTTP
|
||||||
|
end
|
||||||
|
|
||||||
describe "#fetch" do
|
describe "#fetch" do
|
||||||
before do
|
before do
|
||||||
stub_const("HOMEBREW_GITHUB_PACKAGES_AUTH", authorization) if authorization.present?
|
stub_const("HOMEBREW_GITHUB_PACKAGES_AUTH", authorization) if authorization.present?
|
||||||
|
|
||||||
|
allow(strategy).to receive(:system_command)
|
||||||
|
.with(
|
||||||
|
/curl/,
|
||||||
|
hash_including(args: array_including("--head")),
|
||||||
|
)
|
||||||
|
.twice
|
||||||
|
.and_return(instance_double(
|
||||||
|
SystemCommand::Result,
|
||||||
|
success?: true,
|
||||||
|
exit_status: instance_double(Process::Status, exitstatus: 0),
|
||||||
|
stdout: head_response,
|
||||||
|
))
|
||||||
|
|
||||||
strategy.temporary_path.dirname.mkpath
|
strategy.temporary_path.dirname.mkpath
|
||||||
FileUtils.touch strategy.temporary_path
|
FileUtils.touch strategy.temporary_path
|
||||||
end
|
end
|
||||||
|
|||||||
@ -10,9 +10,28 @@ describe CurlPostDownloadStrategy do
|
|||||||
let(:url) { "https://example.com/foo.tar.gz" }
|
let(:url) { "https://example.com/foo.tar.gz" }
|
||||||
let(:version) { "1.2.3" }
|
let(:version) { "1.2.3" }
|
||||||
let(:specs) { {} }
|
let(:specs) { {} }
|
||||||
|
let(:head_response) do
|
||||||
|
<<~HTTP
|
||||||
|
HTTP/1.1 200\r
|
||||||
|
Content-Disposition: attachment; filename="foo.tar.gz"
|
||||||
|
HTTP
|
||||||
|
end
|
||||||
|
|
||||||
describe "#fetch" do
|
describe "#fetch" do
|
||||||
before do
|
before do
|
||||||
|
allow(strategy).to receive(:system_command)
|
||||||
|
.with(
|
||||||
|
/curl/,
|
||||||
|
hash_including(args: array_including("--head")),
|
||||||
|
)
|
||||||
|
.twice
|
||||||
|
.and_return(instance_double(
|
||||||
|
SystemCommand::Result,
|
||||||
|
success?: true,
|
||||||
|
exit_status: instance_double(Process::Status, exitstatus: 0),
|
||||||
|
stdout: head_response,
|
||||||
|
))
|
||||||
|
|
||||||
strategy.temporary_path.dirname.mkpath
|
strategy.temporary_path.dirname.mkpath
|
||||||
FileUtils.touch strategy.temporary_path
|
FileUtils.touch strategy.temporary_path
|
||||||
end
|
end
|
||||||
|
|||||||
@ -12,6 +12,10 @@ describe CurlDownloadStrategy do
|
|||||||
let(:specs) { { user: "download:123456" } }
|
let(:specs) { { user: "download:123456" } }
|
||||||
let(:artifact_domain) { nil }
|
let(:artifact_domain) { nil }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(strategy).to receive(:curl_head).and_return({ responses: [{ headers: {} }] })
|
||||||
|
end
|
||||||
|
|
||||||
it "parses the opts and sets the corresponding args" do
|
it "parses the opts and sets the corresponding args" do
|
||||||
expect(strategy.send(:_curl_args)).to eq(["--user", "download:123456"])
|
expect(strategy.send(:_curl_args)).to eq(["--user", "download:123456"])
|
||||||
end
|
end
|
||||||
@ -190,48 +194,48 @@ describe CurlDownloadStrategy do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "#cached_location" do
|
describe "#cached_location" do
|
||||||
subject(:cached_location) { described_class.new(url, name, version, **specs).cached_location }
|
subject(:cached_location) { strategy.cached_location }
|
||||||
|
|
||||||
context "when URL ends with file" do
|
context "when URL ends with file" do
|
||||||
it {
|
it "falls back to the file name in the URL" do
|
||||||
expect(cached_location).to eq(
|
expect(cached_location).to eq(
|
||||||
HOMEBREW_CACHE/"downloads/3d1c0ae7da22be9d83fb1eb774df96b7c4da71d3cf07e1cb28555cf9a5e5af70--foo.tar.gz",
|
HOMEBREW_CACHE/"downloads/3d1c0ae7da22be9d83fb1eb774df96b7c4da71d3cf07e1cb28555cf9a5e5af70--foo.tar.gz",
|
||||||
)
|
)
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when URL file is in middle" do
|
context "when URL file is in middle" do
|
||||||
let(:url) { "https://example.com/foo.tar.gz/from/this/mirror" }
|
let(:url) { "https://example.com/foo.tar.gz/from/this/mirror" }
|
||||||
|
|
||||||
it {
|
it "falls back to the file name in the URL" do
|
||||||
expect(cached_location).to eq(
|
expect(cached_location).to eq(
|
||||||
HOMEBREW_CACHE/"downloads/1ab61269ba52c83994510b1e28dd04167a2f2e8393a35a9c50c1f7d33fd8f619--foo.tar.gz",
|
HOMEBREW_CACHE/"downloads/1ab61269ba52c83994510b1e28dd04167a2f2e8393a35a9c50c1f7d33fd8f619--foo.tar.gz",
|
||||||
)
|
)
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with a file name trailing the URL path" do
|
context "with a file name trailing the URL path" do
|
||||||
let(:url) { "https://example.com/cask.dmg" }
|
let(:url) { "https://example.com/cask.dmg" }
|
||||||
|
|
||||||
it {
|
it "falls back to the file extension in the URL" do
|
||||||
expect(cached_location.extname).to eq(".dmg")
|
expect(cached_location.extname).to eq(".dmg")
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with a file name trailing the first query parameter" do
|
context "with a file name trailing the first query parameter" do
|
||||||
let(:url) { "https://example.com/download?file=cask.zip&a=1" }
|
let(:url) { "https://example.com/download?file=cask.zip&a=1" }
|
||||||
|
|
||||||
it {
|
it "falls back to the file extension in the URL" do
|
||||||
expect(cached_location.extname).to eq(".zip")
|
expect(cached_location.extname).to eq(".zip")
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with a file name trailing the second query parameter" do
|
context "with a file name trailing the second query parameter" do
|
||||||
let(:url) { "https://example.com/dl?a=1&file=cask.zip&b=2" }
|
let(:url) { "https://example.com/dl?a=1&file=cask.zip&b=2" }
|
||||||
|
|
||||||
it {
|
it "falls back to the file extension in the URL" do
|
||||||
expect(cached_location.extname).to eq(".zip")
|
expect(cached_location.extname).to eq(".zip")
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with an unusually long query string" do
|
context "with an unusually long query string" do
|
||||||
@ -253,10 +257,10 @@ describe CurlDownloadStrategy do
|
|||||||
].join
|
].join
|
||||||
end
|
end
|
||||||
|
|
||||||
it {
|
it "falls back to the file extension in the URL" do
|
||||||
expect(cached_location.extname).to eq(".zip")
|
expect(cached_location.extname).to eq(".zip")
|
||||||
expect(cached_location.to_path.length).to be_between(0, 255)
|
expect(cached_location.to_path.length).to be_between(0, 255)
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -141,7 +141,7 @@ module Utils
|
|||||||
raise Timeout::Error, result.stderr.lines.last.chomp if timeout && result.status.exitstatus == 28
|
raise Timeout::Error, result.stderr.lines.last.chomp if timeout && result.status.exitstatus == 28
|
||||||
|
|
||||||
# Error in the HTTP2 framing layer
|
# Error in the HTTP2 framing layer
|
||||||
if result.status.exitstatus == 16
|
if result.exit_status == 16
|
||||||
return curl_with_workarounds(
|
return curl_with_workarounds(
|
||||||
*args, "--http1.1",
|
*args, "--http1.1",
|
||||||
timeout: end_time&.remaining, **command_options, **options
|
timeout: end_time&.remaining, **command_options, **options
|
||||||
@ -149,7 +149,7 @@ module Utils
|
|||||||
end
|
end
|
||||||
|
|
||||||
# This is a workaround for https://github.com/curl/curl/issues/1618.
|
# This is a workaround for https://github.com/curl/curl/issues/1618.
|
||||||
if result.status.exitstatus == 56 # Unexpected EOF
|
if result.exit_status == 56 # Unexpected EOF
|
||||||
out = curl_output("-V").stdout
|
out = curl_output("-V").stdout
|
||||||
|
|
||||||
# If `curl` doesn't support HTTP2, the exception is unrelated to this bug.
|
# If `curl` doesn't support HTTP2, the exception is unrelated to this bug.
|
||||||
@ -207,6 +207,28 @@ module Utils
|
|||||||
curl_with_workarounds(*args, print_stderr: false, show_output: true, **options)
|
curl_with_workarounds(*args, print_stderr: false, show_output: true, **options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def curl_head(*args, **options)
|
||||||
|
[[], ["--request", "GET"]].each do |request_args|
|
||||||
|
result = curl_output(
|
||||||
|
"--fail", "--location", "--silent", "--head", *request_args, *args,
|
||||||
|
**options
|
||||||
|
)
|
||||||
|
|
||||||
|
# 22 means a non-successful HTTP status code, not a `curl` error, so we still got some headers.
|
||||||
|
if result.success? || result.exit_status == 22
|
||||||
|
parsed_output = parse_curl_output(result.stdout)
|
||||||
|
|
||||||
|
# If we didn't get a `Content-Disposition` header yet, retry using `GET`.
|
||||||
|
next if request_args.empty? &&
|
||||||
|
parsed_output.fetch(:responses).none? { |r| r.fetch(:headers).key?("content-disposition") }
|
||||||
|
|
||||||
|
return parsed_output if result.success?
|
||||||
|
end
|
||||||
|
|
||||||
|
result.assert_success!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Check if a URL is protected by CloudFlare (e.g. badlion.net and jaxx.io).
|
# Check if a URL is protected by CloudFlare (e.g. badlion.net and jaxx.io).
|
||||||
# @param response [Hash] A response hash from `#parse_curl_response`.
|
# @param response [Hash] A response hash from `#parse_curl_response`.
|
||||||
# @return [true, false] Whether a response contains headers indicating that
|
# @return [true, false] Whether a response contains headers indicating that
|
||||||
|
|||||||
@ -29,14 +29,13 @@ module Utils
|
|||||||
# defined by the formula, as only `HOMEBREW_PREFIX` is available
|
# defined by the formula, as only `HOMEBREW_PREFIX` is available
|
||||||
# in the {DATAPatch embedded patch}.
|
# in the {DATAPatch embedded patch}.
|
||||||
#
|
#
|
||||||
# `inreplace` supports regular expressions:
|
# @example `inreplace` supports regular expressions:
|
||||||
# <pre>inreplace "somefile.cfg", /look[for]what?/, "replace by #{bin}/tool"</pre>
|
# inreplace "somefile.cfg", /look[for]what?/, "replace by #{bin}/tool"
|
||||||
#
|
#
|
||||||
# `inreplace` supports blocks:
|
# @example `inreplace` supports blocks:
|
||||||
# <pre>inreplace "Makefile" do |s|
|
# inreplace "Makefile" do |s|
|
||||||
# s.gsub! "/usr/local", HOMEBREW_PREFIX.to_s
|
# s.gsub! "/usr/local", HOMEBREW_PREFIX.to_s
|
||||||
# end
|
# end
|
||||||
# </pre>
|
|
||||||
#
|
#
|
||||||
# @see StringInreplaceExtension
|
# @see StringInreplaceExtension
|
||||||
# @api public
|
# @api public
|
||||||
|
|||||||
@ -30,7 +30,7 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
|
|||||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/zeitwerk-2.6.7/lib")
|
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/zeitwerk-2.6.7/lib")
|
||||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/activesupport-6.1.7.3/lib")
|
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/activesupport-6.1.7.3/lib")
|
||||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/public_suffix-5.0.1/lib")
|
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/public_suffix-5.0.1/lib")
|
||||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/addressable-2.8.2/lib")
|
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/addressable-2.8.3/lib")
|
||||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ast-2.4.2/lib")
|
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ast-2.4.2/lib")
|
||||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bindata-2.4.15/lib")
|
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bindata-2.4.15/lib")
|
||||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/msgpack-1.7.0")
|
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/universal-darwin-21/#{Gem.extension_api_version}/msgpack-1.7.0")
|
||||||
|
|||||||
@ -896,19 +896,16 @@ module Addressable
|
|||||||
#
|
#
|
||||||
# @return [Hash, Array, String] The normalized values
|
# @return [Hash, Array, String] The normalized values
|
||||||
def normalize_value(value)
|
def normalize_value(value)
|
||||||
unless value.is_a?(Hash)
|
|
||||||
value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str
|
|
||||||
end
|
|
||||||
|
|
||||||
# Handle unicode normalization
|
# Handle unicode normalization
|
||||||
if value.kind_of?(Array)
|
if value.respond_to?(:to_ary)
|
||||||
value.map! { |val| normalize_value(val) }
|
value.to_ary.map! { |val| normalize_value(val) }
|
||||||
elsif value.kind_of?(Hash)
|
elsif value.kind_of?(Hash)
|
||||||
value = value.inject({}) { |acc, (k, v)|
|
value = value.inject({}) { |acc, (k, v)|
|
||||||
acc[normalize_value(k)] = normalize_value(v)
|
acc[normalize_value(k)] = normalize_value(v)
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
value = value.to_s if !value.kind_of?(String)
|
||||||
if value.encoding != Encoding::UTF_8
|
if value.encoding != Encoding::UTF_8
|
||||||
value = value.dup.force_encoding(Encoding::UTF_8)
|
value = value.dup.force_encoding(Encoding::UTF_8)
|
||||||
end
|
end
|
||||||
@ -23,7 +23,7 @@ if !defined?(Addressable::VERSION)
|
|||||||
module VERSION
|
module VERSION
|
||||||
MAJOR = 2
|
MAJOR = 2
|
||||||
MINOR = 8
|
MINOR = 8
|
||||||
TINY = 2
|
TINY = 3
|
||||||
|
|
||||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||||
end
|
end
|
||||||
@ -1,7 +1,10 @@
|
|||||||
# typed: false
|
# typed: true
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
def init
|
def init
|
||||||
|
# `sorbet` is available transitively through the `yard-sorbet` plugin, but we're
|
||||||
|
# outside of the standalone sorbet config, so `checked` is enabled by default
|
||||||
|
T.bind(self, YARD::Templates::Template, checked: false)
|
||||||
super
|
super
|
||||||
|
|
||||||
return if sections.empty?
|
return if sections.empty?
|
||||||
@ -10,5 +13,6 @@ def init
|
|||||||
end
|
end
|
||||||
|
|
||||||
def internal
|
def internal
|
||||||
|
T.bind(self, YARD::Templates::Template, checked: false)
|
||||||
erb(:internal) if object.has_tag?(:api) && object.tag(:api).text == "internal"
|
erb(:internal) if object.has_tag?(:api) && object.tag(:api).text == "internal"
|
||||||
end
|
end
|
||||||
|
|||||||
162
docs/How-To-Organize-AGM.md
Normal file
162
docs/How-To-Organize-AGM.md
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# How to Organize AGM
|
||||||
|
|
||||||
|
AGM is our combination of business meeting, yearly work planning session, and opportunity to meet others in our international team in person.
|
||||||
|
|
||||||
|
This document is a _guide_ that assumes that the meeting will be held in person.
|
||||||
|
If a situation occurs that prevents that, it is acceptable to execute it virtually, as was done in 2021 and 2022 during the COVID-19 pandemic.
|
||||||
|
|
||||||
|
<!-- TOC start -->
|
||||||
|
|
||||||
|
* [Roles](#roles)
|
||||||
|
* [Logistics Timeline](#logistics-timeline)
|
||||||
|
* [Three months prior](#three-months-prior)
|
||||||
|
* [Two months prior](#two-months-prior)
|
||||||
|
* [Four weeks prior](#four-weeks-prior)
|
||||||
|
* [Three weeks prior](#three-weeks-prior)
|
||||||
|
* [Two weeks prior](#two-weeks-prior)
|
||||||
|
* [10 days prior](#10-days-prior)
|
||||||
|
* [One week prior](#one-week-prior)
|
||||||
|
* [Day before](#day-before)
|
||||||
|
* [Day-of](#day-of)
|
||||||
|
* [Pre-planning](#pre-planning)
|
||||||
|
* [Finding a Meeting Venue](#finding-a-meeting-venue)
|
||||||
|
* [Who Qualifies For AGM Travel Assistance](#who-qualifies-for-agm-travel-assistance)
|
||||||
|
* [Ideas for future AGMs](#ideas-for-future-agms)
|
||||||
|
* [Meeting enhancements](#meeting-enhancements)
|
||||||
|
* [Day-of enhancements](#day-of-enhancements)
|
||||||
|
|
||||||
|
<!-- TOC end -->
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
Expected participants:
|
||||||
|
|
||||||
|
|Who|Role|
|
||||||
|
|---|---|
|
||||||
|
|Project Leadership Committee (PLC)|Should be physically present if possible, dialed-in if not. Several members must be present in person to run the event. Several members, regardless, needed to provide content for meeting.|
|
||||||
|
|Project Leader (PL)|Should be physically present if possible, dialed-in if not. Regardless, needed to provide content for meeting.|
|
||||||
|
|Technology Steering Committee (TSC)|Should be physically present if possible, dialed-in if not. Regardless, needed to provide content for meeting.|
|
||||||
|
|Members|Should dial-in or participate in person if possible.|
|
||||||
|
|
||||||
|
PLC members' roles of responsibility for planning and execution:
|
||||||
|
|
||||||
|
|Who|Role|
|
||||||
|
|---|---|
|
||||||
|
|Logistics Coordinator (LC)|Coordinates with meeting venue, restaurants, members, committees, vendors|
|
||||||
|
|Agenda Coordinator (AC)|Coordinates agenda and content to be presented|
|
||||||
|
|Technology Coordinator (TC)|Coordinates video conference audiovisual setup|
|
||||||
|
|
||||||
|
:information_source: _(A person may have more than one role but one person should not have all roles.)_
|
||||||
|
|
||||||
|
## Logistics Timeline
|
||||||
|
|
||||||
|
Past practice and future intent is for AGM to coincide with [FOSDEM](https://fosdem.org "Free and Open Source Developers European Meeting"), which is held in Brussels, Belgium annually typically on the Saturday and Sunday of the fifth ISO-8601 week of the calendar year, calculable with:
|
||||||
|
|
||||||
|
ruby -rdate -e "s=ARGV[0].to_i;s.upto(s+4).map{|y|Date.commercial(y,5,6)}.each{|y|puts [y,y+1].join(' - ')}" 2024
|
||||||
|
|
||||||
|
AGM should be held on the Friday before or the Monday following FOSDEM.
|
||||||
|
|
||||||
|
:information_source: _Regenerate the dates for the WHEN lines in the next several headers
|
||||||
|
using this quick command:_
|
||||||
|
|
||||||
|
ruby -rdate -e "YEAR=ARGV[0].to_i;puts ([[44,YEAR-1],[49,YEAR-1]]+(1.upto(4).map{|wk|[wk, YEAR]})).map{|wk,yr|Date.commercial(yr,wk).to_s}" 2024
|
||||||
|
|
||||||
|
### Three months prior
|
||||||
|
|
||||||
|
**When:** Week 44 of YEAR-1 :date: `2023-10-30`
|
||||||
|
|
||||||
|
* [ ] LC: Seek venue through previous contacts or RFP.
|
||||||
|
* [ ] PLC: Notify members of eligibility to attend AGM, with date to be determined.
|
||||||
|
* This is primarily to enable members to begin planning travel by
|
||||||
|
asking for time off, requesting employer reimbursement,
|
||||||
|
arranging childcare or pet sitters,
|
||||||
|
[applying for a visa](https://5195.f2w.bosa.be/en/themes/entry/border-control/visa/visa-type-c)
|
||||||
|
which may [take 2–7 weeks](https://dofi.ibz.be/en/themes/third-country-nationals/short-stay/processing-time-visa-application),
|
||||||
|
etc.
|
||||||
|
|
||||||
|
### Two months prior
|
||||||
|
|
||||||
|
**When:** Week 49 of YEAR-1 :date: `2023-12-04`
|
||||||
|
|
||||||
|
* [ ] LC: Seek informal count of members intending to attend in-person.
|
||||||
|
* [ ] PL: Review maintainer activity per [Governance/Maintainers](Homebrew-Governance.md#8-maintainers).
|
||||||
|
* [ ] PLC: Determine travel assistance budget.
|
||||||
|
* [ ] PLC: Open travel assistance pre-approval process.
|
||||||
|
|
||||||
|
### Four weeks prior
|
||||||
|
|
||||||
|
**When:** Week 1 of YEAR :date: `2024-01-01`
|
||||||
|
|
||||||
|
* [ ] PLC: Solicit changes to [Homebrew Governance](Homebrew-Governance.md) in the form of PRs on the `homebrew-governance-private` repo.
|
||||||
|
|
||||||
|
### Three weeks prior
|
||||||
|
|
||||||
|
**When:** Week 2 of YEAR :date: `2024-01-08`
|
||||||
|
|
||||||
|
* [ ] PLC: Close travel assistance pre-approval process.
|
||||||
|
|
||||||
|
### Two weeks prior
|
||||||
|
|
||||||
|
**When:** Week 3 of YEAR :date: `2024-01-15`
|
||||||
|
|
||||||
|
* [ ] AC: Create agenda, solicit agenda items from PLC and TSC.
|
||||||
|
* [ ] LC: Seek committed member attendance and dietary requirements for each.
|
||||||
|
* [ ] PLC: Close proposals for new Governance changes.
|
||||||
|
|
||||||
|
### 10 days prior
|
||||||
|
|
||||||
|
**When:** Week 4 of YEAR :date: `2024-01-22`
|
||||||
|
|
||||||
|
* [ ] PLC: Resolve all open Governance PRs, roll-up changes, and open PR with changes to `docs/Homebrew-Governance.md` on `homebrew/brew`.
|
||||||
|
|
||||||
|
### One week prior
|
||||||
|
|
||||||
|
**When:** Week 4 of YEAR :date: `2024-01-22`
|
||||||
|
|
||||||
|
* [ ] PLC: Open voting for PLC, PL, and Governance changes.
|
||||||
|
* [ ] AC: Solicit agenda items from membership.
|
||||||
|
* [ ] LC: Secure a venue and reservation for dinner
|
||||||
|
|
||||||
|
### Day before
|
||||||
|
|
||||||
|
* [ ] LC: Confirm reservation count for dinner with attendees
|
||||||
|
* [ ] LC: Hand-off venue AV contact to TC
|
||||||
|
|
||||||
|
### Day-of
|
||||||
|
|
||||||
|
* [ ] LC: Confirm reservation count for dinner with venue
|
||||||
|
* [ ] TC: Connect to video conference, ensure audiovisual equipment is ready and appropriately placed and leveled periodically
|
||||||
|
* [ ] AC: Keep the meeting paced to the agenda, keep time for timeboxed discussions, cut people off if they're talking too long, ensure remote attendees can get a word in
|
||||||
|
|
||||||
|
## Pre-planning
|
||||||
|
|
||||||
|
### Finding a Meeting Venue
|
||||||
|
|
||||||
|
In the past, PLC hosted the AGM at the
|
||||||
|
[THON Hotel Brussels City Centre](https://www.thonhotels.com/conference/belgium/brussels/thon-hotel-brussels-city-centre/?Persons=20)
|
||||||
|
and arranged for a room block checking in the day before FOSDEM and AGM weekend, generally on Friday, and checking out the day after, generally Tuesday when the AGM is Monday.
|
||||||
|
|
||||||
|
### Who Qualifies For AGM Travel Assistance
|
||||||
|
|
||||||
|
Travel assistance is available for AGM participants who are expected to attend the AGM in-person.
|
||||||
|
Those who have employers able to cover all or a part of the costs of attending FOSDEM should exhaust that
|
||||||
|
source of funding before seeking Homebrew funding.
|
||||||
|
|
||||||
|
PLC, TSC, PL and maintainers can expect to have all reasonable, in-policy expenses covered while members will be considered on a case-by-case basis.
|
||||||
|
|
||||||
|
See also the [Reimbursement Policy](Expense-and-Reimbursement-Policy.md#travel) for process and details on what is covered.
|
||||||
|
It is important that all attendees expecting reimbursement stay in-policy.
|
||||||
|
|
||||||
|
## Ideas for future AGMs
|
||||||
|
|
||||||
|
### Meeting enhancements
|
||||||
|
|
||||||
|
* Captioning or transcription, or both - [White Coat Captioning](https://whitecoatcaptioning.com) could handle the live captioning and provide us that for a transcript.
|
||||||
|
* Separate meeting runner
|
||||||
|
* Keep PL ideally focused on content and not agenda or tracking who's asked to speak
|
||||||
|
* Should be a PLC member who is not the AC, LC, or TC
|
||||||
|
* Should be someone happy and willing to cut people off mid-sentence and, assertively but in a friendly manner, stop conversations that are not running to time
|
||||||
|
|
||||||
|
### Day-of enhancements
|
||||||
|
|
||||||
|
* Track dietary requirements centrally for in-person participants
|
||||||
45
docs/governance/2023-agm-minutes.md
Normal file
45
docs/governance/2023-agm-minutes.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Homebrew Annual General Meeting 2022
|
||||||
|
|
||||||
|
## Minutes
|
||||||
|
|
||||||
|
- 09:10 Call to order
|
||||||
|
|
||||||
|
## Reports
|
||||||
|
|
||||||
|
- 09:15 Project Leader Report (15 min, Mike McQuaid)
|
||||||
|
- 09:40 Project Leadership Committee Report (15 min, Sean Molenaar)
|
||||||
|
- 10:00 Technical Steering Commitee Report (15 min, Michka Popoff)
|
||||||
|
|
||||||
|
## Election Results
|
||||||
|
|
||||||
|
- 10:00 PL Voting Results (5 min, Issy Long)
|
||||||
|
- Mike McQuaid was re-elected.
|
||||||
|
|
||||||
|
- 10:05 PLC Voting Results (5 min, Sean Molenaar because Issy felt weird announcing their own election)
|
||||||
|
- Issy Long and Jon Chang were re-elected and Colin Dean was newly elected.
|
||||||
|
- Commiserations to George Adams who was not elected.
|
||||||
|
|
||||||
|
- 10:10 Governance Changes Voting Results (5 min, Issy Long)
|
||||||
|
- The governance changes (<https://github.com/Homebrew/brew/pull/14482>) passed.
|
||||||
|
|
||||||
|
## Member presentations and discussions (11:00-12:00 and 13:30-15:00)
|
||||||
|
|
||||||
|
- ARM on Linux (Michka Popoff)
|
||||||
|
- Install from API (Rylan Polster)
|
||||||
|
- Homebrew on Linux (Patrick Linanne and Bo Anderson)
|
||||||
|
- CI infrastructure (Rui Chen)
|
||||||
|
- Security initiatives (Patrick Linanne)
|
||||||
|
- Formula licenses (Rui Chen)
|
||||||
|
- GitHub authentication security improvements (Colin Dean)
|
||||||
|
- Improvements to autobumping formulae and casks (Rui Chen)
|
||||||
|
- Formalizing the reimbursement process (Colin Dean)
|
||||||
|
- Homebrew Mastodon account (Mike McQuaid)
|
||||||
|
- InfluxDB analytics (Mike McQuaid)
|
||||||
|
- Security posture improvements, potentially making a security-focused volunteer group (Mike McQuaid)
|
||||||
|
|
||||||
|
## Hackathon (undocumented, 15:00-17:00)
|
||||||
|
|
||||||
|
## Adjournment
|
||||||
|
|
||||||
|
- Final remarks, thanks for coming (Issy Long)
|
||||||
|
- 17:00 Meeting finished.
|
||||||
Loading…
x
Reference in New Issue
Block a user