git_helper 3.2.0 → 3.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +131 -0
  4. data/Guardfile +3 -1
  5. data/README.md +11 -6
  6. data/Rakefile +2 -0
  7. data/bin/git-helper +25 -13
  8. data/lib/git_helper.rb +5 -1
  9. data/lib/git_helper/change_remote.rb +11 -3
  10. data/lib/git_helper/checkout_default.rb +2 -0
  11. data/lib/git_helper/clean_branches.rb +2 -0
  12. data/lib/git_helper/code_request.rb +29 -15
  13. data/lib/git_helper/empty_commit.rb +2 -0
  14. data/lib/git_helper/forget_local_commits.rb +2 -0
  15. data/lib/git_helper/git_config_reader.rb +12 -6
  16. data/lib/git_helper/gitlab_client.rb +2 -0
  17. data/lib/git_helper/highline_cli.rb +15 -69
  18. data/lib/git_helper/local_code.rb +18 -8
  19. data/lib/git_helper/merge_request.rb +88 -68
  20. data/lib/git_helper/new_branch.rb +3 -1
  21. data/lib/git_helper/octokit_client.rb +2 -0
  22. data/lib/git_helper/pull_request.rb +95 -64
  23. data/lib/git_helper/setup.rb +116 -0
  24. data/lib/git_helper/version.rb +3 -1
  25. data/spec/git_helper/change_remote_spec.rb +19 -19
  26. data/spec/git_helper/checkout_default_spec.rb +2 -0
  27. data/spec/git_helper/clean_branches_spec.rb +2 -0
  28. data/spec/git_helper/code_request_spec.rb +27 -24
  29. data/spec/git_helper/empty_commit_spec.rb +2 -0
  30. data/spec/git_helper/forget_local_commits_spec.rb +2 -0
  31. data/spec/git_helper/git_config_reader_spec.rb +32 -4
  32. data/spec/git_helper/gitlab_client_spec.rb +2 -0
  33. data/spec/git_helper/highline_cli_spec.rb +21 -185
  34. data/spec/git_helper/local_code_spec.rb +2 -0
  35. data/spec/git_helper/merge_request_spec.rb +22 -21
  36. data/spec/git_helper/new_branch_spec.rb +4 -2
  37. data/spec/git_helper/octokit_client_spec.rb +2 -0
  38. data/spec/git_helper/pull_request_spec.rb +17 -15
  39. data/spec/git_helper/setup_spec.rb +180 -0
  40. data/spec/spec_helper.rb +3 -1
  41. metadata +22 -5
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitHelper
2
4
  class EmptyCommit
3
5
  def execute
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitHelper
2
4
  class ForgetLocalCommits
3
5
  def execute
@@ -1,23 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitHelper
2
4
  class GitConfigReader
3
5
  def gitlab_token
4
6
  config_file[:gitlab_token]
5
7
  end
6
8
 
9
+ def gitlab_user
10
+ config_file[:gitlab_user]
11
+ end
12
+
7
13
  def github_token
8
14
  config_file[:github_token]
9
15
  end
10
16
 
11
- private def config_file
12
- YAML.load_file(git_config_file_path)
17
+ def github_user
18
+ config_file[:github_user]
13
19
  end
14
20
 
15
- private def git_config_file_path
16
- Dir.pwd.scan(/\A\/[\w]*\/[\w]*\//).first << git_config_file
21
+ def git_config_file_path
22
+ "#{Dir.pwd.scan(%r{\A/\w*/\w*/}).first}.git_helper/config.yml"
17
23
  end
18
24
 
19
- private def git_config_file
20
- '.git_helper/config.yml'
25
+ private def config_file
26
+ YAML.load_file(git_config_file_path)
21
27
  end
22
28
  end
23
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitHelper
2
4
  class GitLabClient
3
5
  def client
@@ -1,85 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitHelper
2
4
  class HighlineCli
3
- def new_branch_name
4
- ask('New branch name?')
5
- end
6
-
7
- def process_directory_remotes?(directory)
8
- answer = ask("Found git directory: #{directory}. Do you wish to proceed in updating #{directory}'s remote URLs? (y/n)")
9
- answer.empty? ? true : !!(answer =~ /^y/i)
10
- end
11
-
12
- def conflicting_remote_clarification
13
- ask('Found git remotes for both GitHub and GitLab. Would you like to proceed with GitLab or GitHub? (github/gitlab)').downcase
14
- end
15
-
16
- def title
17
- ask('Title?')
18
- end
19
-
20
- def base_branch
21
- ask('Base branch?')
22
- end
23
-
24
- def code_request_id(request_type)
25
- ask("#{request_type} Request ID?")
26
- end
27
-
28
- def accept_autogenerated_title?(autogenerated_title)
29
- return false unless autogenerated_title
30
- answer = ask("Accept the autogenerated code request title '#{autogenerated_title}'? (y/n)")
31
- answer.empty? ? true : !!(answer =~ /^y/i)
32
- end
33
-
34
- def base_branch_default?(default_branch)
35
- answer = ask("Is '#{default_branch}' the correct base branch for your new code request? (y/n)")
36
- answer.empty? ? true : !!(answer =~ /^y/i)
37
- end
38
-
39
- def squash_merge_request?
40
- answer = ask('Squash merge request? (y/n)')
41
- answer.empty? ? true : !!(answer =~ /^y/i)
42
- end
43
-
44
- def remove_source_branch?
45
- answer = ask('Remove source branch after merging? (y/n)')
46
- answer.empty? ? true : !!(answer =~ /^y/i)
47
- end
48
-
49
- def merge_method(merge_options)
50
- index = ask_options("Merge method?", merge_options)
51
- merge_options[index]
52
- end
53
-
54
- def apply_template?(template_file_name, request_type)
55
- answer = ask("Apply the #{request_type} request template from #{template_file_name}? (y/n)")
56
- answer.empty? ? true : !!(answer =~ /^y/i)
57
- end
58
-
59
- def template_to_apply(template_options, request_type)
60
- complete_options = template_options << 'None'
61
- index = ask_options("Which #{request_type} request template should be applied?", complete_options)
62
- complete_options[index]
5
+ def ask(prompt)
6
+ highline_client.ask(prompt) do |conf|
7
+ conf.readline = true
8
+ end.to_s
63
9
  end
64
10
 
65
- #######################
66
- ### GENERAL METHODS ###
67
- #######################
68
-
69
- private def ask(prompt)
70
- highline_client.ask(prompt) do |conf|
11
+ def ask_yes_no(prompt)
12
+ answer = highline_client.ask(prompt) do |conf|
71
13
  conf.readline = true
72
14
  end.to_s
15
+
16
+ answer.empty? ? true : !!(answer =~ /^y/i)
73
17
  end
74
18
 
75
- private def ask_options(prompt, choices)
76
- choices_as_string_options = ''
19
+ def ask_options(prompt, choices)
20
+ choices_as_string_options = ''.dup
77
21
  choices.each { |choice| choices_as_string_options << "#{choices.index(choice) + 1}. #{choice}\n" }
78
22
  compiled_prompt = "#{prompt}\n#{choices_as_string_options.strip}"
79
23
 
80
- highline_client.ask(compiled_prompt) do |conf|
24
+ index = highline_client.ask(compiled_prompt) do |conf|
81
25
  conf.readline = true
82
26
  end.to_i - 1
27
+
28
+ choices[index]
83
29
  end
84
30
 
85
31
  private def highline_client
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitHelper
2
4
  class LocalCode
3
5
  def checkout_default
@@ -44,20 +46,20 @@ module GitHelper
44
46
  end
45
47
 
46
48
  def https_remote?(remote)
47
- remote.scan(/(https:\/\/)/).any?
49
+ remote.scan(%r{(https://)}).any?
48
50
  end
49
51
 
50
52
  def remote_project(remote)
51
53
  if https_remote?(remote)
52
- remote.scan(/https:\/\/[\S]+\/([\S]*).git/).first.first
54
+ remote.scan(%r{https://\S+/(\S*).git}).first.first
53
55
  elsif ssh_remote?(remote)
54
- remote.scan(/\/([\S]*).git/).first.first
56
+ remote.scan(%r{/(\S*).git}).first.first
55
57
  end
56
58
  end
57
59
 
58
60
  def remote_source(remote)
59
61
  if https_remote?(remote)
60
- remote.scan(/https:\/\/([a-zA-z.]+)\//).first.first
62
+ remote.scan(%r{https://([a-zA-z.]+)/}).first.first
61
63
  elsif ssh_remote?(remote)
62
64
  remote.scan(/git@([a-zA-z.]+):/).first.first
63
65
  end
@@ -73,18 +75,20 @@ module GitHelper
73
75
 
74
76
  def project_name
75
77
  # Get the repo/project name by looking in the remote URLs for the full name
76
- `git remote -v`.scan(/\S[\s]*[\S]+.com[\S]{1}([\S]*).git/).first.first
78
+ `git remote -v`.scan(/\S\s*\S+.com\S{1}(\S*).git/).first.first
77
79
  end
78
80
 
79
81
  def branch
80
82
  # Get the current branch by looking in the list of branches for the *
81
- `git branch`.scan(/\*\s([\S]*)/).first.first
83
+ `git branch`.scan(/\*\s(\S*)/).first.first
82
84
  end
83
85
 
84
86
  def default_branch
85
87
  `git symbolic-ref refs/remotes/origin/HEAD | sed "s@^refs/remotes/origin/@@" | tr -d "\n"`
86
88
  end
87
89
 
90
+ # rubocop:disable Metrics/AbcSize
91
+ # rubocop:disable Metrics/MethodLength
88
92
  def template_options(identifiers)
89
93
  nested_templates = Dir.glob(
90
94
  File.join("#{identifiers[:template_directory]}/#{identifiers[:nested_directory_name]}", '*.md'),
@@ -100,11 +104,15 @@ module GitHelper
100
104
  )
101
105
  nested_templates.concat(non_nested_templates).concat(root_templates).uniq
102
106
  end
107
+ # rubocop:enable Metrics/MethodLength
108
+ # rubocop:enable Metrics/AbcSize
103
109
 
104
110
  def read_template(file_name)
105
111
  File.open(file_name).read
106
112
  end
107
113
 
114
+ # rubocop:disable Metrics/AbcSize
115
+ # rubocop:disable Metrics/MethodLength
108
116
  def generate_title(local_branch)
109
117
  branch_arr = local_branch.split(local_branch.include?('_') ? '_' : '-')
110
118
 
@@ -112,11 +120,11 @@ module GitHelper
112
120
 
113
121
  if branch_arr.length == 1
114
122
  branch_arr.first.capitalize
115
- elsif branch_arr[0].scan(/([\w]+)/).any? && branch_arr[1].scan(/([\d]+)/).any? # branch includes jira_123 at beginning
123
+ elsif branch_arr[0].scan(/(\w+)/).any? && branch_arr[1].scan(/(\d+)/).any? # branch includes jira_123 at beginning
116
124
  issue = "#{branch_arr[0].upcase}-#{branch_arr[1]}"
117
125
  description = branch_arr[2..-1].join(' ')
118
126
  "#{issue} #{description.capitalize}"
119
- elsif branch_arr[0].scan(/([\w]+-[\d]+)/).any? # branch includes string jira-123 at beginning
127
+ elsif branch_arr[0].scan(/(\w+-\d+)/).any? # branch includes string jira-123 at beginning
120
128
  issue = branch_arr[0].upcase
121
129
  description = branch_arr[1..-1].join(' ')
122
130
  "#{issue} #{description.capitalize}"
@@ -124,5 +132,7 @@ module GitHelper
124
132
  branch_arr[0..-1].join(' ').capitalize
125
133
  end
126
134
  end
135
+ # rubocop:enable Metrics/MethodLength
136
+ # rubocop:enable Metrics/AbcSize
127
137
  end
128
138
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GitHelper
2
4
  class GitLabMergeRequest
3
5
  attr_accessor :local_project, :local_branch, :local_code, :cli, :base_branch, :new_mr_title
@@ -9,70 +11,74 @@ module GitHelper
9
11
  @cli = options[:cli]
10
12
  end
11
13
 
14
+ # rubocop:disable Metrics/MethodLength
15
+ # rubocop:disable Metrics/AbcSize
12
16
  def create(options)
13
17
  @base_branch = options[:base_branch]
14
18
  @new_mr_title = options[:new_title]
15
19
 
16
- begin
17
- options = {
18
- source_branch: local_branch,
19
- target_branch: base_branch,
20
- squash: squash_merge_request,
21
- remove_source_branch: remove_source_branch,
22
- description: new_mr_body
23
- }
24
-
25
- puts "Creating merge request: #{new_mr_title}"
26
- mr = gitlab_client.create_merge_request(local_project, new_mr_title, options)
27
-
28
- if mr.diff_refs.base_sha == mr.diff_refs.head_sha
29
- puts "Merge request was created, but no commits have been pushed to GitLab: #{mr.web_url}"
30
- else
31
- puts "Merge request successfully created: #{mr.web_url}"
32
- end
33
- rescue Gitlab::Error::Conflict => e
34
- puts 'Could not create merge request:'
35
- puts ' A merge request already exists for this branch'
36
- rescue Exception => e
37
- puts 'Could not create merge request:'
38
- puts e.message
20
+ options = {
21
+ source_branch: local_branch,
22
+ target_branch: base_branch,
23
+ squash: squash_merge_request,
24
+ remove_source_branch: remove_source_branch,
25
+ description: new_mr_body
26
+ }
27
+
28
+ puts "Creating merge request: #{new_mr_title}"
29
+ mr = gitlab_client.create_merge_request(local_project, new_mr_title, options)
30
+
31
+ if mr.diff_refs.base_sha == mr.diff_refs.head_sha
32
+ puts "Merge request was created, but no commits have been pushed to GitLab: #{mr.web_url}"
33
+ else
34
+ puts "Merge request successfully created: #{mr.web_url}"
39
35
  end
36
+ rescue Gitlab::Error::Conflict
37
+ puts 'Could not create merge request:'
38
+ puts ' A merge request already exists for this branch'
39
+ rescue StandardError => e
40
+ puts 'Could not create merge request:'
41
+ puts e.message
40
42
  end
43
+ # rubocop:enable Metrics/AbcSize
44
+ # rubocop:enable Metrics/MethodLength
41
45
 
46
+ # rubocop:disable Metrics/MethodLength
47
+ # rubocop:disable Metrics/AbcSize
42
48
  def merge
43
- begin
44
- mr_id
45
- options = {
46
- should_remove_source_branch: existing_mr.should_remove_source_branch || existing_mr.force_remove_source_branch,
47
- squash: existing_mr.squash,
48
- squash_commit_message: existing_mr.title
49
- }
50
-
51
- puts "Merging merge request: #{mr_id}"
49
+ mr_id
50
+ options = {
51
+ should_remove_source_branch: existing_mr.should_remove_source_branch || existing_mr.force_remove_source_branch,
52
+ squash: existing_mr.squash,
53
+ squash_commit_message: existing_mr.title
54
+ }
55
+
56
+ puts "Merging merge request: #{mr_id}"
57
+ merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
58
+
59
+ if merge.merge_commit_sha.nil?
60
+ options[:squash] = true
52
61
  merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
62
+ end
53
63
 
54
- if merge.merge_commit_sha.nil?
55
- options[:squash] = true
56
- merge = gitlab_client.accept_merge_request(local_project, mr_id, options)
57
- end
58
-
59
- if merge.merge_commit_sha.nil?
60
- puts 'Could not merge merge request:'
61
- puts " #{merge.merge_error}"
62
- else
63
- puts "Merge request successfully merged: #{merge.merge_commit_sha}"
64
- end
65
- rescue Gitlab::Error::MethodNotAllowed => e
66
- puts 'Could not merge merge request:'
67
- puts ' The merge request is not mergeable'
68
- rescue Gitlab::Error::NotFound => e
64
+ if merge.merge_commit_sha.nil?
69
65
  puts 'Could not merge merge request:'
70
- puts " Could not a locate a merge request to merge with ID #{mr_id}"
71
- rescue Exception => e
72
- puts 'Could not merge merge request:'
73
- puts e.message
66
+ puts " #{merge.merge_error}"
67
+ else
68
+ puts "Merge request successfully merged: #{merge.merge_commit_sha}"
74
69
  end
70
+ rescue Gitlab::Error::MethodNotAllowed
71
+ puts 'Could not merge merge request:'
72
+ puts ' The merge request is not mergeable'
73
+ rescue Gitlab::Error::NotFound
74
+ puts 'Could not merge merge request:'
75
+ puts " Could not a locate a merge request to merge with ID #{mr_id}"
76
+ rescue StandardError => e
77
+ puts 'Could not merge merge request:'
78
+ puts e.message
75
79
  end
80
+ # rubocop:enable Metrics/AbcSize
81
+ # rubocop:enable Metrics/MethodLength
76
82
 
77
83
  private def new_mr_body
78
84
  @new_mr_body ||= template_name_to_apply ? local_code.read_template(template_name_to_apply) : ''
@@ -80,39 +86,53 @@ module GitHelper
80
86
 
81
87
  private def template_name_to_apply
82
88
  return @template_name_to_apply if @template_name_to_apply
89
+
83
90
  @template_name_to_apply = nil
84
91
 
85
- unless mr_template_options.empty?
86
- if mr_template_options.count == 1
87
- apply_single_template = cli.apply_template?(mr_template_options.first, 'merge')
88
- @template_name_to_apply = mr_template_options.first if apply_single_template
89
- else
90
- response = cli.template_to_apply(mr_template_options, 'merge')
91
- @template_name_to_apply = response unless response == 'None'
92
- end
93
- end
92
+ determine_template unless mr_template_options.empty?
94
93
 
95
94
  @template_name_to_apply
96
95
  end
97
96
 
97
+ # rubocop:disable Metrics/MethodLength
98
+ private def determine_template
99
+ if mr_template_options.count == 1
100
+ apply_single_template = cli.ask_yes_no(
101
+ "Apply the merge request template from #{mr_template_options.first}? (y/n)"
102
+ )
103
+ @template_name_to_apply = mr_template_options.first if apply_single_template
104
+ else
105
+ response = cli.ask_options(
106
+ 'Which merge request template should be applied?', mr_template_options << 'None'
107
+ )
108
+ @template_name_to_apply = response unless response == 'None'
109
+ end
110
+ end
111
+ # rubocop:enable Metrics/MethodLength
112
+
98
113
  private def mr_template_options
99
- @mr_template_options ||= local_code.template_options({
100
- template_directory: '.gitlab',
101
- nested_directory_name: 'merge_request_templates',
102
- non_nested_file_name: 'merge_request_template'
103
- })
114
+ @mr_template_options ||= local_code.template_options(
115
+ {
116
+ template_directory: '.gitlab',
117
+ nested_directory_name: 'merge_request_templates',
118
+ non_nested_file_name: 'merge_request_template'
119
+ }
120
+ )
104
121
  end
105
122
 
106
123
  private def mr_id
107
- @mr_id ||= cli.code_request_id('Merge')
124
+ @mr_id ||= cli.ask('Merge Request ID?')
108
125
  end
109
126
 
110
127
  private def squash_merge_request
111
- @squash_merge_request ||= cli.squash_merge_request?
128
+ @squash_merge_request ||= cli.ask_yes_no('Squash merge request? (y/n)')
112
129
  end
113
130
 
114
131
  private def remove_source_branch
115
- @remove_source_branch ||= existing_project.remove_source_branch_after_merge || cli.remove_source_branch?
132
+ @remove_source_branch ||=
133
+ existing_project.remove_source_branch_after_merge || cli.ask_yes_no(
134
+ 'Remove source branch after merging? (y/n)'
135
+ )
116
136
  end
117
137
 
118
138
  private def existing_project