# frozen_string_literal: true module Jiminy require "thor" class CLI < Thor require "jiminy/reporting/ci_providers/circle_ci" require "jiminy/cli/exit_codes" include Thor::Actions include Jiminy::Reporting::CIProviders class WorkflowStillRunningError < StandardError; end private_constant :WorkflowStillRunningError MAX_TIMEOUT_SECONDS = 1800 POLL_INTERVAL_SECONDS = 60 source_root File.expand_path("templates/", __dir__) def self.exit_on_failure? false end desc "Report results", "Reports the results of tests" method_option :commit, type: :string, aliases: "c", required: true, banner: "3e078f8770743549b722382ec5d412a30b9fdcc5", desc: "The full SHA for the current HEAD commit" method_option :pr_number, type: :numeric, aliases: %w(pr p), required: true, banner: "1", desc: "The GitHub PR number" method_option :dry_run, type: :boolean, default: false, lazy_default: true, desc: "Print to STDOUT instead of leaving a comment on GitHub" method_option :timeout, type: :numeric, aliases: %w(max-timeout), default: MAX_TIMEOUT_SECONDS, desc: "How long to poll CircleCI before timing out (in seconds)" method_option :poll_interval, type: :numeric, aliases: %w(poll-interval), default: POLL_INTERVAL_SECONDS, desc: "How frequently to poll CircleCI (in seconds)" method_option :source, type: :string, default: "circleci", desc: "Where are the results.yml files we should report?" def report self.start_time = Time.now artifact_urls = artifacts.map(&:url) Jiminy::Reporting.report!(*artifact_urls, pr_number: options[:pr_number], dry_run: options[:dry_run]) finish(ExitCodes::Success) end desc "Install Jiminy", "Installs jiminy configuration files in your app" def init template("config.rb", "./config/jiminy.rb") end # rubocop:disable Metrics/BlockLength no_tasks do attr_accessor :start_time def finish(exit_code_klass, *args, **kwargs) exit_code = exit_code_klass.new(*args, **kwargs) if exit_code.value == 0 puts exit_code else warn exit_code end exit(exit_code.value) end def poll_interval options[:poll_interval] || POLL_INTERVAL_SECONDS end def source options[:source] || :circleci end def max_timeout options[:timeout] || MAX_TIMEOUT_SECONDS end def timed_out? (Time.now - start_time) > max_timeout end def git_revision options[:commit] end def pr_number options[:pr_number] end def pipeline @_pipeline ||= CircleCI::Pipeline.find_by_revision(git_revision: git_revision, pr_number: pr_number) or finish(ExitCodes::PipelineNotFound, git_revision: git_revision) end def missing_options [].tap do |array| array.concat("--pr-number") unless pr_number array.concat("--commit") unless git_revision end end # rubocop:disable Metrics/AbcSize def workflow @_workflow ||= begin result = CircleCI::Workflow.find(pipeline_id: pipeline.id, workflow_name: Jiminy.config.ci_workflow_name) if result.nil? finish(ExitCodes::WorkflowNotFound, workflow_name: Jiminy.config.ci_workflow_name, pipeline_id: pipeline.id) end if result.not_run? || result.running? $stdout.puts "Workflow still running..." raise(WorkflowStillRunningError) end unless result.success? finish(ExitCodes::WorkflowNotSuccess, status: result.status) end result rescue WorkflowStillRunningError sleep(poll_interval) $stdout.puts "Retrying..." retry unless timed_out? finish(ExitCodes::ProcessTimeout, start_time: start_time) end end # rubocop:enable Metrics/AbcSize def jobs @_jobs ||= CircleCI::Job.all(workflow_id: workflow.id) end def artifacts @_artifacts ||= begin unless respond_to?(:"artifacts_from_#{source}") raise ArgumentError, "Uknown value for source option #{source}" end public_send(:"artifacts_from_#{source}") end end def artifacts_from_local Local::Artifact.all end def test_job @_test_job ||= jobs.detect { |job| job.name == Jiminy.config.ci_job_name } end def artifacts_from_circle_ci CircleCI::Artifact.all(job_number: test_job.job_number) end alias_method :artifacts_from_circleci, :artifacts_from_circle_ci end # rubocop:enable Metrics/BlockLength end end