git_reflow 0.9.0 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -4,8 +4,8 @@
4
4
  </h1>
5
5
 
6
6
  <p>
7
- <a href="https://circleci.com/gh/reenhanced/git-reflow/tree/master" title="current build status">
8
- <img alt="Circle CI" src="https://circleci.com/gh/reenhanced/gitreflow.svg?style=svg&circle-token=d94825c17c7e33558dc2a82b37afad7b558e5a12">
7
+ <a href="https://actions-badge.atrox.dev/reenhanced/gitreflow/goto?ref=master" title="git workflow">
8
+ <img alt="Git workflow powered by git-reflow" src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Freenhanced%2Fgitreflow%2Fbadge%3Fref%3Dmaster&style=flat">
9
9
  </a>
10
10
  <a href="https://github.com/reenhanced/gitreflow" title="git workflow">
11
11
  <img alt="Git workflow powered by git-reflow" src="https://img.shields.io/badge/git--reflow-v0.9.0-blue.svg?style=flat">
@@ -443,11 +443,15 @@ If the review is done, it's time to merge. Here's what happens:
443
443
 
444
444
  In order to streamline delivery you can set the following git config to:
445
445
 
446
- 1. always clean up the feature branch after the PR is merged
446
+ 1. always clean up the remote feature branch after the PR is merged
447
447
  ```
448
- git config --global --add "reflow.always-cleanup" "true"
448
+ git config --global --add "reflow.always-cleanup-remote" "true"
449
449
  ```
450
- 2. always deliver without further prompt
450
+ 2. always clean up the local feature branch after the PR is merged
451
+ ```
452
+ git config --global --add "reflow.always-cleanup-local" "true"
453
+ ```
454
+ 3. always deliver without further prompt
451
455
  ```
452
456
  git config --global --add "reflow.always-deliver" "true"
453
457
  ```
data/Rakefile CHANGED
@@ -1,8 +1,15 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
3
  require "rspec/core/rake_task"
4
+ require "github_changelog_generator/task"
4
5
 
5
6
  RSpec::Core::RakeTask.new(:spec)
6
7
 
7
- task :default => :spec
8
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
9
+ config.user = 'reenhanced'
10
+ config.project = 'gitreflow'
11
+ config.since_tag = 'v0.9.2'
12
+ config.future_release = 'master'
13
+ end
8
14
 
15
+ task :default => :spec
data/Workflow ADDED
@@ -0,0 +1,3 @@
1
+ # This file is empty on purpose in case there are any custom workflows configured locally.
2
+ # Eventually we will update it to:
3
+ # use "OpenSourceWorkflow"
data/exe/git-reflow CHANGED
@@ -9,6 +9,11 @@ if reflow_command.nil? || GitReflow.workflow.commands[reflow_command.to_sym].nil
9
9
  elsif ARGV.include? "--help"
10
10
  GitReflow.documentation_for_command(reflow_command)
11
11
  else
12
+ trap 'INT' do
13
+ GitReflow.say "Aborted.", :error
14
+ exit
15
+ end
16
+
12
17
  command_options = GitReflow.parse_command_options!(reflow_command)
13
18
  GitReflow.logger.debug "Running command `#{reflow_command}` with options: #{command_options.inspect}"
14
19
  GitReflow.public_send(reflow_command.to_sym, command_options)
data/git_reflow.gemspec CHANGED
@@ -1,38 +1,40 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Ensure we require the local version and not one we might have installed already
2
- require File.join([File.dirname(__FILE__),'lib','git_reflow/version.rb'])
4
+ require File.join([File.dirname(__FILE__), 'lib', 'git_reflow/version.rb'])
3
5
  Gem::Specification.new do |s|
4
- s.name = 'git_reflow'
5
- s.version = GitReflow::VERSION
6
- s.license = 'MIT'
7
- s.authors = ["Valentino Stoll", "Robert Stern", "Nicholas Hance"]
8
- s.email = ["dev@reenhanced.com"]
9
- s.homepage = "http://github.com/reenhanced/gitreflow"
10
- s.summary = "A better git process"
11
- s.description = "Git Reflow manages your git workflow."
12
- s.platform = Gem::Platform::RUBY
13
- s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
14
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
- s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
16
- s.has_rdoc = true
17
- s.bindir = "exe"
18
- s.require_paths = ["lib"]
19
- s.rdoc_options << '--title' << 'git_reflow' << '-ri'
6
+ s.name = 'git_reflow'
7
+ s.version = GitReflow::VERSION
8
+ s.license = 'MIT'
9
+ s.authors = ['Valentino Stoll', 'Robert Stern', 'Nicholas Hance']
10
+ s.email = ['dev@reenhanced.com']
11
+ s.homepage = 'http://github.com/reenhanced/gitreflow'
12
+ s.summary = 'A better git process'
13
+ s.description = 'Git Reflow manages your git workflow.'
14
+ s.platform = Gem::Platform::RUBY
15
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ s.bindir = 'exe'
19
+ s.require_paths = ['lib']
20
+ s.rdoc_options << '--title' << 'git_reflow' << '-ri'
20
21
 
21
- s.add_development_dependency('appraisal', '2.2.0')
22
- s.add_development_dependency('bundler', "~> 1.16")
22
+ s.add_development_dependency('appraisal', '2.4.0')
23
23
  s.add_development_dependency('chronic')
24
- s.add_development_dependency('pry-byebug')
25
- s.add_development_dependency('rake', "~> 12.3")
24
+ s.add_development_dependency('github_changelog_generator')
25
+ s.add_development_dependency('ruby_jard')
26
+ s.add_development_dependency('rake', '~> 13.0.3')
26
27
  s.add_development_dependency('rdoc')
27
- s.add_development_dependency('rspec', "~> 3.7.0")
28
+ s.add_development_dependency('rspec', '~> 3.10')
28
29
  s.add_development_dependency('webmock')
29
- s.add_development_dependency('wwtd', '1.3.0')
30
+ s.add_development_dependency('wwtd', '1.4')
30
31
 
31
- s.add_dependency('colorize', '>= 0.7.0')
32
+ s.add_dependency('bundler', '>= 1.10.0')
33
+ s.add_dependency('codenamev_bitbucket_api', '0.4.1')
34
+ s.add_dependency('colorize', '>= 0.8.1')
35
+ s.add_dependency('github_api', '0.19')
32
36
  s.add_dependency('highline')
33
37
  s.add_dependency('httpclient')
34
- s.add_dependency('github_api', '0.18.2')
35
- s.add_dependency('reenhanced_bitbucket_api', '0.3.2')
36
38
 
37
39
  s.post_install_message = "You need to setup your GitHub OAuth token\nPlease run 'git-reflow setup'"
38
40
  end
@@ -7,7 +7,14 @@ module GitReflow
7
7
 
8
8
  module_function
9
9
 
10
- def get(key, reload: false, all: false, local: false)
10
+ # Gets the reqested git configuration variable.
11
+ #
12
+ # @param [String] key The key to get the value(s) for
13
+ # @option options [Boolean] :reload (false) whether to reload the value or use a cached value if available
14
+ # @option options [Boolean] :all (false) whether to return all keys for a multi-valued key
15
+ # @option options [Boolean] :local (false) whether to get the value specific to the current project
16
+ # @return the value of the git configuration
17
+ def get(key, reload: false, all: false, local: false, **_other_options)
11
18
  return cached_git_config_value(key) unless reload || cached_git_config_value(key).empty?
12
19
 
13
20
  local = local ? '--local ' : ''
@@ -19,7 +26,13 @@ module GitReflow
19
26
  cache_git_config_key(key, new_value)
20
27
  end
21
28
 
22
- def set(key, value, local: false)
29
+ # Sets the reqested git configuration variable.
30
+ #
31
+ # @param [String] key The key to set the value for
32
+ # @param [String] value The value to set it to
33
+ # @option options [Boolean] :local (false) whether to set the value specific to the current project
34
+ # @return the value of the git configuration
35
+ def set(key, value, local: false, **_other_options)
23
36
  value = value.to_s.strip
24
37
  if local
25
38
  GitReflow::Sandbox.run "git config --replace-all #{key} \"#{value}\"", loud: false, blocking: false
@@ -28,7 +41,13 @@ module GitReflow
28
41
  end
29
42
  end
30
43
 
31
- def unset(key, value: nil, local: false)
44
+ # Remove values of the reqested git configuration variable.
45
+ #
46
+ # @param [String] key The key to remove
47
+ # @option options [Boolean] :value (nil) The value of the key to remove
48
+ # @option options [Boolean] :local (false) whether to remove the value specific to the current project
49
+ # @return the result of running the git command
50
+ def unset(key, value: nil, local: false, **_other_options)
32
51
  value = value.nil? ? '' : "\"#{value}\""
33
52
  if local
34
53
  GitReflow::Sandbox.run "git config --unset-all #{key} #{value}", loud: false, blocking: false
@@ -37,7 +56,14 @@ module GitReflow
37
56
  end
38
57
  end
39
58
 
40
- def add(key, value, local: false, global: false)
59
+ # Adds a new git configuration variable.
60
+ #
61
+ # @param [String] key The new key to set the value for
62
+ # @param [String] value The value to set it to
63
+ # @option options [Boolean] :local (false) whether to set the value specific to the current project
64
+ # @option options [Boolean] :global (false) whether to set the value globaly. if neither local or global is set gitreflow will default to using a configuration file
65
+ # @return the result of running the git command
66
+ def add(key, value, local: false, global: false, **_other_options)
41
67
  if global
42
68
  GitReflow::Sandbox.run "git config --global --add #{key} \"#{value}\"", loud: false, blocking: false
43
69
  elsif local
@@ -84,56 +84,69 @@ module GitReflow
84
84
  end
85
85
 
86
86
  def authenticate(options = {silent: false})
87
+ if !options[:user].to_s.empty?
88
+ self.class.user = options[:user]
89
+ elsif self.class.user.empty?
90
+ self.class.user = ask("Please enter your GitHub username: ")
91
+ end
92
+
87
93
  if connection and self.class.oauth_token.length > 0
88
- unless options[:silent]
89
- GitReflow.say "Your GitHub account was already setup with: "
90
- GitReflow.say "\tUser Name: #{self.class.user}"
91
- GitReflow.say "\tEndpoint: #{self.class.api_endpoint}"
92
- end
93
- else
94
94
  begin
95
- gh_user = options[:user] || ask("Please enter your GitHub username: ")
96
- gh_password = options[:password] || ask("Please enter your GitHub password (we do NOT store this): ") { |q| q.echo = false }
97
-
98
- @connection = ::Github.new do |config|
99
- config.basic_auth = "#{gh_user}:#{gh_password}"
100
- config.endpoint = GitServer::GitHub.api_endpoint
101
- config.site = GitServer::GitHub.site_url
102
- config.adapter = :net_http
95
+ connection.users.get
96
+ unless options[:silent]
97
+ GitReflow.say "Your GitHub account was already setup with: "
98
+ GitReflow.say "\tUser Name: #{self.class.user}"
99
+ GitReflow.say "\tEndpoint: #{self.class.api_endpoint}"
103
100
  end
101
+ return connection
102
+ rescue ::Github::Error::Unauthorized => e
103
+ GitReflow.logger.debug "[GitHub Error] Current oauth-token is invalid or expired..."
104
+ end
105
+ end
104
106
 
105
- @connection.connection_options = {headers: {"X-GitHub-OTP" => options[:two_factor_auth_code]}} if options[:two_factor_auth_code]
107
+ begin
108
+ gh_password = options[:password] || ask("Please enter your GitHub password (we do NOT store this): ") { |q| q.echo = false }
106
109
 
107
- previous_authorizations = @connection.oauth.all.select {|auth| auth.note == "git-reflow (#{run('hostname', loud: false).strip})" }
108
- if previous_authorizations.any?
109
- authorization = previous_authorizations.last
110
- GitReflow.say "You have previously setup git-reflow on this machine, but we can no longer find the stored token.", :error
111
- GitReflow.say "Please visit https://github.com/settings/tokens and delete the token for: git-reflow (#{run('hostname', loud: false).strip})", :notice
112
- raise "Setup could not be completed."
113
- else
114
- authorization = @connection.oauth.create scopes: ['repo'], note: "git-reflow (#{run('hostname', loud: false).strip})"
115
- end
110
+ @connection = ::Github.new do |config|
111
+ config.basic_auth = "#{self.class.user}:#{gh_password}"
112
+ config.endpoint = GitServer::GitHub.api_endpoint
113
+ config.site = GitServer::GitHub.site_url
114
+ config.adapter = :net_http
115
+ end
116
116
 
117
- self.class.oauth_token = authorization.token
117
+ @connection.connection_options = {headers: {"X-GitHub-OTP" => options[:two_factor_auth_code]}} if options[:two_factor_auth_code]
118
118
 
119
- rescue ::Github::Error::Unauthorized => e
120
- if e.inspect.to_s.include?('two-factor')
121
- begin
122
- # dummy request to trigger a 2FA SMS since a HTTP GET won't do it
123
- @connection.oauth.create scopes: ['repo'], note: "thank Github for not making this straightforward"
124
- rescue ::Github::Error::Unauthorized
125
- ensure
126
- two_factor_code = ask("Please enter your two-factor authentication code: ")
127
- self.authenticate options.merge({user: gh_user, password: gh_password, two_factor_auth_code: two_factor_code})
128
- end
129
- else
130
- GitReflow.say "Github Authentication Error: #{e.inspect}", :error
119
+ previous_authorizations = @connection.oauth.all.select {|auth| auth.note == "git-reflow (#{run('hostname', loud: false).strip})" }
120
+ if previous_authorizations.any?
121
+ authorization = previous_authorizations.last
122
+ GitReflow.say "You have previously setup git-reflow on this machine, but we can no longer find the stored token.", :error
123
+ GitReflow.say "Please visit https://github.com/settings/tokens and delete the token for: git-reflow (#{run('hostname', loud: false).strip})", :notice
124
+ raise "Setup could not be completed."
125
+ else
126
+ authorization = @connection.oauth.create scopes: ['repo'], note: "git-reflow (#{run('hostname', loud: false).strip})"
127
+ end
128
+
129
+ self.class.oauth_token = authorization.token
130
+
131
+ rescue ::Github::Error::Unauthorized => e
132
+ if e.inspect.to_s.include?('two-factor')
133
+ begin
134
+ # dummy request to trigger a 2FA SMS since a HTTP GET won't do it
135
+ @connection.oauth.create scopes: ['repo'], note: "thank Github for not making this straightforward"
136
+ rescue ::Github::Error::Unauthorized
137
+ ensure
138
+ two_factor_code = ask("Please enter your two-factor authentication code: ")
139
+ self.authenticate options.merge({user: self.class.user, password: gh_password, two_factor_auth_code: two_factor_code})
131
140
  end
132
- rescue StandardError => e
133
- raise "We were unable to authenticate with Github."
134
141
  else
135
- GitReflow.say "Your GitHub account was successfully setup!", :success
142
+ GitReflow.say "Github Authentication Error: #{e.inspect}", :error
143
+ raise "Setup could not be completed."
136
144
  end
145
+ rescue StandardError => e
146
+ raise "We were unable to authenticate with Github."
147
+ else
148
+ GitReflow.say "Your GitHub account was successfully setup!", :success
149
+
137
150
  end
138
151
 
139
152
  @connection
@@ -24,8 +24,18 @@ module GitReflow
24
24
  base: options[:base]))
25
25
  end
26
26
 
27
- def self.find_open(to: 'master', from: GitReflow.git_server.class.current_branch)
28
- matching_pull = GitReflow.git_server.connection.pull_requests.all(GitReflow.remote_user, GitReflow.remote_repo_name, base: to, head: "#{GitReflow.remote_user}:#{from}", state: 'open').first
27
+ def self.find_open(options = {})
28
+ options[:to] ||= 'master'
29
+ options[:from] ||= GitReflow.git_server.class.current_branch
30
+
31
+ matching_pull = GitReflow.git_server.connection.pull_requests.all(
32
+ GitReflow.remote_user,
33
+ GitReflow.remote_repo_name,
34
+ base: options[:to],
35
+ head: "#{GitReflow.remote_user}:#{options[:from]}",
36
+ state: 'open'
37
+ ).first
38
+
29
39
  if matching_pull
30
40
  self.new matching_pull
31
41
  end
@@ -147,13 +157,19 @@ module GitReflow
147
157
  GitReflow.run_command_with_label "git pull origin #{self.base_branch_name}"
148
158
  GitReflow.say "Pull request ##{self.number} successfully merged.", :success
149
159
 
150
- if cleanup_feature_branch?
151
- GitReflow.run_command_with_label "git push origin :#{self.feature_branch_name}"
160
+ if cleanup_remote_feature_branch?
161
+ GitReflow.run_command_with_label "git push origin :#{self.feature_branch_name}", blocking: false
162
+ else
163
+ GitReflow.say "Skipped. Remote feature branch #{self.feature_branch_name} left in tact."
164
+ end
165
+
166
+ if cleanup_local_feature_branch?
152
167
  GitReflow.run_command_with_label "git branch -D #{self.feature_branch_name}"
153
- GitReflow.say "Nice job buddy."
154
168
  else
155
- cleanup_failure_message
169
+ GitReflow.say "Skipped. Local feature branch #{self.feature_branch_name} left in tact."
156
170
  end
171
+
172
+ GitReflow.say "Nice job buddy."
157
173
  else
158
174
  GitReflow.say merge_response.to_s, :deliver_halted
159
175
  GitReflow.say "There were problems commiting your feature... please check the errors above and try again.", :error
@@ -178,7 +178,21 @@ module GitReflow
178
178
  end
179
179
 
180
180
  def cleanup_feature_branch?
181
- GitReflow::Config.get('reflow.always-cleanup') == "true" || (ask "Would you like to push this branch to your remote repo and cleanup your feature branch? ") =~ /^y/i
181
+ cleanup_local_feature_branch? || cleanup_remote_feature_branch?
182
+ end
183
+
184
+ def cleanup_local_feature_branch?
185
+ # backwards compat
186
+ always_cleanup_local = GitReflow::Config.get('reflow.always-cleanup').to_s
187
+ always_cleanup_local = GitReflow::Config.get('reflow.always-cleanup-local') if always_cleanup_local.empty?
188
+ always_cleanup_local == "true" || (ask "Would you like to cleanup your local feature branch? ") =~ /^y/i
189
+ end
190
+
191
+ def cleanup_remote_feature_branch?
192
+ # backwards compat
193
+ always_cleanup_remote = GitReflow::Config.get('reflow.always-cleanup').to_s
194
+ always_cleanup_remote = GitReflow::Config.get('reflow.always-cleanup-remote') if always_cleanup_remote.empty?
195
+ always_cleanup_remote == "true" || (ask "Would you like to cleanup your remote feature branch? ") =~ /^y/i
182
196
  end
183
197
 
184
198
  def deliver?
@@ -7,6 +7,7 @@ module GitReflow
7
7
  def stub_command_line
8
8
  $commands_ran = []
9
9
  $stubbed_commands = {}
10
+ $stubbed_runners = Set.new
10
11
  $output = []
11
12
  $says = []
12
13
 
@@ -33,6 +34,7 @@ module GitReflow
33
34
  end
34
35
 
35
36
  def stub_run_for(module_to_stub)
37
+ $stubbed_runners << module_to_stub
36
38
  allow(module_to_stub).to receive(:run) do |command, options|
37
39
  options = { loud: true, blocking: true }.merge(options || {})
38
40
  $commands_ran << Hashie::Mash.new(command: command, options: options)
@@ -52,24 +54,31 @@ module GitReflow
52
54
  $says = []
53
55
  end
54
56
 
55
- def stub_command(command, return_value)
57
+ def stub_command(command:, return_value: "", options: {})
56
58
  $stubbed_commands[command] = return_value
57
- allow(GitReflow::Sandbox).to receive(:run).with(command).and_return(return_value)
59
+ $stubbed_runners.each do |runner|
60
+ allow(runner).to receive(:run).with(command, options) do |command, options|
61
+ options = { loud: true, blocking: true }.merge(options || {})
62
+ $commands_ran << Hashie::Mash.new(command: command, options: options)
63
+ $stubbed_commands[command] = return_value
64
+ raise GitReflow::Sandbox::CommandError.new(return_value, "\"#{command}\" failed to run.") if options[:raise]
65
+ end
66
+ end
58
67
  end
59
68
 
60
69
  def stub_command_line_inputs_for(module_to_stub, inputs)
61
70
  allow(module_to_stub).to receive(:ask) do |terminal, question|
62
- return_value = inputs[question]
63
- question = ""
64
- return_value
71
+ return_value = inputs[question]
72
+ question = ""
73
+ return_value
65
74
  end
66
75
  end
67
76
 
68
77
  def stub_command_line_inputs(inputs)
69
78
  allow_any_instance_of(HighLine).to receive(:ask) do |terminal, question|
70
- return_value = inputs[question]
71
- question = ""
72
- return_value
79
+ return_value = inputs[question]
80
+ question = ""
81
+ return_value
73
82
  end
74
83
  end
75
84
 
@@ -12,8 +12,16 @@ module GitReflow
12
12
  plain: :white
13
13
  }
14
14
 
15
+ class CommandError < StandardError;
16
+ attr_reader :output
17
+ def initialize(output, *args)
18
+ @output = output
19
+ super(*args)
20
+ end
21
+ end
22
+
15
23
  def run(command, options = {})
16
- options = { loud: true, blocking: true }.merge(options)
24
+ options = { loud: true, blocking: true, raise: false }.merge(options)
17
25
 
18
26
  GitReflow.logger.debug "Running... #{command}"
19
27
 
@@ -22,12 +30,13 @@ module GitReflow
22
30
  else
23
31
  output = %x{#{command}}
24
32
 
25
- if options[:blocking] == true && !$?.success?
26
- abort "\"#{command}\" failed to run."
27
- else
28
- puts output if options[:loud] == true
29
- output
33
+ if !$?.success?
34
+ raise CommandError.new(output, "\"#{command}\" failed to run.") if options[:raise] == true
35
+ abort "\"#{command}\" failed to run." if options[:blocking] == true
30
36
  end
37
+
38
+ puts output if options[:loud] == true
39
+ output
31
40
  end
32
41
  end
33
42