diff --git a/.git-commands/start.rb b/.git-commands/start.rb new file mode 100644 index 0000000..f40b6a3 --- /dev/null +++ b/.git-commands/start.rb @@ -0,0 +1,17 @@ +plugin :github + +command :start do |cmd| + cmd.summary "Start work on a git-commander issue" + + cmd.helper :github_setup? do + !git.config["github.site"].to_s.strip.emtpy? && + !git.config["github.api"].to_s.strip.emtpy? && + !git.config["github.login"].to_s.strip.emtpy? && + !git.config["github.token"].to_s.strip.emtpy? + end + + cmd.on_run do |cmd| + run "github:setup", [GitCommander::Command::Option.new(name: :silence_success, value: true)] unless github_setup? + say "Woot" + end +end diff --git a/.rubocop.yml b/.rubocop.yml index 00c2189..84c8b26 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,6 +4,9 @@ Layout/DotPosition: Metrics/LineLength: Max: 120 +Style/HashSyntax: + EnforcedStyle: ruby19_no_mixed_keys + EnforcedShorthandSyntax: either Style/StringLiterals: EnforcedStyle: double_quotes ConsistentQuotesInMultiline: true diff --git a/lib/git_commander/command/runner.rb b/lib/git_commander/command/runner.rb index 76e8315..a6efcbd 100644 --- a/lib/git_commander/command/runner.rb +++ b/lib/git_commander/command/runner.rb @@ -25,12 +25,16 @@ def say(message) end def respond_to_missing?(method_sym, include_all = false) + binding.irb if method_sym == :git plugin_executor(method_sym).respond_to?(method_sym, include_all) || + has_helper?(method_sym) || super(method_sym, include_all) end def method_missing(method_sym, *arguments, &block) + binding.irb if method_sym == :git return plugin_executor(method_sym) if plugin_executor(method_sym) + return run_helper(method_sym, *arguments) if has_helper?(method_sym) super end @@ -40,6 +44,15 @@ def method_missing(method_sym, *arguments, &block) def plugin_executor(plugin_name) @plugin_executor ||= command.registry.find_plugin(plugin_name)&.executor end + + def has_helper?(helper_name) + command.helpers.has_key?(helper_name) + end + + def run_helper(helper_name, *arguments) + GitCommander.logger.info "Running helper '#{helper_name}' with arguments: #{arguments.inspect}" + instance_exec(arguments, &command.helpers[helper_name]) + end end end end diff --git a/lib/git_commander/command_loader_options.rb b/lib/git_commander/command_loader_options.rb index c1d6e88..ea72a5c 100644 --- a/lib/git_commander/command_loader_options.rb +++ b/lib/git_commander/command_loader_options.rb @@ -3,6 +3,16 @@ module GitCommander # Establishes values to be set by loaders module CommandLoaderOptions + module HelperHooks + def add_helper_method(helper_name, &block) + define_method(helper_name, &block) + end + end + + def self.included(klass) + klass.extend HelperHooks + end + def summary(value = nil) return @summary = value if value @@ -30,5 +40,14 @@ def switch(switch_name, options = {}) def on_run(&on_run) @block = on_run end + + def helpers + @helpers ||= {} + end + + def helper(helper_name, &block) + self.class.add_helper_method helper_name, &block + helpers[helper_name] = block + end end end diff --git a/lib/git_commander/plugin/loader.rb b/lib/git_commander/plugin/loader.rb index c51bd57..b08e6f0 100644 --- a/lib/git_commander/plugin/loader.rb +++ b/lib/git_commander/plugin/loader.rb @@ -20,14 +20,16 @@ def initialize(registry) end def load(name) - @plugin = GitCommander::Plugin.new( - resolve_plugin_name(name), - source_instance: instance_eval(resolve_content(name)) - ) - @plugin.commands = @commands - result.plugins << @plugin - result.commands |= @commands - result + Bundler.with_original_env do + @plugin = GitCommander::Plugin.new( + resolve_plugin_name(name), + source_instance: instance_eval(resolve_content(name)) + ) + @plugin.commands = @commands + result.plugins << @plugin + result.commands |= @commands + result + end rescue Errno::ENOENT, Errno::EACCES => e handle_error LoadError, e rescue StandardError => e @@ -57,7 +59,13 @@ def command(name, &block) def plugin(name, **options) plugin_result = GitCommander::Plugin::Loader.new(registry).load(name, **options) - result.plugins |= plugin_result.plugins + if plugin_result.success? + result.plugins |= plugin_result.plugins + else + binding.irb + result.errors |= plugin_result.errors + end + plugin_result end private diff --git a/lib/git_commander/plugins/github.rb b/lib/git_commander/plugins/github.rb index 31cc55b..18ed93d 100644 --- a/lib/git_commander/plugins/github.rb +++ b/lib/git_commander/plugins/github.rb @@ -10,22 +10,39 @@ command :setup do |cmd| cmd.summary "Connects to GitHub, creates an access token, and stores it in the git-cmd section of your git config" + cmd.flag :silence_success, default: false, description: "Silence output when already setup" + + cmd.helper :github_setup? do + !git.config["github.site"].to_s.strip.emtpy? && + !git.config["github.api"].to_s.strip.emtpy? && + !git.config["github.login"].to_s.strip.emtpy? && + !git.config["github.token"].to_s.strip.emtpy? + end + + cmd.on_run do |options| + if github_setup? + return if options[:silence_success] + + say <<~ALREADY_SETUP + GitHub already setup: + \tsite: #{git.config["github.site"]} + \tapi: #{git.config["github.api"]} + \tlogin: #{git.config["github.login"]} + ALREADY_SETUP + end - cmd.on_run do gh_user = prompt.ask("Please enter your GitHub username", required: true) - gh_password = promt.mask("Please enter your GitHub password (this is NOT stored): ", required: true) + + say "Visit https://github.com/settings/tokens if you don't already have an access token." + gh_token = prompt.mask("Enter your token: ", required: true) github.login = gh_user - github.password = gh_password + github.access_token = gh_token - # Check for 2-factor requirements begin github.user - rescue Octokit::Unauthorized - github.user( - gh_user, - headers: { "X-GitHub-OTP" => prompt.ask("Please enter your two-factor authentication code") } - ) + rescue Octokit::Unauthorized => e + fail "Unable to authenticate your GitHub credentials: #{e}" end say "GitHub account successfully setup!" diff --git a/spec/lib/git_commander/command/configurator_spec.rb b/spec/lib/git_commander/command/configurator_spec.rb index 848854e..29afe6d 100644 --- a/spec/lib/git_commander/command/configurator_spec.rb +++ b/spec/lib/git_commander/command/configurator_spec.rb @@ -20,6 +20,14 @@ cmd.flag :as_question, default: false cmd.switch :loud, default: false + cmd.helper :yell do |saying| + say saying.to_s.upcase + end + + cmd.helper :woot do + yell :woot + end + cmd.on_run do |options| response = options[:greeting].dup response += ", \#{options[:name]}" unless options[:name].to_s.empty? @@ -53,6 +61,43 @@ expect(output).to have_received(:puts).with "SALUTATIONS." end + it "allows setting a helper method" do + registered_command = configurator.configure(:parrot) do |cmd| + cmd.summary "The ultimate copy-cat." + cmd.argument :saying, default: "SQUAWK" + + cmd.helper :yell do |saying| + say saying.to_s.upcase + end + + cmd.helper :woot do + yell :woot + end + + cmd.on_run do |options| + yell options[:saying] + woot + end + end + + expect(registered_command.summary).to eq "The ultimate copy-cat." + expect(registered_command.description).to eq nil + expect(registered_command.arguments.size).to eq 1 + expect(registered_command.arguments.first.name).to eq :saying + expect(registered_command.arguments.first.default).to eq "SQUAWK" + expect(registered_command.flags.size).to eq 0 + expect(registered_command.switches.size).to eq 0 + + output = spy("output") + registered_command.output = output + + registered_command.run [ + GitCommander::Command::Option.new(name: :saying, value: "Salutations") + ] + expect(output).to have_received(:puts).with "SALUTATIONS" + expect(output).to have_received(:puts).with "WOOT" + end + it "rescues syntax errors and reports them in the LoaderResult" do expect do configurator.configure(:danger) do |cmd| # rubocop:disable Style/SymbolProc diff --git a/spec/lib/git_commander/plugin/loader_spec.rb b/spec/lib/git_commander/plugin/loader_spec.rb index 0f64d18..da6f90a 100644 --- a/spec/lib/git_commander/plugin/loader_spec.rb +++ b/spec/lib/git_commander/plugin/loader_spec.rb @@ -49,6 +49,25 @@ expect(plugin.executor).to respond_to(:run) end + it "loads dependent plugins for the plugin" do + allow(File).to receive(:read).and_call_original + allow(File).to receive(:read) + .with("#{described_class::NATIVE_PLUGIN_DIR}/plugin_with_commands.rb") + .and_return( + <<~COMMAND + plugin :git + COMMAND + ) + + result = loader.load(:plugin_with_commands) + + expect(result).to be_success + binding.irb + + expect(result.plugins.map(&:name)).to include :plugin_with_commands + expect(result.plugins.map(&:name)).to include :git + end + it "reports NotFound if the native plugin doesn't exist" do result = loader.load(:bubbles)