git_reflow 0.8.9 → 0.9.3

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.
Files changed (87) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/multi-ruby-tests.yml +33 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +2 -0
  5. data/.ruby-version +1 -0
  6. data/Appraisals +1 -6
  7. data/CHANGELOG.md +466 -348
  8. data/Gemfile.lock +99 -72
  9. data/LICENSE +20 -20
  10. data/README.md +481 -0
  11. data/Rakefile +15 -8
  12. data/Workflow +3 -0
  13. data/_config.yml +1 -0
  14. data/bin/console +7 -7
  15. data/bin/setup +6 -6
  16. data/exe/git-reflow +20 -36
  17. data/git_reflow.gemspec +26 -30
  18. data/lib/git_reflow.rb +3 -15
  19. data/lib/git_reflow/config.rb +48 -13
  20. data/lib/git_reflow/git_helpers.rb +69 -22
  21. data/lib/git_reflow/git_server.rb +63 -63
  22. data/lib/git_reflow/git_server/base.rb +68 -68
  23. data/lib/git_reflow/git_server/bit_bucket.rb +101 -101
  24. data/lib/git_reflow/git_server/bit_bucket/pull_request.rb +84 -84
  25. data/lib/git_reflow/git_server/git_hub.rb +53 -41
  26. data/lib/git_reflow/git_server/git_hub/pull_request.rb +16 -14
  27. data/lib/git_reflow/git_server/pull_request.rb +4 -2
  28. data/lib/git_reflow/merge_error.rb +9 -9
  29. data/lib/git_reflow/rspec.rb +3 -2
  30. data/lib/git_reflow/rspec/command_line_helpers.rb +23 -6
  31. data/lib/git_reflow/rspec/stub_helpers.rb +13 -13
  32. data/lib/git_reflow/rspec/workflow_helpers.rb +18 -0
  33. data/lib/git_reflow/sandbox.rb +16 -6
  34. data/lib/git_reflow/version.rb +1 -1
  35. data/lib/git_reflow/workflow.rb +304 -9
  36. data/lib/git_reflow/workflows/FlatMergeWorkflow +38 -0
  37. data/lib/git_reflow/workflows/core.rb +364 -238
  38. data/spec/fixtures/authentication_failure.json +3 -0
  39. data/spec/fixtures/awesome_workflow.rb +3 -7
  40. data/spec/fixtures/git/git_config +7 -7
  41. data/spec/fixtures/issues/comment.json.erb +27 -27
  42. data/spec/fixtures/issues/comments.json +29 -29
  43. data/spec/fixtures/issues/comments.json.erb +15 -15
  44. data/spec/fixtures/pull_requests/comment.json.erb +45 -45
  45. data/spec/fixtures/pull_requests/comments.json +47 -47
  46. data/spec/fixtures/pull_requests/comments.json.erb +15 -15
  47. data/spec/fixtures/pull_requests/commits.json +29 -29
  48. data/spec/fixtures/pull_requests/external_pull_request.json +145 -145
  49. data/spec/fixtures/pull_requests/pull_request.json +142 -142
  50. data/spec/fixtures/pull_requests/pull_request.json.erb +142 -142
  51. data/spec/fixtures/pull_requests/pull_request_branch_nonexistent_error.json +32 -0
  52. data/spec/fixtures/pull_requests/pull_request_exists_error.json +32 -32
  53. data/spec/fixtures/pull_requests/pull_requests.json +136 -136
  54. data/spec/fixtures/repositories/commit.json +53 -53
  55. data/spec/fixtures/repositories/commit.json.erb +53 -53
  56. data/spec/fixtures/repositories/commits.json.erb +13 -13
  57. data/spec/fixtures/repositories/statuses.json +31 -31
  58. data/spec/fixtures/users/user.json +32 -0
  59. data/spec/lib/git_reflow/git_helpers_spec.rb +115 -12
  60. data/spec/lib/git_reflow/git_server/bit_bucket_spec.rb +81 -81
  61. data/spec/lib/git_reflow/git_server/git_hub/pull_request_spec.rb +10 -10
  62. data/spec/lib/git_reflow/git_server/git_hub_spec.rb +77 -3
  63. data/spec/lib/git_reflow/git_server/pull_request_spec.rb +9 -3
  64. data/spec/lib/git_reflow/git_server_spec.rb +101 -101
  65. data/spec/lib/git_reflow/sandbox_spec.rb +1 -1
  66. data/spec/lib/git_reflow/workflow_spec.rb +304 -59
  67. data/spec/lib/git_reflow/workflows/core_spec.rb +225 -67
  68. data/spec/lib/git_reflow/workflows/flat_merge_spec.rb +71 -59
  69. data/spec/lib/git_reflow_spec.rb +2 -25
  70. data/spec/spec_helper.rb +3 -0
  71. data/spec/support/fixtures.rb +54 -54
  72. data/spec/support/github_helpers.rb +99 -109
  73. data/spec/support/mock_pull_request.rb +17 -17
  74. data/spec/support/web_mocks.rb +39 -39
  75. metadata +51 -74
  76. data/README.rdoc +0 -461
  77. data/circle.yml +0 -26
  78. data/lib/git_reflow/commands/deliver.rb +0 -10
  79. data/lib/git_reflow/commands/refresh.rb +0 -20
  80. data/lib/git_reflow/commands/review.rb +0 -13
  81. data/lib/git_reflow/commands/setup.rb +0 -11
  82. data/lib/git_reflow/commands/stage.rb +0 -9
  83. data/lib/git_reflow/commands/start.rb +0 -18
  84. data/lib/git_reflow/commands/status.rb +0 -7
  85. data/lib/git_reflow/os_detector.rb +0 -23
  86. data/lib/git_reflow/workflows/flat_merge.rb +0 -10
  87. data/spec/fixtures/workflow_with_super.rb +0 -8
@@ -1,238 +1,364 @@
1
- $: << File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../..')
2
- require 'git_reflow/workflow'
3
-
4
- module GitReflow
5
- module Workflows
6
- # This class contains the core workflow for git-reflow. Going forward, this
7
- # will act as the base class for customizing and extending git-reflow.
8
- class Core
9
- include GitReflow::Workflow
10
-
11
- # Sets up the required git configurations that git-reflow depends on.
12
- #
13
- # @param local [Boolean] whether to configure git-reflow specific to the current project
14
- # @param enterprise [Boolean] whether to configure git-reflow for use with Github Enterprise
15
- command(:setup, defaults: {local: false, enterprise: false}) do |**params|
16
- reflow_options = { project_only: params[:local], enterprise: params[:enterprise] }
17
- existing_git_include_paths = GitReflow::Config.get('include.path', all: true).split("\n")
18
-
19
- unless File.exist?(GitReflow::Config::CONFIG_FILE_PATH) or existing_git_include_paths.include?(GitReflow::Config::CONFIG_FILE_PATH)
20
- GitReflow.say "We'll walk you through setting up git-reflow's defaults for all your projects.", :notice
21
- GitReflow.say "In the future, you can run \`git-reflow setup\` from the root of any project you want to setup differently.", :notice
22
- GitReflow.say "To adjust these settings globally, you can run \`git-reflow setup --global\`.", :notice
23
- GitReflow.run "touch #{GitReflow::Config::CONFIG_FILE_PATH}"
24
- GitReflow.say "Created #{GitReflow::Config::CONFIG_FILE_PATH} for git-reflow specific configurations.", :notice
25
- GitReflow::Config.add "include.path", GitReflow::Config::CONFIG_FILE_PATH, global: true
26
- GitReflow.say "Added #{GitReflow::Config::CONFIG_FILE_PATH} to include.path in $HOME/.gitconfig.", :notice
27
- end
28
-
29
- choose do |menu|
30
- menu.header = "Available remote Git Server services"
31
- menu.prompt = "Which service would you like to use for this project? "
32
-
33
- menu.choice('GitHub') { GitReflow::GitServer.connect reflow_options.merge({ provider: 'GitHub', silent: false }) }
34
- menu.choice('BitBucket (team-owned repos only)') { GitReflow::GitServer.connect reflow_options.merge({ provider: 'BitBucket', silent: false }) }
35
- end
36
-
37
- GitReflow::Config.set "constants.minimumApprovals", ask("Set the minimum number of approvals (leaving blank will require approval from all commenters): "), local: reflow_options[:project_only]
38
- GitReflow::Config.set "constants.approvalRegex", GitReflow::GitServer::PullRequest::DEFAULT_APPROVAL_REGEX, local: reflow_options[:project_only]
39
-
40
- if GitReflow::Config.get('core.editor').length <= 0
41
- GitReflow::Config.set('core.editor', GitReflow.default_editor, local: reflow_options[:project_only])
42
- GitReflow.say "Updated git's editor (via git config key 'core.editor') to: #{GitReflow.default_editor}.", :notice
43
- end
44
- end
45
-
46
- # Start a new feature branch
47
- #
48
- # @param feature_branch [String] the name of the branch to create your feature on
49
- # @option base [String] the name of the base branch you want to checkout your feature from
50
- command(:start, defaults: {base: 'master'}) do |**params|
51
- base_branch = params[:base]
52
- feature_branch = params[:feature_branch]
53
-
54
- if feature_branch.nil? or feature_branch.length <= 0
55
- GitReflow.say "usage: git-reflow start [new-branch-name]", :error
56
- else
57
- GitReflow.run_command_with_label "git checkout #{base_branch}"
58
- GitReflow.run_command_with_label "git pull origin #{base_branch}"
59
- GitReflow.run_command_with_label "git push origin #{base_branch}:refs/heads/#{feature_branch}"
60
- GitReflow.run_command_with_label "git checkout --track -b #{feature_branch} origin/#{feature_branch}"
61
- end
62
- end
63
-
64
- # Submit a feature branch for review
65
- #
66
- # @option base [String] the name of the base branch you want to merge your feature into
67
- # @option title [String] the title of your pull request
68
- # @option body [String] the body of your pull request
69
- command(:review, defaults: {base: 'master'}) do |**params|
70
- base_branch = params[:base]
71
- create_pull_request = true
72
-
73
- GitReflow.fetch_destination base_branch
74
- begin
75
- GitReflow.push_current_branch
76
-
77
- existing_pull_request = GitReflow.git_server.find_open_pull_request( from: GitReflow.current_branch, to: base_branch )
78
- if existing_pull_request
79
- say "A pull request already exists for these branches:", :notice
80
- existing_pull_request.display_pull_request_summary
81
- else
82
- unless params[:title] || params[:body]
83
- pull_request_msg_file = "#{GitReflow.git_root_dir}/.git/GIT_REFLOW_PR_MSG"
84
-
85
- File.open(pull_request_msg_file, 'w') do |file|
86
- file.write(params[:title] || GitReflow.pull_request_template || GitReflow.current_branch)
87
- end
88
-
89
- GitReflow.run("#{GitReflow.git_editor_command} #{pull_request_msg_file}", with_system: true)
90
-
91
- pr_msg = File.read(pull_request_msg_file).split(/[\r\n]|\r\n/).map(&:strip)
92
- title = pr_msg.shift
93
-
94
- File.delete(pull_request_msg_file)
95
-
96
- unless pr_msg.empty?
97
- pr_msg.shift if pr_msg.first.empty?
98
- end
99
-
100
- params[:title] = title
101
- params[:body] = "#{pr_msg.join("\n")}\n"
102
-
103
- say "\nReview your PR:\n"
104
- say "--------\n"
105
- say "Title:\n#{params[:title]}\n\n"
106
- say "Body:\n#{params[:body]}\n"
107
- say "--------\n"
108
-
109
- create_pull_request = ask("Submit pull request? (Y)") =~ /y/i
110
- end
111
-
112
- if create_pull_request
113
- pull_request = GitReflow.git_server.create_pull_request(title: params[:title] || params[:body],
114
- body: params[:body],
115
- head: "#{GitReflow.remote_user}:#{GitReflow.current_branch}",
116
- base: params[:base])
117
-
118
- say "Successfully created pull request ##{pull_request.number}: #{pull_request.title}\nPull Request URL: #{pull_request.html_url}\n", :success
119
- else
120
- say "Review aborted. No pull request has been created.", :review_halted
121
- end
122
- end
123
- rescue Github::Error::UnprocessableEntity => e
124
- say "Github Error: #{e.to_s}", :error
125
- rescue StandardError => e
126
- say "\nError: #{e.inspect}", :error
127
- end
128
- end
129
-
130
- # Checks the status of an existing pull request
131
- #
132
- # @option destination_branch [String] the branch you're merging your feature into ('master' is default)
133
- command(:status, defaults: {destination_branch: 'master'}) do |**params|
134
- pull_request = GitReflow.git_server.find_open_pull_request( :from => GitReflow.current_branch, :to => params[:destination_branch] )
135
-
136
- if pull_request.nil?
137
- say "No pull request exists for #{GitReflow.current_branch} -> #{params[:destination_branch]}", :notice
138
- say "Run 'git reflow review #{params[:destination_branch]}' to start the review process", :notice
139
- else
140
- say "Here's the status of your review:"
141
- pull_request.display_pull_request_summary
142
- end
143
- end
144
-
145
- command(:deploy) do |**params|
146
- destination_server = params[:destination_server] || 'default'
147
- deploy_command = GitReflow::Config.get("reflow.deploy-to-#{destination_server}-command", local: true)
148
-
149
- # first check is to allow for automated setup
150
- if deploy_command.empty?
151
- deploy_command = ask("Enter the command you use to deploy to #{destination_server} (leaving blank will skip deployment)")
152
- end
153
-
154
- # second check is to see if the user wants to skip
155
- if deploy_command.empty?
156
- say "Skipping deployment..."
157
- false
158
- else
159
- GitReflow::Config.set("reflow.deploy-to-#{destination_server}-command", deploy_command, local: true)
160
- run_command_with_label(deploy_command, with_system: true)
161
- end
162
- end
163
-
164
- # Merge and deploy a feature branch to a staging branch
165
- command(:stage) do |**params|
166
- feature_branch_name = GitReflow.current_branch
167
- staging_branch_name = GitReflow::Config.get('reflow.staging-branch', local: true)
168
-
169
- if staging_branch_name.empty?
170
- staging_branch_name = GitReflow.ask("What's the name of your staging branch? (default: 'staging') ")
171
- staging_branch_name = 'staging' if staging_branch_name.strip == ''
172
- GitReflow::Config.set('reflow.staging-branch', staging_branch_name, local: true)
173
- end
174
-
175
- GitReflow.run_command_with_label "git checkout #{staging_branch_name}"
176
- GitReflow.run_command_with_label "git pull origin #{staging_branch_name}"
177
-
178
- if GitReflow.run_command_with_label "git merge #{feature_branch_name}", with_system: true
179
- GitReflow.run_command_with_label "git push origin #{staging_branch_name}"
180
-
181
- staged = self.deploy(destination_server: :staging)
182
-
183
- if staged
184
- GitReflow.say "Deployed to Staging.", :success
185
- else
186
- GitReflow.say "There were issues deploying to staging.", :error
187
- end
188
- else
189
- GitReflow.say "There were issues merging your feature branch to staging.", :error
190
- end
191
- end
192
-
193
- # Deliver a feature branch to a base branch
194
- #
195
- # @option base [String] base branch to merge your feature branch into
196
- # @option force [Boolean] whether to force-deliver the feature branch, ignoring any QA checks
197
- command(:deliver, defaults: {base: 'master'}) do |**params|
198
- begin
199
- existing_pull_request = GitReflow.git_server.find_open_pull_request( from: GitReflow.current_branch, to: params[:base] )
200
-
201
- if existing_pull_request.nil?
202
- say "No pull request exists for #{GitReflow.remote_user}:#{GitReflow.current_branch}\nPlease submit your branch for review first with \`git reflow review\`", :deliver_halted
203
- else
204
-
205
- if existing_pull_request.good_to_merge?(force: params[:force])
206
- # displays current status and prompts user for confirmation
207
- self.status destination_branch: params[:base]
208
- # TODO: change name of this in the merge! method
209
- params[:skip_lgtm] = params[:force] if params[:force]
210
- existing_pull_request.merge!(params)
211
- else
212
- say existing_pull_request.rejection_message, :deliver_halted
213
- end
214
-
215
- end
216
-
217
- rescue Github::Error::UnprocessableEntity => e
218
- say "Github Error: #{e.inspect}", :error
219
- end
220
- end
221
-
222
-
223
- # Updates and synchronizes your base branch and feature branch.
224
- #
225
- # Performs the following:
226
- # $ git checkout <base_branch>
227
- # $ git pull <remote_location> <base_branch>
228
- # $ git checkout <current_branch>
229
- # $ git pull origin <current_branch>
230
- # $ git merge <base_branch>
231
- # @param remote [String] the name of the remote repository to fetch updates from (origin by default)
232
- # @param base [String] the branch that you want to fetch updates from (master by default)
233
- command(:refresh, defaults: {remote: 'origin', base: 'master'}) do |**params|
234
- GitReflow.update_feature_branch(params)
235
- end
236
- end
237
- end
238
- end
1
+ $LOAD_PATH << File.expand_path(File.realpath(__dir__) + '/../..')
2
+ require 'git_reflow/workflow'
3
+
4
+ module GitReflow
5
+ module Workflows
6
+ # This class contains the core workflow for git-reflow. Going forward, this
7
+ # will act as the base class for customizing and extending git-reflow.
8
+ class Core
9
+ include ::GitReflow::Workflow
10
+
11
+ # Reads and evaluates the provided file in the context of this class
12
+ #
13
+ # @param workflow_path [String] the path of the Workflow file to eval
14
+ def self.load_workflow(workflow_path)
15
+ return unless workflow_path.length > 0 and File.exists?(workflow_path)
16
+ ::GitReflow.logger.debug "Using workflow: #{workflow_path}"
17
+ self.load_raw_workflow(File.read(workflow_path))
18
+ end
19
+
20
+ # Evaluates the provided string in the context of this class
21
+ #
22
+ # @param workflow_string [String] the contents of a Workflow file to eval
23
+ def self.load_raw_workflow(workflow_string)
24
+ return if workflow_string.strip.empty?
25
+ ::GitReflow.logger.debug "Evaluating workflow..."
26
+ binding.eval(workflow_string)
27
+ end
28
+
29
+ # Sets up the required git configurations that git-reflow depends on.
30
+ #
31
+ # @param [Hash] options the options to run setup with
32
+ # @option options [Boolean] :local (false) whether to configure git-reflow specific to the current project
33
+ # @option options [Boolean] :enterprise (false) whether to configure git-reflow for use with Github Enterprise
34
+ command(:setup, switches: { local: false, enterprise: false}) do |**params|
35
+ reflow_options = { project_only: params[:local], enterprise: params[:enterprise] }
36
+ existing_git_include_paths = git_config.get('include.path', all: true).split("\n")
37
+
38
+ unless File.exist?(git_config::CONFIG_FILE_PATH) or existing_git_include_paths.include?(git_config::CONFIG_FILE_PATH)
39
+ say "We'll walk you through setting up git-reflow's defaults for all your projects.", :notice
40
+ say "In the future, you can run \`git-reflow setup --local\` from the root of any project you want to setup differently.", :notice
41
+ run "touch #{git_config::CONFIG_FILE_PATH}"
42
+ say "Created #{git_config::CONFIG_FILE_PATH} for git-reflow specific configurations.", :notice
43
+ git_config.add "include.path", git_config::CONFIG_FILE_PATH, global: true
44
+ say "Added #{git_config::CONFIG_FILE_PATH} to include.path in $HOME/.gitconfig.", :notice
45
+ end
46
+
47
+ choose do |menu|
48
+ menu.header = "Available remote Git Server services"
49
+ menu.prompt = "Which service would you like to use for this project? "
50
+
51
+ menu.choice('GitHub') { ::GitReflow::GitServer.connect reflow_options.merge({ provider: 'GitHub', silent: false }) }
52
+ menu.choice('BitBucket (team-owned repos only)') { ::GitReflow::GitServer.connect reflow_options.merge({ provider: 'BitBucket', silent: false }) }
53
+ end
54
+
55
+ git_config.set "constants.minimumApprovals", ask("Set the minimum number of approvals (leaving blank will require approval from all commenters): "), local: reflow_options[:project_only]
56
+ git_config.set "constants.approvalRegex", ::GitReflow::GitServer::PullRequest::DEFAULT_APPROVAL_REGEX, local: reflow_options[:project_only]
57
+
58
+ if git_config.get('core.editor').length <= 0
59
+ git_config.set('core.editor', default_editor, local: reflow_options[:project_only])
60
+ say "Updated git's editor (via git config key 'core.editor') to: #{default_editor}.", :notice
61
+ end
62
+ end
63
+ command_help(
64
+ :setup,
65
+ summary: "Connect your GitServer (e.g. GitHub) account to git-reflow",
66
+ switches: {
67
+ local: "setup GitReflow for the current project only",
68
+ enterprise: "setup GitReflow with a Github Enterprise account",
69
+ }
70
+ )
71
+
72
+ # Start a new feature branch
73
+ #
74
+ # @param [Hash] options the options to run start with
75
+ # @option options [String] :base ("master") the name of the base branch you want to checkout your feature from
76
+ # @option options [String] :feature_branch the name of the base branch you want to checkout your feature from
77
+ command(:start, arguments: { feature_branch: nil }, flags: { base: nil }) do |**params|
78
+ base_branch = params[:base] || default_base_branch
79
+ feature_branch = params[:feature_branch]
80
+
81
+ if feature_branch.nil? or feature_branch.length <= 0
82
+ say "usage: git-reflow start [new-branch-name]", :error
83
+ else
84
+ run_command_with_label "git checkout #{base_branch}"
85
+ run_command_with_label "git pull origin #{base_branch}"
86
+ run_command_with_label "git push origin #{base_branch}:refs/heads/#{feature_branch}"
87
+ run_command_with_label "git checkout --track -b #{feature_branch} origin/#{feature_branch}"
88
+ end
89
+ end
90
+ command_help(
91
+ :start,
92
+ summary: "This will create a new feature branch and setup remote tracking",
93
+ arguments: { new_feature_branch: "name of the new feature branch" },
94
+ flags: { base: "name of a branch you want to branch off of" },
95
+ description: <<LONGTIME
96
+ Performs the following:\n
97
+ \t$ git checkout <base_branch>\n
98
+ \t$ git pull origin <base_branch>\n
99
+ \t$ git push origin <base_branch>:refs/heads/[new_feature_branch]\n
100
+ \t$ git checkout --track -b [new_feature_branch] origin/[new_feature_branch]\n
101
+ LONGTIME
102
+ )
103
+
104
+ # Submit a feature branch for review
105
+ #
106
+ # @param [Hash] options the options to run review with
107
+ # @option options [String] :base (GitReflow::Config.get('reflow.base-branch') or "master") the name of the base branch you want to merge your feature into
108
+ # @option options [String] :title (<current-branch-name>) the title of your pull request
109
+ # @option options [String] :message ("") the body of your pull request
110
+ command(:review, arguments: { base: nil }, flags: { title: nil, message: nil }) do |**params|
111
+ base_branch = params[:base] || default_base_branch
112
+ create_pull_request = true
113
+
114
+ fetch_destination base_branch
115
+ begin
116
+ push_current_branch
117
+
118
+ existing_pull_request = git_server.find_open_pull_request( from: current_branch, to: base_branch )
119
+ if existing_pull_request
120
+ say "A pull request already exists for these branches:", :notice
121
+ existing_pull_request.display_pull_request_summary
122
+ else
123
+ unless params[:title] || params[:message]
124
+ pull_request_msg_file = "#{git_root_dir}/.git/GIT_REFLOW_PR_MSG"
125
+
126
+ File.open(pull_request_msg_file, 'w') do |file|
127
+ begin
128
+ pr_message = params[:title] || pull_request_template || current_branch
129
+ file.write(pr_message)
130
+ rescue StandardError => e
131
+ logger.error "Unable to parse PR template (#{pull_request_msg_file}): #{e.inspect}"
132
+ file.write(params[:title] || current_branch)
133
+ end
134
+ end
135
+
136
+ run("#{git_editor_command} #{pull_request_msg_file}", with_system: true)
137
+
138
+ pr_msg = File.read(pull_request_msg_file).split(/[\r\n]|\r\n/).map(&:strip)
139
+ title = pr_msg.shift
140
+
141
+ File.delete(pull_request_msg_file)
142
+
143
+ unless pr_msg.empty?
144
+ pr_msg.shift if pr_msg.first.empty?
145
+ end
146
+
147
+ params[:title] = title
148
+ params[:message] = "#{pr_msg.join("\n")}\n"
149
+
150
+ say "\nReview your PR:\n"
151
+ say "--------\n"
152
+ say "Title:\n#{params[:title]}\n\n"
153
+ say "Body:\n#{params[:message]}\n"
154
+ say "--------\n"
155
+
156
+ create_pull_request = ask("Submit pull request? (Y)") =~ /y/i
157
+ end
158
+
159
+ if create_pull_request
160
+ begin
161
+ retries ||= 0
162
+ pull_request = git_server.create_pull_request(
163
+ title: params[:title] || params[:message],
164
+ body: params[:message],
165
+ head: "#{remote_user}:#{current_branch}",
166
+ base: base_branch
167
+ )
168
+ rescue Github::Error::UnprocessableEntity
169
+ retry if (retries += 1) < 3
170
+ raise
171
+ end
172
+
173
+ say "Successfully created pull request ##{pull_request.number}: #{pull_request.title}\nPull Request URL: #{pull_request.html_url}\n", :success
174
+ else
175
+ say "Review aborted. No pull request has been created.", :review_halted
176
+ end
177
+ end
178
+ rescue Github::Error::UnprocessableEntity => e
179
+ say "Github Error: #{e.to_s}", :error
180
+ rescue StandardError => e
181
+ say "\nError: #{e.inspect}", :error
182
+ end
183
+ end
184
+ command_help(
185
+ :review,
186
+ summary: "Pushes your latest feature branch changes to your remote repo and creates a pull request",
187
+ arguments: {
188
+ base: "the branch you want to merge your feature branch into"
189
+ },
190
+ flags: {
191
+ title: "the title of the Pull Request we'll create",
192
+ message: "the body of the Pull Request we'll create"
193
+ }
194
+ )
195
+
196
+ # Checks the status of an existing pull request
197
+ #
198
+ # @param [Hash] options the options to run review with
199
+ # @option options [String] :destination_branch ("master") the branch you're merging your feature into
200
+ command(:status, arguments: { destination_branch: nil }) do |**params|
201
+ base_branch = params[:destination_branch] || default_base_branch
202
+ pull_request = git_server.find_open_pull_request( :from => current_branch, :to => base_branch )
203
+
204
+ if pull_request.nil?
205
+ say "No pull request exists for #{current_branch} -> #{base_branch}", :notice
206
+ say "Run 'git reflow review #{base_branch}' to start the review process", :notice
207
+ else
208
+ say "Here's the status of your review:"
209
+ pull_request.display_pull_request_summary
210
+ end
211
+ end
212
+ command_help(
213
+ :status,
214
+ summary: "Display information about the status of your feature in the review process",
215
+ arguments: {
216
+ destination_branch: "the branch to merge your feature into"
217
+ }
218
+ )
219
+
220
+ # Deploys the current branch to a specified server
221
+ #
222
+ # @param [Hash] options the options to run review with
223
+ # @option options [String] :destination_server ("default") the environment server to deploy to (pulled from `git config "reflow.deploy-to-#{destination_server}-command")
224
+ command(:deploy, arguments: { destination_server: "default" }) do |**params|
225
+ destination_server = params[:destination_server] || "default"
226
+ deploy_command = git_config.get("reflow.deploy-to-#{destination_server}-command", local: true)
227
+
228
+ # first check is to allow for automated setup
229
+ if deploy_command.empty?
230
+ deploy_command = ask("Enter the command you use to deploy to #{destination_server} (leaving blank will skip deployment)")
231
+ end
232
+
233
+ # second check is to see if the user wants to skip
234
+ if deploy_command.empty?
235
+ say "Skipping deployment..."
236
+ false
237
+ else
238
+ git_config.set("reflow.deploy-to-#{destination_server}-command", deploy_command, local: true)
239
+ run_command_with_label(deploy_command, with_system: true)
240
+ end
241
+ end
242
+ command_help(
243
+ :deploy,
244
+ summary: "Deploys the current branch to a specified server",
245
+ arguments: {
246
+ destination_server: 'the environment to deploy to (from: `git config "reflow.deploy-to-#{destination_server}-command"`)'
247
+ }
248
+ )
249
+
250
+ # Merge and deploy a feature branch to a staging branch
251
+ command(:stage) do |**params|
252
+ feature_branch_name = current_branch
253
+ staging_branch_name = git_config.get('reflow.staging-branch', local: true)
254
+
255
+ if staging_branch_name.empty?
256
+ staging_branch_name = ask("What's the name of your staging branch? (default: 'staging') ")
257
+ staging_branch_name = 'staging' if staging_branch_name.strip == ''
258
+ git_config.set('reflow.staging-branch', staging_branch_name, local: true)
259
+ end
260
+
261
+ run_command_with_label "git checkout #{staging_branch_name}"
262
+ run_command_with_label "git pull origin #{staging_branch_name}"
263
+
264
+ if run_command_with_label "git merge #{feature_branch_name}", with_system: true
265
+ run_command_with_label "git push origin #{staging_branch_name}"
266
+
267
+ staged = self.deploy(destination_server: :staging)
268
+
269
+ if staged
270
+ say "Deployed to Staging.", :success
271
+ else
272
+ say "There were issues deploying to staging.", :error
273
+ end
274
+ else
275
+ say "There were issues merging your feature branch to staging.", :error
276
+ end
277
+ end
278
+ command_help(
279
+ :stage,
280
+ summary: "Merge and deploy a feature branch to a staging branch"
281
+ )
282
+
283
+ # Deliver a feature branch to a base branch
284
+ #
285
+ # @param [Hash] options the options to run review with
286
+ # @option options [String] :base ("master") the base branch to merge your feature branch into
287
+ # @option options [String] :force (false) whether to force-deliver the feature branch, ignoring any QA checks
288
+ command(:deliver, arguments: { base: nil }, flags: { merge_method: "squash" }, switches: { force: false, skip_lgtm: false }) do |**params|
289
+ params[:force] = params[:force] || params[:skip_lgtm]
290
+ params[:base] ||= default_base_branch
291
+
292
+ begin
293
+ existing_pull_request = git_server.find_open_pull_request( from: current_branch, to: params[:base] )
294
+
295
+ if existing_pull_request.nil?
296
+ say "No pull request exists for #{remote_user}:#{current_branch}\nPlease submit your branch for review first with \`git reflow review\`", :deliver_halted
297
+ else
298
+
299
+ if existing_pull_request.good_to_merge?(force: params[:force])
300
+ # displays current status and prompts user for confirmation
301
+ self.status destination_branch: params[:base]
302
+ existing_pull_request.merge!(params)
303
+ else
304
+ say existing_pull_request.rejection_message, :deliver_halted
305
+ end
306
+
307
+ end
308
+
309
+ rescue Github::Error::UnprocessableEntity => e
310
+ say "Github Error: #{e.inspect}", :error
311
+ end
312
+ end
313
+ command_help(
314
+ :deliver,
315
+ summary: "deliver your feature branch",
316
+ arguments: {
317
+ base: "the branch to merge this feature into"
318
+ },
319
+ flags: {
320
+ merge_method: "how you want your feature branch merged ('squash', 'merge', 'rebase')"
321
+ },
322
+ switches: {
323
+ force: "skip the lgtm checks and deliver your feature branch",
324
+ skip_lgtm: "skip the lgtm checks and deliver your feature branch"
325
+ },
326
+ description: "merge your feature branch down to your base branch, and cleanup your feature branch"
327
+ )
328
+
329
+
330
+ # Updates and synchronizes your base branch and feature branch.
331
+ #
332
+ # Performs the following:
333
+ # $ git checkout <base_branch>
334
+ # $ git pull <remote_location> <base_branch>
335
+ # $ git checkout <current_branch>
336
+ # $ git pull origin <current_branch>
337
+ # $ git merge <base_branch>
338
+ #
339
+ # @param [Hash] options the options to run review with
340
+ # @option options [String] :remote ("origin") the name of the remote repository to fetch updates from
341
+ # @option options [String] :base ("master") the branch that you want to fetch updates from
342
+ command(:refresh, flags: { remote: 'origin', base: nil}) do |**params|
343
+ params[:base] ||= default_base_branch
344
+ update_feature_branch(params)
345
+ end
346
+ command_help(
347
+ :refresh,
348
+ summary: "Updates and synchronizes your base branch and feature branch.",
349
+ flags: {
350
+ base: "branch to merge into",
351
+ remote: "remote repository name to fetch updates from",
352
+ },
353
+ description: <<LONGTIME
354
+ Performs the following:\n
355
+ \t$ git checkout <base_branch>\n
356
+ \t$ git pull <remote_location> <base_branch>\n
357
+ \t$ git checkout <current_branch>\n
358
+ \t$ git pull origin <current_branch>\n
359
+ \t$ git merge <base_branch>\n
360
+ LONGTIME
361
+ )
362
+ end
363
+ end
364
+ end