From 78b259c8c6ba42dc61d552ea1f83117c9d609592 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Fri, 29 Mar 2024 18:26:19 -0700 Subject: [PATCH] Port Homebrew::Cmd::GistLogs --- Library/Homebrew/cmd/gist-logs.rb | 222 ++++++++++---------- Library/Homebrew/test/cmd/gist-logs_spec.rb | 3 +- 2 files changed, 113 insertions(+), 112 deletions(-) diff --git a/Library/Homebrew/cmd/gist-logs.rb b/Library/Homebrew/cmd/gist-logs.rb index f18e99d67b..3860a5493e 100644 --- a/Library/Homebrew/cmd/gist-logs.rb +++ b/Library/Homebrew/cmd/gist-logs.rb @@ -1,130 +1,130 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "formula" require "install" require "system_config" require "stringio" require "socket" -require "cli/parser" module Homebrew - extend Install + module Cmd + class GistLogs < AbstractCommand + include Install + cmd_args do + description <<~EOS + Upload logs for a failed build of to a new Gist. Presents an + error message if no logs are found. + EOS + switch "--with-hostname", + description: "Include the hostname in the Gist." + switch "-n", "--new-issue", + description: "Automatically create a new issue in the appropriate GitHub repository " \ + "after creating the Gist." + switch "-p", "--private", + description: "The Gist will be marked private and will not appear in listings but will " \ + "be accessible with its link." - module_function + named_args :formula, number: 1 + end - sig { returns(CLI::Parser) } - def gist_logs_args - Homebrew::CLI::Parser.new do - description <<~EOS - Upload logs for a failed build of to a new Gist. Presents an - error message if no logs are found. - EOS - switch "--with-hostname", - description: "Include the hostname in the Gist." - switch "-n", "--new-issue", - description: "Automatically create a new issue in the appropriate GitHub repository " \ - "after creating the Gist." - switch "-p", "--private", - description: "The Gist will be marked private and will not appear in listings but will " \ - "be accessible with its link." + sig { override.void } + def run + Install.perform_preinstall_checks(all_fatal: true) + Install.perform_build_from_source_checks(all_fatal: true) + gistify_logs(args.named.to_resolved_formulae.first) + end - named_args :formula, number: 1 - end - end + def gistify_logs(formula) + files = load_logs(formula.logs) + build_time = formula.logs.ctime + timestamp = build_time.strftime("%Y-%m-%d_%H-%M-%S") - def gistify_logs(formula, args:) - files = load_logs(formula.logs) - build_time = formula.logs.ctime - timestamp = build_time.strftime("%Y-%m-%d_%H-%M-%S") - - s = StringIO.new - SystemConfig.dump_verbose_config s - # Dummy summary file, asciibetically first, to control display title of gist - files["# #{formula.name} - #{timestamp}.txt"] = { - content: brief_build_info(formula, with_hostname: args.with_hostname?), - } - files["00.config.out"] = { content: s.string } - files["00.doctor.out"] = { content: Utils.popen_read("#{HOMEBREW_PREFIX}/bin/brew", "doctor", err: :out) } - unless formula.core_formula? - tap = <<~EOS - Formula: #{formula.name} - Tap: #{formula.tap} - Path: #{formula.path} - EOS - files["00.tap.out"] = { content: tap } - end - - odie "`brew gist-logs` requires HOMEBREW_GITHUB_API_TOKEN to be set!" if GitHub::API.credentials_type == :none - - # Description formatted to work well as page title when viewing gist - descr = if formula.core_formula? - "#{formula.name} on #{OS_VERSION} - Homebrew build logs" - else - "#{formula.name} (#{formula.full_name}) on #{OS_VERSION} - Homebrew build logs" - end - - begin - url = GitHub.create_gist(files, descr, private: args.private?) - rescue GitHub::API::HTTPNotFoundError - odie <<~EOS - Your GitHub API token likely doesn't have the `gist` scope. - #{GitHub.pat_blurb(GitHub::CREATE_GIST_SCOPES)} - EOS - end - - url = GitHub.create_issue(formula.tap, "#{formula.name} failed to build on #{OS_VERSION}", url) if args.new_issue? - - puts url if url - end - - def brief_build_info(formula, with_hostname:) - build_time_string = formula.logs.ctime.strftime("%Y-%m-%d %H:%M:%S") - string = +<<~EOS - Homebrew build logs for #{formula.full_name} on #{OS_VERSION} - EOS - if with_hostname - hostname = Socket.gethostname - string << "Host: #{hostname}\n" - end - string << "Build date: #{build_time_string}\n" - string.freeze - end - - # Causes some terminals to display secure password entry indicators. - def noecho_gets - system "stty", "-echo" - result = $stdin.gets - system "stty", "echo" - puts - result - end - - def load_logs(dir, basedir = dir) - logs = {} - if dir.exist? - dir.children.sort.each do |file| - if file.directory? - logs.merge! load_logs(file, basedir) - else - contents = file.size? ? file.read : "empty log" - # small enough to avoid GitHub "unicorn" page-load-timeout errors - max_file_size = 1_000_000 - contents = truncate_text_to_approximate_size(contents, max_file_size, front_weight: 0.2) - logs[file.relative_path_from(basedir).to_s.tr("/", ":")] = { content: contents } + s = StringIO.new + SystemConfig.dump_verbose_config s + # Dummy summary file, asciibetically first, to control display title of gist + files["# #{formula.name} - #{timestamp}.txt"] = { + content: brief_build_info(formula, with_hostname: args.with_hostname?), + } + files["00.config.out"] = { content: s.string } + files["00.doctor.out"] = { content: Utils.popen_read("#{HOMEBREW_PREFIX}/bin/brew", "doctor", err: :out) } + unless formula.core_formula? + tap = <<~EOS + Formula: #{formula.name} + Tap: #{formula.tap} + Path: #{formula.path} + EOS + files["00.tap.out"] = { content: tap } end + + odie "`brew gist-logs` requires HOMEBREW_GITHUB_API_TOKEN to be set!" if GitHub::API.credentials_type == :none + + # Description formatted to work well as page title when viewing gist + descr = if formula.core_formula? + "#{formula.name} on #{OS_VERSION} - Homebrew build logs" + else + "#{formula.name} (#{formula.full_name}) on #{OS_VERSION} - Homebrew build logs" + end + + begin + url = GitHub.create_gist(files, descr, private: args.private?) + rescue GitHub::API::HTTPNotFoundError + odie <<~EOS + Your GitHub API token likely doesn't have the `gist` scope. + #{GitHub.pat_blurb(GitHub::CREATE_GIST_SCOPES)} + EOS + end + + if args.new_issue? + url = GitHub.create_issue(formula.tap, "#{formula.name} failed to build on #{OS_VERSION}", + url) + end + + puts url if url + end + + def brief_build_info(formula, with_hostname:) + build_time_string = formula.logs.ctime.strftime("%Y-%m-%d %H:%M:%S") + string = +<<~EOS + Homebrew build logs for #{formula.full_name} on #{OS_VERSION} + EOS + if with_hostname + hostname = Socket.gethostname + string << "Host: #{hostname}\n" + end + string << "Build date: #{build_time_string}\n" + string.freeze + end + + # Causes some terminals to display secure password entry indicators. + def noecho_gets + system "stty", "-echo" + result = $stdin.gets + system "stty", "echo" + puts + result + end + + def load_logs(dir, basedir = dir) + logs = {} + if dir.exist? + dir.children.sort.each do |file| + if file.directory? + logs.merge! load_logs(file, basedir) + else + contents = file.size? ? file.read : "empty log" + # small enough to avoid GitHub "unicorn" page-load-timeout errors + max_file_size = 1_000_000 + contents = truncate_text_to_approximate_size(contents, max_file_size, front_weight: 0.2) + logs[file.relative_path_from(basedir).to_s.tr("/", ":")] = { content: contents } + end + end + end + odie "No logs." if logs.empty? + + logs end end - odie "No logs." if logs.empty? - - logs - end - - def gist_logs - args = gist_logs_args.parse - - Install.perform_preinstall_checks(all_fatal: true) - Install.perform_build_from_source_checks(all_fatal: true) - gistify_logs(args.named.to_resolved_formulae.first, args:) end end diff --git a/Library/Homebrew/test/cmd/gist-logs_spec.rb b/Library/Homebrew/test/cmd/gist-logs_spec.rb index 25bb9ff393..98a272251b 100644 --- a/Library/Homebrew/test/cmd/gist-logs_spec.rb +++ b/Library/Homebrew/test/cmd/gist-logs_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true +require "cmd/gist-logs" require "cmd/shared_examples/args_parse" -RSpec.describe "brew gist-logs" do +RSpec.describe Homebrew::Cmd::GistLogs do it_behaves_like "parseable arguments" end