git_reflow 0.9.0 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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