lib/gdk/command/report.rb (181 lines of code) (raw):

# frozen_string_literal: true require 'fileutils' require 'erb' require 'shellwords' require 'etc' module GDK module Command class Report < BaseCommand REPORT_TEMPLATE_PATH = 'lib/support/files/report_template.md.erb' LOG_NAMES = %w[ gitlab-db-migrate update-make-ensure-databases-setup update-make-gitlab-topology-service-update reconfigure-make-gdk-reconfigure-task update-make-gitaly-update update-make-gitlab-translations reconfigure-make-gitlab-http-router-setup update-make-gitlab-asdf-install update-make-gitlab-workhorse-update reconfigure-make-gitlab-topology-service-setup update-make-gitlab-bundle update-make-gitlab-yarn reconfigure-make-postgresql update-make-gitlab-git update-make-postgresql update-gdk_bundle_install update-make-gitlab-http-router-update update-platform update-gitlab-git-pull update-make-gitlab-lefthook update-tool-versions update-graphql update-make-gitlab-shell-update ].freeze REPOSITORY_NAMES = %w[gdk gitaly gitlab].freeze NEW_ISSUE_URL = 'https://gitlab.com/gitlab-org/gitlab-development-kit/-/issues/new?issue[label]=~Category:GDK' LABELING = '/label ~type::bug ~bug::functional ~Category:GDK ~gdk-report ~group::developer tooling' COPY_COMMANDS = [ 'pbcopy', # macOS 'xclip -selection clipboard', # Linux 'xsel --clipboard --input', # Linux 'wl-copy' # Wayland ].freeze OPEN_COMMANDS = [ 'open', # macOS 'xdg-open' # Linux ].freeze def initialize @debug_info = GDK::Command::DebugInfo.new end def run(_ = []) @report_id = SecureRandom.uuid template_path = GDK.root.join(REPORT_TEMPLATE_PATH) GDK::Output.info('We are collecting report details, this might take a minute ...') # Create variables for the template report_json = { report_id: report_id, os_name: debug_info.os_name, arch: debug_info.arch, ruby_version: debug_info.ruby_version, gdk_version: debug_info.gdk_version, package_manager: package_manager, env_variables: env_variables, gdk_config: gdk_config, gdk_doctor: gdk_doctor, gem_env: gem_env, bundle_env: bundle_env, network_information: network_information, logs: logs, git_repositories: git_repositories, date_time: date_time } # Render the template renderer = Templates::ErbRenderer.new(template_path, report_json: report_json) report_content = renderer.render_to_string GDK::Output.puts report_content open_browser copy_clipboard(report_content) GDK::Output.info('This report has been copied to your clipboard.') GDK::Output.info('We opened the browser with a new issue, please paste this report from your clipboard into the description.') true end def package_manager if GDK.config.mise.enabled? "mise-en-place #{shellout('mise --version')}" elsif !GDK.config.asdf.opt_out? "asdf #{shellout('asdf version')}" else 'Neither mise nor asdf is used.' end end def env_variables debug_info.env_vars.map do |var, content| "#{var}=#{content}" end.join("\n") end def gdk_config return 'No GDK configuration found.' unless debug_info.gdk_yml_exists? debug_info.gdk_yml end def gdk_doctor output = GDK::OutputBuffered.new GDK::Command::Doctor.new(out: output).run redact_home(output.dump.chomp) end def gem_env redact_home(shellout('gem env')) end def bundle_env redact_home(shellout('bundle env')) end def network_information shellout('lsof -iTCP -sTCP:LISTEN').gsub(Etc.getpwuid.name, '$USER') end def logs LOG_NAMES.each_with_object({}) do |service_name, logs| log_file_path = "log/gdk/#{log_file_name(service_name)}" next if Dir.glob(log_file_path).empty? logs[service_name] = redact_home(Support::Rake::TaskLogger.new(log_file_path).tail) end end def log_file_name(service_name) Time.now.strftime("rake-%Y-%m-%d_*/#{service_name}.log") end def git_repositories REPOSITORY_NAMES.each_with_object({}) do |repo_name, repositories| repositories[repo_name] = { git_status: git_status(repo_name), git_head: git_head(repo_name) } end end def date_time Time.now.strftime('%d/%m/%Y %H:%M:%S %Z') end def git_status(repo_name) command = repo_name == 'gdk' ? 'git status' : "cd #{repo_name} && git status" shellout(command) end def git_head(repo_name) command = repo_name == 'gdk' ? 'git show HEAD' : "cd #{repo_name} && git show HEAD" shellout(command)[/.*/] end def shellout(cmd, **args) debug_info.shellout(cmd, **args) end def redact_home(message) message.gsub(Dir.home, ConfigRedactor::HOME_REDACT_WITH) end def copy_clipboard(content) (command = find_command(COPY_COMMANDS)) || abort('Could not automatically copy message to clipboard. Please copy the output manually.') IO.popen(::Shellwords.split(command), 'w') do |pipe| pipe.print(content) end end def open_browser (command = find_command(OPEN_COMMANDS)) || abort('Could not automatically open browser. Please open the URL manually.') url = URI(NEW_ISSUE_URL) url.query = query system(*Shellwords.split(command), url) end def query URI.encode_www_form( 'issue[issue_type]': :incident, 'issue[confidential]': true, 'issue[title]': "GDK Troubleshooting Report #{report_id}: ENTER A TITLE FOR YOUR REPORT", 'issue[description]': description ) end def description <<~MARKDOWN #{LABELING} <!-- Please paste the report from your clipboard below here. --> MARKDOWN end def find_command(list) list.find { |command| Utils.find_executable(command.split.first) } end private attr_reader :report_id, :debug_info end end end