git_helper 3.2.0 → 3.3.2

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 (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