Last active
April 22, 2024 22:59
Revisions
-
brand-it revised this gist
Sep 25, 2023 . 1 changed file with 10 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -287,7 +287,7 @@ end class ScanFailures attr_reader :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) 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! join_with = ArgParser.options['format'] == 'newline' ? "\n" : ' ' puts found.join(join_with) when 'info' progress_logs = Codefresh.progress_logs VerifyWorkflow.call(progress_logs) -
brand-it revised this gist
Sep 8, 2023 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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'] }&.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] } -
brand-it revised this gist
Mar 30, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -272,7 +272,7 @@ class Codefresh ] end if response.code == '301' && redirect_limit.positive? return api_request( response['location'], method: method, body: body, -
brand-it revised this gist
Feb 14, 2023 . 1 changed file with 4 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -390,7 +390,10 @@ class UpdateScript end begin update_script = UpdateScript.new 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})") -
brand-it revised this gist
Feb 14, 2023 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 [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 'space', 'newline' progress_logs = Codefresh.progress_logs VerifyWorkflow.call(progress_logs) failed_pattern = %r{\[31mrspec \./(?<spec>spec/\S+:\d+)} -
brand-it revised this gist
Feb 14, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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? response = api_request( response['location'], method: method, -
brand-it revised this gist
Feb 2, 2023 . 1 changed file with 10 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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', 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 -
brand-it revised this gist
Feb 1, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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['serviceName'].downcase == ArgParser.options['workflow-name'].downcase && ArgParser.options['repo-name'] == workflow['repoName'] end -
brand-it revised this gist
Feb 1, 2023 . 1 changed file with 18 additions and 14 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -186,7 +186,7 @@ class GitInfo end class Codefresh 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 api_request(progress.dig('location', 'url'), content_type: nil), workflow end private 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([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([API_URL, 'api', "progress/#{workflow['progress']}"].join('/')) end def parse_json(response, default = {}) @@ -241,20 +242,23 @@ class Codefresh {} end 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', content_type) if content_type request.add_field('Authorization', "Bearer #{ArgParser.options['api-token']}") Logger.debug do [ "Method: #{method}", "URI: #{uri}", "Query: #{uri.query}", "Request Path: #{request.path}", "Body: #{body}", "Request: #{request.inspect}", "Bearer Token: #{Logger.obscure(ArgParser.options['api-token'].split('.').last)}", "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.empty? ? logs : found.join("\n") Logger.details( "View Build Here: #{Codefresh::API_URL}/build/#{progress_logs.workflow['id']}" ) -
brand-it revised this gist
Jan 25, 2023 . 1 changed file with 2 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -144,8 +144,6 @@ class ArgParser @options ||= {} end 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/972f92815888a62c45a02bf34c6aa3ea') GIST_LINK = 'https://gist.github.com/brand-it/972f92815888a62c45a02bf34c6aa3ea' ONE_DAY = 86_400 UPDATE_COMPLETE_TEXT = <<~TXT ███████╗███╗ ██╗██╗ ██╗ █████╗ ███╗ ██╗ ██████╗███████╗ -
brand-it created this gist
Jan 13, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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