Skip to content

Instantly share code, notes, and snippets.

@brand-it
Last active April 22, 2024 22:59

Revisions

  1. brand-it revised this gist Sep 25, 2023. 1 changed file with 10 additions and 9 deletions.
    19 changes: 10 additions & 9 deletions codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -287,7 +287,7 @@ end
    class ScanFailures
    attr_reader :failures

    START_MATCHER = /Failures:/
    START_MATCHER = /(Failures:|Failure\/Error:)/
    END_MATCHER = /Finished in \d+/

    def initialize(failures)
    @@ -430,16 +430,17 @@ begin
    when 'space', 'newline'
    progress_logs = Codefresh.progress_logs
    VerifyWorkflow.call(progress_logs)
    failed_pattern = %r{\[31mrspec \./(?<spec>spec/\S+:\d+)}
    logs = progress_logs.response['steps']&.flat_map { |s| s['logs'] }&.join || ''
    found = ScanFailures.new(logs).call
    # failed_pattern = %r{\[31mrspec \./(?<spec>spec/\S+:\d+)}
    failed_pattern = %r{(?<spec>spec/\S+_spec.rb:\d+)}
    found.select! { _1.match(failed_pattern) }
    .map! { _1.match(failed_pattern)[:spec].strip }
    .sort!
    .uniq!

    logs = progress_logs.response['steps']&.flat_map { |s| s['logs'] }&.join("\n")&.lines || []
    logs = logs.select { |l| l.include?('rspec ./') }.map(&:strip)
    join_with = ArgParser.options['format'] == 'newline' ? "\n" : ' '
    puts logs.flat_map { |l| (l.match(failed_pattern) || {})[:spec] }
    .compact
    .sort
    .uniq
    .join(join_with)
    puts found.join(join_with)
    when 'info'
    progress_logs = Codefresh.progress_logs
    VerifyWorkflow.call(progress_logs)
  2. brand-it revised this gist Sep 8, 2023. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -431,7 +431,8 @@ begin
    progress_logs = Codefresh.progress_logs
    VerifyWorkflow.call(progress_logs)
    failed_pattern = %r{\[31mrspec \./(?<spec>spec/\S+:\d+)}
    logs = progress_logs.response['steps']&.flat_map { |s| s['logs'] } || []

    logs = progress_logs.response['steps']&.flat_map { |s| s['logs'] }&.join("\n")&.lines || []
    logs = logs.select { |l| l.include?('rspec ./') }.map(&:strip)
    join_with = ArgParser.options['format'] == 'newline' ? "\n" : ' '
    puts logs.flat_map { |l| (l.match(failed_pattern) || {})[:spec] }
  3. brand-it revised this gist Mar 30, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -272,7 +272,7 @@ class Codefresh
    ]
    end
    if response.code == '301' && redirect_limit.positive?
    response = api_request(
    return api_request(
    response['location'],
    method: method,
    body: body,
  4. brand-it revised this gist Feb 14, 2023. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -390,7 +390,10 @@ class UpdateScript
    end
    begin
    update_script = UpdateScript.new
    update_script.update! if update_script.diff?
    if update_script.diff?
    update_script.update!
    puts UpdateScript::UPDATE_COMPLETE_TEXT
    end

    ArgParser.parser(COMMAND_NAME) do |ops|
    ops.on('-b', '--branch [NAME]', "Name of branch (default: #{GitInfo.branch})")
  5. brand-it revised this gist Feb 14, 2023. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -400,7 +400,7 @@ begin
    ops.on('-o', '--repo-owner [OWNER]', "Owner of the repo (default: #{GitInfo.repo_owner})")
    ops.on('-l', '--log-level [LEVEL]', "Log level [#{Logger::LEVELS.join(', ')}] (default: #{Logger::DEFAULT_LEVEL})")
    ops.on('-f', '--format [FORMAT]',
    'This will change the format from the default rspec spec/file:123 to somethings else [file, info, newline] (default: info)')
    'This will change the format from the default rspec spec/file:123 to somethings else [space, info, newline] (default: info)')
    ops.on('-a', '--api-token [TOKEN]',
    "Owner of the repo #{ENV['CODEFRESH_API_TOKEN'].to_s != '' ? "CODEFRESH_API_TOKEN=#{Logger.obscure(ENV['CODEFRESH_API_TOKEN'].split('.').last)}" : 'export CODEFRESH_API_TOKEN=<token>'} https://g.codefresh.io/user/settings")
    end
    @@ -424,7 +424,7 @@ begin
    Logger.debug { ArgParser.options }

    case ArgParser.options['format']
    when 'file', 'newline'
    when 'space', 'newline'
    progress_logs = Codefresh.progress_logs
    VerifyWorkflow.call(progress_logs)
    failed_pattern = %r{\[31mrspec \./(?<spec>spec/\S+:\d+)}
  6. brand-it revised this gist Feb 14, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -271,7 +271,7 @@ class Codefresh
    "Response Body: #{response.body[0..1000]}"
    ]
    end
    if response.code == 301 && redirect_limit.positive?
    if response.code == '301' && redirect_limit.positive?
    response = api_request(
    response['location'],
    method: method,
  7. brand-it revised this gist Feb 2, 2023. 1 changed file with 10 additions and 1 deletion.
    11 changes: 10 additions & 1 deletion codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -242,7 +242,7 @@ class Codefresh
    {}
    end

    def api_request(url, method: :get, body: nil, content_type: 'application/json')
    def api_request(url, method: :get, body: nil, content_type: 'application/json', redirect_limit: 5)
    uri = URI.parse(url)
    api_client = api_client(uri)
    request = Net::HTTP.const_get(method.to_s.capitalize).new([uri.path, uri.query].compact.join('?'))
    @@ -271,6 +271,15 @@ class Codefresh
    "Response Body: #{response.body[0..1000]}"
    ]
    end
    if response.code == 301 && redirect_limit.positive?
    response = api_request(
    response['location'],
    method: method,
    body: body,
    content_type: content_type,
    redirect_limit: (redirect_limit - 1)
    )
    end
    parse_json(response.body)
    end
    end
  8. brand-it revised this gist Feb 1, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -220,7 +220,7 @@ class Codefresh
    }
    path = "workflow?#{URI.encode_www_form(query)}"
    docs = api_request([API_URL, 'api', path].join('/')).dig('workflows', 'docs') || []
    @workflow = docs.find(-> {}) do |workflow|
    @workflow = docs.find(-> { {} }) do |workflow|
    workflow['serviceName'].downcase == ArgParser.options['workflow-name'].downcase &&
    ArgParser.options['repo-name'] == workflow['repoName']
    end
  9. brand-it revised this gist Feb 1, 2023. 1 changed file with 18 additions and 14 deletions.
    32 changes: 18 additions & 14 deletions codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -186,7 +186,7 @@ class GitInfo
    end

    class Codefresh
    API_URL = URI(ENV['CODEFRESH_API_URL'] || 'https://g.codefresh.io')
    API_URL = ENV['CODEFRESH_API_URL'] || 'https://g.codefresh.io'
    Response = Struct.new(:response, :workflow)

    class << self
    @@ -198,13 +198,13 @@ class Codefresh
    def progress_logs
    return Response.new(nil, workflow) if progress.nil?

    Response.new parse_json(Net::HTTP.get(URI.parse(progress.dig('location', 'url')))), workflow
    Response.new api_request(progress.dig('location', 'url'), content_type: nil), workflow
    end

    private

    def api_client
    @api_client ||= Net::HTTP.new(API_URL.host, API_URL.port).tap do |http|
    def api_client(url)
    Net::HTTP.new(url.host, url.port).tap do |http|
    http.use_ssl = true
    end
    end
    @@ -219,17 +219,18 @@ class Codefresh
    branchName: ArgParser.options['branch']
    }
    path = "workflow?#{URI.encode_www_form(query)}"
    docs = api_request(path).dig('workflows', 'docs') || []
    @workflow = docs.find(-> { {} }) do |workflow|
    workflow['serviceName'].downcase == ArgParser.options['workflow-name'].downcase
    docs = api_request([API_URL, 'api', path].join('/')).dig('workflows', 'docs') || []
    @workflow = docs.find(-> {}) do |workflow|
    workflow['serviceName'].downcase == ArgParser.options['workflow-name'].downcase &&
    ArgParser.options['repo-name'] == workflow['repoName']
    end
    end

    def progress
    return @progress if @progress
    return if workflow['progress'].nil? || workflow['status'] != 'error'

    @progress = api_request("progress/#{workflow['progress']}")
    @progress = api_request([API_URL, 'api', "progress/#{workflow['progress']}"].join('/'))
    end

    def parse_json(response, default = {})
    @@ -241,20 +242,23 @@ class Codefresh
    {}
    end

    def api_request(path, method = :get, body = nil)
    request = Net::HTTP.const_get(method.to_s.capitalize).new("/api/#{path}")
    def api_request(url, method: :get, body: nil, content_type: 'application/json')
    uri = URI.parse(url)
    api_client = api_client(uri)
    request = Net::HTTP.const_get(method.to_s.capitalize).new([uri.path, uri.query].compact.join('?'))
    request.body = body.to_json if body
    request.add_field('Content-Type', 'application/json')
    request.add_field('Content-Type', content_type) if content_type
    request.add_field('Authorization', "Bearer #{ArgParser.options['api-token']}")
    Logger.debug do
    [
    "Method: #{method}",
    "Path: #{path}",
    "URI: #{uri}",
    "Query: #{uri.query}",
    "Request Path: #{request.path}",
    "Body: #{body}",
    "Request: #{request.inspect}",
    "Bearer Token: #{Logger.obscure(ArgParser.options['api-token'].split('.').last)}",
    "API URL: #{API_URL}",
    "Host: #{uri.host}",
    "API Client: #{api_client.inspect}",
    "Headers: #{request.to_hash}"
    ]
    @@ -428,7 +432,7 @@ begin
    VerifyWorkflow.call(progress_logs)
    logs = progress_logs.response['steps']&.flat_map { |s| s['logs'] }&.join || ''
    found = ScanFailures.new(logs).call
    puts found.join("\n")
    puts found.empty? ? logs : found.join("\n")
    Logger.details(
    "View Build Here: #{Codefresh::API_URL}/build/#{progress_logs.workflow['id']}"
    )
  10. brand-it revised this gist Jan 25, 2023. 1 changed file with 2 additions and 4 deletions.
    6 changes: 2 additions & 4 deletions codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -144,8 +144,6 @@ class ArgParser
    @options ||= {}
    end

    private

    def parse!
    self.class.parser.parse!(into: options)
    options.transform_keys!(&:to_s)
    @@ -302,8 +300,8 @@ class ScanFailures
    end

    class UpdateScript
    GIST_URL = URI('https://api.github.com/gists/762887222f95e802585787b67246f995')
    GIST_LINK = 'https://gist.github.com/brand-it/762887222f95e802585787b67246f995'
    GIST_URL = URI('https://api.github.com/gists/972f92815888a62c45a02bf34c6aa3ea')
    GIST_LINK = 'https://gist.github.com/brand-it/972f92815888a62c45a02bf34c6aa3ea'
    ONE_DAY = 86_400
    UPDATE_COMPLETE_TEXT = <<~TXT
    ███████╗███╗ ██╗██╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗
  11. brand-it created this gist Jan 13, 2023.
    446 changes: 446 additions & 0 deletions codefresh-rspec
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,446 @@
    #!/usr/bin/env ruby
    # frozen_string_literal: true

    require 'uri'
    require 'json'
    require 'net/http'
    require 'optparse'
    require 'forwardable'

    COMMAND_NAME = File.basename(__FILE__)

    # A nice way to add color to strings
    class PrettyString < String
    # https://no-color.org/
    NO_COLOR = ENV.key?('NO_COLOR') || `tput colors`.chomp.to_i < 8
    ANSI_COLORS = {
    white: 0,
    red: 31,
    green: 32,
    yellow: 33,
    blue: 34,
    magenta: 35
    }.freeze

    ANSI_COLORS.each do |name, code|
    define_method(name) { NO_COLOR ? self : "\e[#{code}m#{self}\e[0m" }
    end
    end

    class VerifyWorkflow
    def self.call(codefresh_response)
    if codefresh_response.workflow.empty?
    Logger.warn "Could not find a worflow for #{ArgParser.options['repo-name']}/#{ArgParser.options['repo-owner']} (#{ArgParser.options['branch']})"
    exit 1
    elsif codefresh_response.workflow['status'] != 'error'
    Logger.warn(
    "Current workflow is status is #{codefresh_response.workflow['status']} and has to be error"
    )
    Logger.details(
    "View Build Here: #{Codefresh::API_URL}/build/#{codefresh_response.workflow['id']}"
    )
    exit 1
    end
    end
    end

    # Standard logging to STDOUT
    # Logger.info('foo')
    # Logger.info('foo', 'bar')
    # Logger.debug { ['foo', 'bar'] }
    # Logger.obscure('something something')
    # Logger.level = :debug
    class Logger
    LEVELS = %i[debug info warn error].freeze
    DEFAULT_LEVEL = :info
    class << self
    def level=(level)
    @level = LEVELS.index(level&.to_sym)
    end

    def level
    @level ||= LEVELS.index(DEFAULT_LEVEL)
    end

    def info(*messages)
    log(messages, :white) if loggable?(:info)
    end

    def error(*messages)
    log(messages, :red) if loggable?(:error)
    end

    def warn(*messages)
    log(messages, :yellow) if loggable?(:warn)
    end

    def success(*messages)
    log(messages, :green) if loggable?(:info)
    end

    def details(*messages)
    log(messages, :blue) if loggable?(:info)
    end

    def debug
    log(yield, :magenta) if loggable?(:debug)
    end

    def loggable?(l)
    LEVELS.index(l) >= level
    end

    def log(*messages, color)
    Array(messages).flatten.each do |message|
    puts PrettyString.new(message.to_s).public_send(color)
    end
    end

    def obscure(string, length = 6)
    string = string.to_s
    total_obscured = [10, length].max
    "#{string[0, length]}#{'*' * total_obscured}"
    end
    end
    end

    # Usage
    # ArgParser.parser('transitions') do |ops|
    # ops.on('-t', '--transition-to', 'Prints this help message')
    # end
    #
    # ArgParser.parser.require('transitions')
    #
    # calling options will excute a parse and then return a hash of options
    # if you defined a option of --transition-to the key will be 'transition-to'
    # ArgParser.options
    class ArgParser
    class << self
    def parser(command_name = nil)
    @parser ||= OptionParser.new do |opts|
    opts.banner = "Usage: #{command_name} [options]"
    yield opts
    opts.on('-h', '--help', 'Prints this help message') do
    Logger.info opts.to_s
    exit
    end
    end
    end

    def require(key)
    return if options[key].to_s != ''

    Logger.info parser
    Logger.error "Missing option: --#{key}"
    exit 1
    end

    def options
    @options ||= new.tap(&:parse!).options
    end
    end

    def options
    @options ||= {}
    end

    private

    def parse!
    self.class.parser.parse!(into: options)
    options.transform_keys!(&:to_s)
    rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
    Logger.info self.class.parser
    Logger.error "Invalid option: #{e.message}"
    exit 1
    end
    end

    class GitInfo
    REMOTE_URL_MATCHER = %r{(?<host>\S+)(:|/)(?<repo_owner>\S+)/(?<repo_name>\S+)\.(?<prefix>\S+)}.freeze
    class << self
    def github_url
    @github_url ||= "https://github.com/#{repo_path}"
    end

    def repo_path
    @repo_path ||= "#{repo_owner}/#{repo_name}"
    end

    def repo_name
    @repo_name ||= remote_origin_url[:repo_name]
    end

    def repo_owner
    @repo_owner ||= remote_origin_url[:repo_owner]
    end

    def branch
    @branch ||= `git rev-parse --abbrev-ref HEAD`.chomp.strip
    end

    private

    def remote_origin_url
    `git config --get remote.origin.url`.match(REMOTE_URL_MATCHER) || {}
    end
    end
    end

    class Codefresh
    API_URL = URI(ENV['CODEFRESH_API_URL'] || 'https://g.codefresh.io')
    Response = Struct.new(:response, :workflow)

    class << self
    def progress_logs
    new.progress_logs
    end
    end

    def progress_logs
    return Response.new(nil, workflow) if progress.nil?

    Response.new parse_json(Net::HTTP.get(URI.parse(progress.dig('location', 'url')))), workflow
    end

    private

    def api_client
    @api_client ||= Net::HTTP.new(API_URL.host, API_URL.port).tap do |http|
    http.use_ssl = true
    end
    end

    def workflow
    return @workflow if @workflow

    query = {
    limit: 20,
    repoName: ArgParser.options['repo-name'],
    repoOwner: ArgParser.options['repo-owner'],
    branchName: ArgParser.options['branch']
    }
    path = "workflow?#{URI.encode_www_form(query)}"
    docs = api_request(path).dig('workflows', 'docs') || []
    @workflow = docs.find(-> { {} }) do |workflow|
    workflow['serviceName'].downcase == ArgParser.options['workflow-name'].downcase
    end
    end

    def progress
    return @progress if @progress
    return if workflow['progress'].nil? || workflow['status'] != 'error'

    @progress = api_request("progress/#{workflow['progress']}")
    end

    def parse_json(response, default = {})
    return default if response.to_s == ''

    JSON.parse(response)
    rescue JSON::ParserError
    Logger.error "Failed to parse response: #{response}"
    {}
    end

    def api_request(path, method = :get, body = nil)
    request = Net::HTTP.const_get(method.to_s.capitalize).new("/api/#{path}")
    request.body = body.to_json if body
    request.add_field('Content-Type', 'application/json')
    request.add_field('Authorization', "Bearer #{ArgParser.options['api-token']}")
    Logger.debug do
    [
    "Method: #{method}",
    "Path: #{path}",
    "Request Path: #{request.path}",
    "Body: #{body}",
    "Request: #{request.inspect}",
    "Bearer Token: #{Logger.obscure(ArgParser.options['api-token'].split('.').last)}",
    "API URL: #{API_URL}",
    "API Client: #{api_client.inspect}",
    "Headers: #{request.to_hash}"
    ]
    end
    response = api_client.request(request)
    Logger.debug do
    [
    "Response Code: #{response.code}",
    "Response Message: #{response.message}",
    "Response Body: #{response.body[0..1000]}"
    ]
    end
    parse_json(response.body)
    end
    end

    class ScanFailures
    attr_reader :failures

    START_MATCHER = /Failures:/
    END_MATCHER = /Finished in \d+/

    def initialize(failures)
    @failures = failures.lines.map(&:strip)
    end

    def call
    capture = false
    failures.each_with_object([]) do |failure, result|
    if failure =~ END_MATCHER
    Logger.debug { "End Capture at: #{failure}" }
    capture = false
    result << failure
    elsif failure =~ START_MATCHER
    Logger.debug { "Start Capture at: #{failure}" }
    capture = true
    result << failure
    elsif capture
    result << failure
    end
    end
    end
    end

    class UpdateScript
    GIST_URL = URI('https://api.github.com/gists/762887222f95e802585787b67246f995')
    GIST_LINK = 'https://gist.github.com/brand-it/762887222f95e802585787b67246f995'
    ONE_DAY = 86_400
    UPDATE_COMPLETE_TEXT = <<~TXT
    ███████╗███╗ ██╗██╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗
    ██╔════╝████╗ ██║██║ ██║██╔══██╗████╗ ██║██╔════╝██╔════╝
    █████╗ ██╔██╗ ██║███████║███████║██╔██╗ ██║██║ █████╗
    ██╔══╝ ██║╚██╗██║██╔══██║██╔══██║██║╚██╗██║██║ ██╔══╝
    ███████╗██║ ╚████║██║ ██║██║ ██║██║ ╚████║╚██████╗███████╗
    ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝╚══════╝
    ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗ ███████╗████████╗███████╗
    ██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║ ██╔════╝╚══██╔══╝██╔════╝
    ██║ ██║ ██║██╔████╔██║██████╔╝██║ █████╗ ██║ █████╗
    ██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║ ██╔══╝ ██║ ██╔══╝
    ╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ███████╗███████╗ ██║ ███████╗
    ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚══════╝
    TXT

    def diff?
    not_changed_recently? && diffs.any?
    end

    def diffs
    return @diffs if defined? @diffs

    @diffs = [] unless get
    unless get && get.dig(:files, :'codefresh-rspec', :content)
    touch
    @diffs = []
    end
    @diffs ||= reject_blanks(get.dig(:files, :'codefresh-rspec', :content).lines) - reject_blanks(file.lines.compact)
    end

    def reject_blanks(array)
    array.reject { |s| s.strip.empty? }
    end

    def not_changed_recently?
    updated_last + ONE_DAY < Time.now
    end

    def update!
    File.open(__FILE__, 'w') do |file|
    file.write(get.dig(:files, :'codefresh-rspec', :content))
    end
    end

    def touch
    File.lutime(Time.now, Time.now, __FILE__)
    end

    def updated_last
    File.mtime(__FILE__)
    end

    def file
    File.read(__FILE__)
    end

    def lines
    @lines ||= get&.dig(:files, :'codefresh-rspec', :content)&.lines || []
    end

    def get
    @get ||= JSON.parse(Net::HTTP.get(GIST_URL), symbolize_names: true)
    rescue StandardError => e
    puts e.message.to_s
    nil
    end

    def show_diffs
    diffs
    end
    end
    begin
    update_script = UpdateScript.new
    update_script.update! if update_script.diff?

    ArgParser.parser(COMMAND_NAME) do |ops|
    ops.on('-b', '--branch [NAME]', "Name of branch (default: #{GitInfo.branch})")
    ops.on('-n', '--repo-name [NAME]', "Name of the repo (default: #{GitInfo.repo_name})")
    ops.on('-w', '--workflow-name [NAME]',
    "Name of the workflow build branch #{ENV['CODEFRESH_BUILD_WORKFLOW_NAME'] || 'export CODEFRESH_BUILD_WORKFLOW_NAME'} (default: build)")
    ops.on('-o', '--repo-owner [OWNER]', "Owner of the repo (default: #{GitInfo.repo_owner})")
    ops.on('-l', '--log-level [LEVEL]', "Log level [#{Logger::LEVELS.join(', ')}] (default: #{Logger::DEFAULT_LEVEL})")
    ops.on('-f', '--format [FORMAT]',
    'This will change the format from the default rspec spec/file:123 to somethings else [file, info, newline] (default: info)')
    ops.on('-a', '--api-token [TOKEN]',
    "Owner of the repo #{ENV['CODEFRESH_API_TOKEN'].to_s != '' ? "CODEFRESH_API_TOKEN=#{Logger.obscure(ENV['CODEFRESH_API_TOKEN'].split('.').last)}" : 'export CODEFRESH_API_TOKEN=<token>'} https://g.codefresh.io/user/settings")
    end

    ArgParser.options.tap do |options|
    Logger.level = options['log-level']
    options['branch'] ||= GitInfo.branch
    options['repo-name'] ||= GitInfo.repo_name
    options['repo-owner'] ||= GitInfo.repo_owner
    options['api-token'] ||= ENV['CODEFRESH_API_TOKEN']
    options['workflow-name'] ||= ENV['CODEFRESH_BUILD_WORKFLOW_NAME'] || 'build'
    options['format'] ||= 'info'
    end

    ArgParser.require('branch')
    ArgParser.require('repo-name')
    ArgParser.require('repo-owner')
    ArgParser.require('api-token')
    ArgParser.require('format')

    Logger.debug { ArgParser.options }

    case ArgParser.options['format']
    when 'file', 'newline'
    progress_logs = Codefresh.progress_logs
    VerifyWorkflow.call(progress_logs)
    failed_pattern = %r{\[31mrspec \./(?<spec>spec/\S+:\d+)}
    logs = progress_logs.response['steps']&.flat_map { |s| s['logs'] } || []
    logs = logs.select { |l| l.include?('rspec ./') }.map(&:strip)
    join_with = ArgParser.options['format'] == 'newline' ? "\n" : ' '
    puts logs.flat_map { |l| (l.match(failed_pattern) || {})[:spec] }
    .compact
    .sort
    .uniq
    .join(join_with)
    when 'info'
    progress_logs = Codefresh.progress_logs
    VerifyWorkflow.call(progress_logs)
    logs = progress_logs.response['steps']&.flat_map { |s| s['logs'] }&.join || ''
    found = ScanFailures.new(logs).call
    puts found.join("\n")
    Logger.details(
    "View Build Here: #{Codefresh::API_URL}/build/#{progress_logs.workflow['id']}"
    )
    else
    Logger.info ArgParser.parser.help
    Logger.error "Invalid format #{ArgParser.options['format']} use file or info"

    exit 1
    end
    rescue Interrupt
    puts 'Interrupted'
    exit
    end