lib/gurney/cli.rb in gurney_client-0.3.0 vs lib/gurney/cli.rb in gurney_client-0.4.0

- old
+ new

@@ -4,120 +4,128 @@ require 'open3' require 'git' require 'fileutils' module Gurney + + class Error < StandardError; end + class CLI HOOK_STDIN_REGEX = /(?<old>[0-9a-f]{40}) (?<new>[0-9a-f]{40}) refs\/heads\/(?<ref>\w+)/m CLIENT_HOOK_STDIN_REGEX = /refs\/heads\/(?<ref>\w+) (?<new>[0-9a-f]{40}) refs\/heads\/(?<remote_ref>\w+) (?<remote_sha>[0-9a-f]{40})/m MAIN_BRANCHES = ['master', 'main'].freeze def self.run(cmd_parameter=[]) - options = Gurney::CLI::OptionParser.parse(cmd_parameter) + new(cmd_parameter).run + rescue SystemExit + # Do nothing + rescue Gurney::ApiError => e + puts "Gurney API error".red + puts e.message.red + rescue Gurney::Error => e + puts "Gurney error: #{e.message}".red + rescue Exception + puts "Gurney: an unexpected error occurred".red + raise + end - begin - if options.hook - g = Git.bare(ENV['GIT_DIR'] || Dir.pwd) - else - unless Dir.exist? './.git' - raise Gurney::Error.new('Must be run within a git repository') - end - g = Git.open('.') + def initialize(cmd_parameter=[]) + @options = Gurney::CLI::OptionParser.parse(cmd_parameter) + @git = if options.hook + Git.bare(ENV['GIT_DIR'] || Dir.pwd) + else + unless Dir.exist? './.git' + raise Gurney::Error.new('Must be run within a git repository') end - config_file = MAIN_BRANCHES.find do |branch| - file = read_file(g, options.hook, branch, options.config_file) - break file if file - end - if !config_file && options.hook - # dont run as a hook with no config - exit 0 - end - config_file ||= '---' - config = Gurney::Config.from_yaml(config_file) + Git.open('.') + end - options.branches ||= config&.branches - options.branches ||= config&.branches - options.api_token ||= config&.api_token - options.api_url ||= config&.api_url - options.project_id ||= config&.project_id + config_file = MAIN_BRANCHES.find do |branch| + file = read_file(options.hook, branch, options.config_file) + break file if file + end + if options.hook && !config_file + # Git hooks are activated by the config file. Without, do nothing. + exit 0 + end + config_file ||= '---' + config = Gurney::Config.from_yaml(config_file) - if [options.project_id, options.branches, options.api_url, options.api_token].any?(&:nil?) - raise Gurney::Error.new("Either provide in a config file or set the flags for project id, branches, api url and api token") - end + options.branches ||= config&.branches + options.branches ||= config&.branches + options.api_token ||= config&.api_token + options.api_url ||= config&.api_url + options.project_id ||= config&.project_id - branches = [] - if options.hook || options.client_hook - # we get passed changed branches and refs via stdin - $stdin.each_line do |line| - regex = options.client_hook ? CLIENT_HOOK_STDIN_REGEX : HOOK_STDIN_REGEX - line.force_encoding(Encoding::UTF_8) - matches = line.match(regex) - if matches && matches[:new] != '0' * 40 - if options.branches.include? matches[:ref] - branches << matches[:ref] - end - end - end + missing_options = [:project_id, :branches, :api_url, :api_token].select { |option| options.send(option).nil? } + # Use the line below in development + # missing_options = [:project_id, :branches, :api_token].select { |option| options.send(option).nil? } + raise Gurney::Error.new("Incomplete config - missing #{missing_options.map(&:inspect).join(', ')}.") unless missing_options.empty? + end - else - current_branch = g.current_branch - unless options.branches.nil? || options.branches.include?(current_branch) - raise Gurney::Error.new('The current branch is not specified in the config.') - end - branches << current_branch - end + def run + reporting_branches.each do |branch| + dependencies = [] - branches.each do |branch| - dependencies = [] + yarn_source = Gurney::Source::Yarn.new(yarn_lock: read_file(options.hook || options.client_hook, branch, 'yarn.lock')) + dependencies.concat yarn_source.dependencies || [] - yarn_source = Gurney::Source::Yarn.new(yarn_lock: read_file(g, options.hook || options.client_hook, branch, 'yarn.lock')) - dependencies.concat yarn_source.dependencies || [] + bundler_source = Gurney::Source::Bundler.new(gemfile_lock: read_file(options.hook || options.client_hook, branch, 'Gemfile.lock')) + dependencies.concat bundler_source.dependencies || [] - bundler_source = Gurney::Source::Bundler.new(gemfile_lock: read_file(g, options.hook || options.client_hook, branch, 'Gemfile.lock')) - dependencies.concat bundler_source.dependencies || [] + ruby_version_source = Gurney::Source::RubyVersion.new(ruby_version: read_file(options.hook || options.client_hook, branch, '.ruby-version')) + dependencies.concat ruby_version_source.dependencies || [] - ruby_version_source = Gurney::Source::RubyVersion.new(ruby_version: read_file(g, options.hook || options.client_hook, branch, '.ruby-version')) - dependencies.concat ruby_version_source.dependencies || [] + dependencies.compact! - dependencies.compact! + api = Gurney::Api.new(base_url: options.api_url, token: options.api_token) + api.post_dependencies(dependencies: dependencies, branch: branch, project_id: options.project_id, repo_path: git.repo.path) - api = Gurney::Api.new(base_url: options.api_url, token: options.api_token) - api.post_dependencies(dependencies: dependencies, branch: branch, project_id: options.project_id) - - dependency_counts = dependencies.group_by(&:ecosystem).map{|ecosystem, dependencies| "#{ecosystem}: #{dependencies.count}" }.join(', ') - puts "Gurney: reported dependencies (#{dependency_counts})" - end - - rescue SystemExit - rescue Gurney::ApiError => e - puts "Gurney: api error".red - puts e.message.red - rescue Gurney::Error => e - puts "Gurney: error".red - puts e.message.red - rescue Exception => e - puts "Gurney: an unexpected error occurred".red - raise + dependency_counts = dependencies.group_by(&:ecosystem).map{|ecosystem, dependencies| "#{ecosystem}: #{dependencies.count}" }.join(', ') + puts "Gurney: reported dependencies (#{dependency_counts})" end end private - def self.read_file(git, from_git, branch, filename) + attr_accessor :git, :options + + def reporting_branches + branches = [] + if options.hook || options.client_hook + # We get changed branches and refs via stdin + # See https://git-scm.com/docs/githooks#post-receive + $stdin.each_line do |line| + regex = options.client_hook ? CLIENT_HOOK_STDIN_REGEX : HOOK_STDIN_REGEX + line.force_encoding(Encoding::UTF_8) + matches = line.match(regex) + if matches && matches[:new] != '0' * 40 + if options.branches.include? matches[:ref] + branches << matches[:ref] + end + end + end + else + current_branch = git.current_branch + unless options.branches.nil? || options.branches.include?(current_branch) + raise Gurney::Error.new('The current branch is not specified in the config.') + end + branches << current_branch + end + + branches + end + + def read_file(from_git, branch, filename) if from_git begin git.show("#{branch}:#{filename}") rescue Git::GitExecuteError # happens if branch does not exist end else - if File.exist? filename - return File.read filename - end + File.read(filename) if File.exist?(filename) end end - end - - class Error < Exception end end