diff --git a/.circleci/config.yml b/.circleci/config.yml index 13b8850..56e5bb2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,7 +29,7 @@ jobs: - run: name: install dependencies command: | - gem install bundler + gem install bundler -v '~> 1.17' bundle install --jobs=4 --retry=3 --path vendor/bundle # - save_cache: diff --git a/.gitignore b/.gitignore index de5eb49..1ebeaba 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ .rspec_status *.gem +Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 0493fbb..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,38 +0,0 @@ -PATH - remote: . - specs: - git_switch (0.3.2) - -GEM - remote: https://rubygems.org/ - specs: - diff-lcs (1.3) - rake (10.5.0) - rspec (3.8.0) - rspec-core (~> 3.8.0) - rspec-expectations (~> 3.8.0) - rspec-mocks (~> 3.8.0) - rspec-core (3.8.0) - rspec-support (~> 3.8.0) - rspec-expectations (3.8.2) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-mocks (3.8.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-support (3.8.0) - rspec_junit_formatter (0.4.1) - rspec-core (>= 2, < 4, != 2.12.0) - -PLATFORMS - ruby - -DEPENDENCIES - bundler (~> 1.16) - git_switch! - rake (~> 10.0) - rspec (~> 3.0) - rspec_junit_formatter (~> 0.4.1) - -BUNDLED WITH - 1.16.2 diff --git a/README.md b/README.md index ab09579..f9f25ca 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,9 @@ $ rvm @global do gem install git_switch ## Configuration -Although a guided setup is planned, for now you must manually create your configuration at `~/.gitswitch`. The config file is in YAML format. Here is an example. +To run the guided setup, use `git switch --config`. + +You may also manually create or edit your configuration at `~/.gitswitch`. The config file is in YAML format. Here is an example. ``` personal: @@ -42,7 +44,7 @@ The root keys can be any nickname you want. It should be memorable to make it ea ## Usage -Git Switch follows the convention to create a custom git command. It can be invoked as follows, to either set your git profile locally or globally: +Git Switch follows the convention to create a custom git command. It can be invoked as follows, to either set your git profile locally (no flag) or globally (`-g`): ``` $ git switch personal diff --git a/git_switch.gemspec b/git_switch.gemspec index 8cf490f..1bb146b 100644 --- a/git_switch.gemspec +++ b/git_switch.gemspec @@ -6,6 +6,7 @@ require "git_switch/version" Gem::Specification.new do |spec| spec.name = "git_switch" spec.version = GitSwitch::VERSION + spec.platform = Gem::Platform::RUBY spec.authors = ["Randall Reed, Jr."] spec.email = ["randallreedjr@gmail.com"] @@ -27,7 +28,9 @@ Gem::Specification.new do |spec| spec.executables << 'git-switch' spec.require_paths = ["lib"] - spec.add_development_dependency "bundler", "~> 1.16" + spec.add_runtime_dependency "activesupport", "~> 5.2" + + spec.add_development_dependency "bundler", "~> 1.17" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "rspec_junit_formatter", "~> 0.4.1" diff --git a/lib/git_switch/config.rb b/lib/git_switch/config.rb index 9504849..8d6196b 100644 --- a/lib/git_switch/config.rb +++ b/lib/git_switch/config.rb @@ -42,6 +42,63 @@ def valid_profile? end end + def configure! + @profiles = build_profiles + write_profiles_to_config_file if @profiles.any? + end + + def build_profiles + puts "How many profiles would you like to create?" + profile_count = STDIN.gets.chomp.to_i + profiles = Array.new(profile_count, {}) + current = 1 + profiles.map do |profile| + puts "\n#{ordinal(current)} profile name (e.g. 'work' or 'personal'):" + profile[:profile_name] = STDIN.gets.chomp + puts "Git username for #{profile[:profile_name]}:" + profile[:git_username] = STDIN.gets.chomp + puts "Git email for #{profile[:profile_name]}:" + profile[:git_email] = STDIN.gets.chomp + puts "Git name for #{profile[:profile_name]}:" + profile[:git_name] = STDIN.gets.chomp + puts "Path to ssh key for #{profile[:profile_name]} (e.g. '~/.ssh/id_rsa'):" + profile[:ssh_key] = STDIN.gets.chomp + + current +=1 + profile.dup + end + rescue Interrupt + return {} + end + + def ordinal(number) + # Source: https://github.com/infertux/ordinalize_full + abs_number = number.abs + suffix = if (11..13).cover?(abs_number % 100) + "th" + else + case abs_number % 10 + when 1 then "st" + when 2 then "nd" + when 3 then "rd" + else "th" + end + end + "#{abs_number}#{suffix}" + end + + def write_profiles_to_config_file + File.open(File.expand_path('~/.gitswitch'), 'w') do |file| + profiles.each do |profile| + file.write "#{profile[:profile_name]}:\n" + file.write " username: #{profile[:git_username]}\n" + file.write " email: #{profile[:git_email]}\n" + file.write " name: #{profile[:git_name]}\n" + file.write " ssh: #{profile[:ssh_key]}\n" + end + end + end + def print_list profiles = @profiles.map do |key, value| prefix = value["username"] == current_git_username ? "=>" : " " diff --git a/lib/git_switch/options.rb b/lib/git_switch/options.rb index 6d832c3..c56e338 100644 --- a/lib/git_switch/options.rb +++ b/lib/git_switch/options.rb @@ -7,16 +7,19 @@ def initialize(args) def flags @flags ||= args.select do |arg| - arg.match(/\A\-[glv]{1}\z/) || - arg.match(/\A\-\-(global|list|verbose){1}\z/) + arg.match(/\A\-[cglv]{1}\z/) || + arg.match(/\A\-\-(config|global|list|verbose){1}\z/) end end def valid_args? - if list? && args.count > 1 - puts "Invalid args" - return false - elsif no_flags?(args) || one_flag?(args) + if config_flag? && args.count == 1 + return true + elsif list_flag? && args.count == 1 + return true + elsif no_flags? + return true + elsif one_flag? && !flag_only? return true elsif usage? return true @@ -30,8 +33,12 @@ def usage? args.count == 0 end + def config? + config_flag? && args.count == 1 + end + def list? - (flags.include? '-l') || (flags.include? '--list') + list_flag? && args.count == 1 end def global? @@ -44,14 +51,26 @@ def verbose? private - def no_flags?(args) + def list_flag? + (flags.include? '-l') || (flags.include? '--list') + end + + def config_flag? + (flags.include? '-c') || (flags.include? '--config') + end + + def no_flags? args.length == 1 && flag_count(args) == 0 end - def one_flag?(args) + def one_flag? args.length == 2 && flag_count(args) == 1 end + def flag_only? + list_flag? || config_flag? + end + def flag_count(args) args.count {|a| a.start_with? '-'} end diff --git a/lib/git_switch/switcher.rb b/lib/git_switch/switcher.rb index 31e87ae..cf5a921 100644 --- a/lib/git_switch/switcher.rb +++ b/lib/git_switch/switcher.rb @@ -1,8 +1,11 @@ require_relative './version' +require 'active_support/core_ext/module/delegation' module GitSwitch class Switcher attr_reader :config, :options + delegate :usage?, :config?, :list?, :global?, to: :options + delegate :profile, :name, :username, :email, :ssh, :print_list, :configure!, :valid_profile?, to: :config def initialize(args) raise ArgumentError unless args.is_a? Array @@ -11,8 +14,11 @@ def initialize(args) end def run + return unless options.valid_args? if usage? print_usage + elsif config? + configure! elsif list? print_list else @@ -20,38 +26,6 @@ def run end end - def profile - config.profile - end - - def name - config.name - end - - def username - config.username - end - - def email - config.email - end - - def ssh - config.ssh - end - - def usage? - options.usage? - end - - def list? - options.list? - end - - def global? - options.global? - end - def git_repo? if GitHelper.git_repo? || global? return true @@ -61,13 +35,8 @@ def git_repo? end end - def valid_profile? - config.valid_profile? - end - def set! - return unless options.valid_args? && valid_profile? - return unless git_repo? + return unless valid_profile? && git_repo? flag = global? ? '--global' : '' @@ -80,10 +49,6 @@ def print_usage puts usage end - def print_list - config.print_list - end - def print_settings(flag = '') if options.verbose? puts "\nGit Config:" @@ -110,9 +75,12 @@ def set_ssh def usage <<~USAGE - usage: git switch [-l | --list] + usage: git switch [-l | --list] [-c | --config] [-v | --verbose] [-g | --global] + configure profiles + git switch -c + switch to a profile for local development only git switch diff --git a/lib/git_switch/version.rb b/lib/git_switch/version.rb index 3963e8b..26ac620 100644 --- a/lib/git_switch/version.rb +++ b/lib/git_switch/version.rb @@ -1,3 +1,3 @@ module GitSwitch - VERSION = "0.3.2" + VERSION = "0.4.0" end diff --git a/spec/lib/git_switch/config_spec.rb b/spec/lib/git_switch/config_spec.rb index f60e46a..c204775 100644 --- a/spec/lib/git_switch/config_spec.rb +++ b/spec/lib/git_switch/config_spec.rb @@ -22,6 +22,111 @@ end end + describe 'configure!' do + let(:profile_count) { '2' } + let(:profile_name_1) { 'work2' } + let(:git_username_1) { 'johnsmith2' } + let(:git_email_1) { 'john@defmethod2.io' } + let(:git_name_1) { 'John Smith Jr' } + let(:ssh_key_path_1) { '~/.ssh/defmethod2_rsa' } + + let(:profile_name_2) { 'personal2' } + let(:git_username_2) { 'johnnyfive2' } + let(:git_email_2) { 'me@johnsmith2.com' } + let(:git_name_2) { 'Johnny Smith Jr' } + let(:ssh_key_path_2) { '~/.ssh/id2_rsa' } + + before :each do + allow(File).to receive(:expand_path).and_return(File.expand_path('spec/fixtures/.empty')) + @config = GitSwitch::Config.new(['-c']) + + io_obj = double + expect(STDIN) + .to receive(:gets) + .and_return(io_obj) + .exactly(11).times + expect(io_obj).to receive(:chomp).and_return(profile_count) + expect(io_obj).to receive(:chomp).and_return(profile_name_1) + expect(io_obj).to receive(:chomp).and_return(git_username_1) + expect(io_obj).to receive(:chomp).and_return(git_email_1) + expect(io_obj).to receive(:chomp).and_return(git_name_1) + expect(io_obj).to receive(:chomp).and_return(ssh_key_path_1) + + expect(io_obj).to receive(:chomp).and_return(profile_name_2) + expect(io_obj).to receive(:chomp).and_return(git_username_2) + expect(io_obj).to receive(:chomp).and_return(git_email_2) + expect(io_obj).to receive(:chomp).and_return(git_name_2) + expect(io_obj).to receive(:chomp).and_return(ssh_key_path_2) + + expect(@config).to receive(:puts).with('How many profiles would you like to create?') + expect(@config).to receive(:puts).with("\n1st profile name (e.g. 'work' or 'personal'):") + expect(@config).to receive(:puts).with('Git username for work2:') + expect(@config).to receive(:puts).with('Git email for work2:') + expect(@config).to receive(:puts).with('Git name for work2:') + expect(@config).to receive(:puts).with('Path to ssh key for work2 (e.g. \'~/.ssh/id_rsa\'):') + + expect(@config).to receive(:puts).with("\n2nd profile name (e.g. 'work' or 'personal'):") + expect(@config).to receive(:puts).with('Git username for personal2:') + expect(@config).to receive(:puts).with('Git email for personal2:') + expect(@config).to receive(:puts).with('Git name for personal2:') + expect(@config).to receive(:puts).with('Path to ssh key for personal2 (e.g. \'~/.ssh/id_rsa\'):') + end + + it 'creates the .gitswitch config file' do + allow(@config).to receive(:write_profiles_to_config_file) + @config.configure! + expect(@config.profiles).to eq [ + { :profile_name=>"work2", + :git_username=>"johnsmith2", + :git_email=>"john@defmethod2.io", + :git_name=>"John Smith Jr", + :ssh_key=>"~/.ssh/defmethod2_rsa" }, + { :profile_name=>"personal2", + :git_username=>"johnnyfive2", + :git_email=>"me@johnsmith2.com", + :git_name=>"Johnny Smith Jr", + :ssh_key=>"~/.ssh/id2_rsa" } + ] + end + + it 'writes the config to the file' do + expect(File).to receive(:open).with(File.expand_path('~/.gitswitch'), 'w') + @config.configure! + end + end + + describe 'ordinal' do + let(:config) { GitSwitch::Config.new(['-c']) } + + it 'returns the correct value for 1' do + expect(config.ordinal(1)).to eq '1st' + end + + it 'returns the correct value for 2' do + expect(config.ordinal(2)).to eq '2nd' + end + + it 'returns the correct value for 3' do + expect(config.ordinal(3)).to eq '3rd' + end + + it 'returns the correct value for 4' do + expect(config.ordinal(4)).to eq '4th' + end + + it 'returns the correct value for 10' do + expect(config.ordinal(10)).to eq '10th' + end + + it 'returns the correct value for 11' do + expect(config.ordinal(11)).to eq '11th' + end + + it 'returns the correct value for 13' do + expect(config.ordinal(13)).to eq '13th' + end + end + describe '#print_list' do let(:config) { GitSwitch::Config.new(['-l']) } context 'when profiles have been configured' do diff --git a/spec/lib/git_switch/options_spec.rb b/spec/lib/git_switch/options_spec.rb index cd1e460..a41bc17 100644 --- a/spec/lib/git_switch/options_spec.rb +++ b/spec/lib/git_switch/options_spec.rb @@ -5,23 +5,30 @@ describe 'flags' do context 'when short flags are used' do + context 'when run in config mode' do + let(:args) { ['-c'] } + it 'returns an array of the flags' do + expect(options.flags).to eq ['-c'] + end + end + context 'when run in list mode' do let(:args) { ['-l'] } - it 'returns an empty array' do + it 'returns an array of the flags' do expect(options.flags).to eq ['-l'] end end context 'when run in global mode' do let(:args) { ['personal', '-g'] } - it 'returns an empty array' do + it 'returns an array of the flags' do expect(options.flags).to eq ['-g'] end end context 'when run with multiple flags' do let(:args) { ['personal', '-g', '-v'] } - it 'returns an empty array' do + it 'returns an array of the flags' do expect(options.flags).to eq ['-g', '-v'] end end @@ -35,23 +42,30 @@ end context 'when long flags are used' do + context 'when run in config mode' do + let(:args) { ['--config'] } + it 'returns an array of the flags' do + expect(options.flags).to eq ['--config'] + end + end + context 'when run in list mode' do let(:args) { ['--list'] } - it 'returns an empty array' do + it 'returns an array of the flags' do expect(options.flags).to eq ['--list'] end end context 'when run in global mode' do let(:args) { ['personal', '--global'] } - it 'returns an empty array' do + it 'returns an array of the flags' do expect(options.flags).to eq ['--global'] end end context 'when run with multiple flags' do let(:args) { ['personal', '--global', '--verbose'] } - it 'returns an empty array' do + it 'returns an array of the flags' do expect(options.flags).to eq ['--global', '--verbose'] end end @@ -103,6 +117,22 @@ end end + context 'when run with list flag' do + let(:args) { ['-l'] } + + it 'returns true' do + expect(options.valid_args?).to be true + end + end + + context 'when run with config flag' do + let(:args) { ['-c'] } + + it 'returns true' do + expect(options.valid_args?).to be true + end + end + context 'when run with multiple flags' do let(:args) { ['personal','-g','-l'] } let(:options) { GitSwitch::Options.new(args) } @@ -160,6 +190,36 @@ end end + describe 'config?' do + context 'when args includes -c' do + let(:args) { ['-c'] } + it 'returns true' do + expect(options.config?).to be true + end + end + + context 'when args includes --config' do + let(:args) { ['--config'] } + it 'returns true' do + expect(options.config?).to be true + end + end + + context 'when there are multiple args' do + let(:args) { ['-c', 'foo'] } + it 'returns false' do + expect(options.config?).to be false + end + end + + context 'when args do not include -c or --config' do + let(:args) { [] } + it 'returns false' do + expect(options.config?).to be false + end + end + end + describe 'list?' do context 'when args includes -l' do let(:args) { ['-l'] } @@ -175,6 +235,13 @@ end end + context 'when there are multiple args' do + let(:args) { ['-l', 'foo'] } + it 'returns false' do + expect(options.list?).to be false + end + end + context 'when args do not include -l or --list' do let(:args) { [] } it 'returns false' do diff --git a/spec/lib/git_switch/switcher_spec.rb b/spec/lib/git_switch/switcher_spec.rb index 2f60a28..c473b3e 100644 --- a/spec/lib/git_switch/switcher_spec.rb +++ b/spec/lib/git_switch/switcher_spec.rb @@ -53,45 +53,89 @@ end end + describe '#config?' do + context 'when -c is passed as only argument' do + it 'returns true' do + expect(GitSwitch::Switcher.new(['-c']).config?).to be true + end + end + + context 'when -c is passed as first argument' do + it 'returns false' do + expect(GitSwitch::Switcher.new(['-c','foo']).config?).to be false + end + end + + context 'when -c is passed as second argument' do + it 'returns false' do + expect(GitSwitch::Switcher.new(['foo','-c']).config?).to be false + end + end + + context 'when --config is passed as only argument' do + it 'returns true' do + expect(GitSwitch::Switcher.new(['--config']).config?).to be true + end + end + + context 'when --config is passsed as first argument' do + it 'returns false' do + expect(GitSwitch::Switcher.new(['--config', 'foo']).config?).to be false + end + end + + context 'when --config is passsed as second argument' do + it 'returns false' do + expect(GitSwitch::Switcher.new(['foo','--config']).config?).to be false + end + end + + context 'when no flag is passed' do + it 'returns false' do + expect(GitSwitch::Switcher.new(['foo']).config?).to be false + end + end + end + describe '#list?' do context 'when -l is passed as only argument' do - it 'sets to true' do + it 'returns true' do expect(GitSwitch::Switcher.new(['-l']).list?).to be true end end context 'when -l is passed as first argument' do - it 'sets to true' do - expect(GitSwitch::Switcher.new(['-l','foo']).list?).to be true + it 'returns false' do + expect(GitSwitch::Switcher.new(['-l','foo']).list?).to be false end end context 'when -l is passed as second argument' do - it 'sets to true' do - expect(GitSwitch::Switcher.new(['foo','-l']).list?).to be true + it 'returns false' do + expect(GitSwitch::Switcher.new(['foo','-l']).list?).to be false end end context 'when --list is passed as only argument' do - it 'sets to true' do - expect(GitSwitch::Switcher.new(['-l']).list?).to be true + it 'returns true' do + expect(GitSwitch::Switcher.new(['--list']).list?).to be true end end context 'when --list is passsed as first argument' do - it 'sets to true' do - expect(GitSwitch::Switcher.new(['--list']).list?).to be true + it 'returns false' do + expect(GitSwitch::Switcher.new(['--list', 'foo']).list?).to be false end end context 'when --list is passsed as second argument' do - it 'sets to true' do - expect(GitSwitch::Switcher.new(['foo','--list']).list?).to be true + it 'returns false' do + expect(GitSwitch::Switcher.new(['foo','--list']).list?).to be false end end context 'when no flag is passed' do - it 'sets to false' do + it 'returns false' do expect(GitSwitch::Switcher.new(['foo']).list?).to be false end end @@ -106,9 +150,19 @@ end end + context 'in config mode' do + let(:switcher) { GitSwitch::Switcher.new(['-c']) } + it 'calls configure' do + expect(switcher).to receive(:configure!).and_call_original + expect(switcher.config).to receive(:configure!) + switcher.run + end + end + context 'in list mode' do let(:switcher) { GitSwitch::Switcher.new(['-l']) } it 'calls print_list' do + expect(switcher).to receive(:print_list).and_call_original expect(switcher.config).to receive(:print_list) switcher.run end @@ -215,9 +269,12 @@ let(:switcher) { GitSwitch::Switcher.new([]) } let(:expected_output) do <<~USAGE - usage: git switch [-l | --list] + usage: git switch [-l | --list] [-c | --config] [-v | --verbose] [-g | --global] + configure profiles + git switch -c + switch to a profile for local development only git switch