git_helper 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 155df8ff3253824dd7a7ba9b8e1c68862bd84c02985b56cc5aae3e4a8111bf02
4
+ data.tar.gz: ec4aacae883bf8655dbb8382c738180cf29d150a67f674c6d8b3ed02dea76335
5
+ SHA512:
6
+ metadata.gz: 42382a1b731fbcc6a601a733158deb41c38af92141d438148330736c5be2d5934f5283524fec215e41c61375a1d0fef341a318dce2c925c42e857c5d4950b3fc
7
+ data.tar.gz: 7307415e608846cd7f55bae4540fff9a322e19bb6813c1cbd22291370cb7432ba44105b393cbd7f32614ccbf9f8821534aea9d6f99ec8048de7a917dd6767cc2
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,97 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ git_helper (1.0.0)
5
+ gitlab (~> 4.16)
6
+ gli (~> 2.13)
7
+ highline (~> 2.0)
8
+ octokit (~> 4.18)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ addressable (2.7.0)
14
+ public_suffix (>= 2.0.2, < 5.0)
15
+ coderay (1.1.3)
16
+ diff-lcs (1.4.4)
17
+ faraday (1.0.1)
18
+ multipart-post (>= 1.2, < 3)
19
+ ffi (1.13.1)
20
+ formatador (0.2.5)
21
+ gitlab (4.16.1)
22
+ httparty (~> 0.14, >= 0.14.0)
23
+ terminal-table (~> 1.5, >= 1.5.1)
24
+ gli (2.19.2)
25
+ guard (2.16.2)
26
+ formatador (>= 0.2.4)
27
+ listen (>= 2.7, < 4.0)
28
+ lumberjack (>= 1.0.12, < 2.0)
29
+ nenv (~> 0.1)
30
+ notiffany (~> 0.0)
31
+ pry (>= 0.9.12)
32
+ shellany (~> 0.0)
33
+ thor (>= 0.18.1)
34
+ guard-compat (1.2.1)
35
+ guard-rspec (4.7.3)
36
+ guard (~> 2.1)
37
+ guard-compat (~> 1.1)
38
+ rspec (>= 2.99.0, < 4.0)
39
+ highline (2.0.3)
40
+ httparty (0.18.1)
41
+ mime-types (~> 3.0)
42
+ multi_xml (>= 0.5.2)
43
+ listen (3.2.1)
44
+ rb-fsevent (~> 0.10, >= 0.10.3)
45
+ rb-inotify (~> 0.9, >= 0.9.10)
46
+ lumberjack (1.2.8)
47
+ method_source (1.0.0)
48
+ mime-types (3.3.1)
49
+ mime-types-data (~> 3.2015)
50
+ mime-types-data (3.2020.0512)
51
+ multi_xml (0.6.0)
52
+ multipart-post (2.1.1)
53
+ nenv (0.3.0)
54
+ notiffany (0.1.3)
55
+ nenv (~> 0.1)
56
+ shellany (~> 0.0)
57
+ octokit (4.18.0)
58
+ faraday (>= 0.9)
59
+ sawyer (~> 0.8.0, >= 0.5.3)
60
+ pry (0.13.1)
61
+ coderay (~> 1.1)
62
+ method_source (~> 1.0)
63
+ public_suffix (4.0.6)
64
+ rake (10.5.0)
65
+ rb-fsevent (0.10.4)
66
+ rb-inotify (0.10.1)
67
+ ffi (~> 1.0)
68
+ rspec (2.99.0)
69
+ rspec-core (~> 2.99.0)
70
+ rspec-expectations (~> 2.99.0)
71
+ rspec-mocks (~> 2.99.0)
72
+ rspec-core (2.99.2)
73
+ rspec-expectations (2.99.2)
74
+ diff-lcs (>= 1.1.3, < 2.0)
75
+ rspec-mocks (2.99.4)
76
+ sawyer (0.8.2)
77
+ addressable (>= 2.3.5)
78
+ faraday (> 0.8, < 2.0)
79
+ shellany (0.0.1)
80
+ terminal-table (1.8.0)
81
+ unicode-display_width (~> 1.1, >= 1.1.1)
82
+ thor (1.0.1)
83
+ unicode-display_width (1.7.0)
84
+
85
+ PLATFORMS
86
+ ruby
87
+
88
+ DEPENDENCIES
89
+ bundler (~> 2.1)
90
+ git_helper!
91
+ guard (~> 2.6)
92
+ guard-rspec (~> 4.3)
93
+ rake (~> 10.1)
94
+ rspec (~> 2.99)
95
+
96
+ BUNDLED WITH
97
+ 2.1.4
@@ -0,0 +1,5 @@
1
+ guard :rspec, cmd: 'bundle exec rspec', all_on_start: true, all_after_pass: true do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright 2020 Emma Sax
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,124 @@
1
+ # git_helper [![Maintainability](https://api.codeclimate.com/v1/badges/ce1bdd719cc21b7c22a6/maintainability)](https://codeclimate.com/github/emmasax4/git_helper/maintainability)
2
+
3
+ ## Usage
4
+
5
+ ```bash
6
+ gem install git_helper
7
+ ```
8
+
9
+ Some of the commands in this gem can be used without any additional configuration. However, others utilize special GitHub or GitLab configuration. To provide access tokens for this, create a `~/.git_config.yml` file. The contents should look like this:
10
+
11
+ ```
12
+ :github_user: GITHUB-USERNAME
13
+ :github_token: GITHUB-TOKEN
14
+ :gitlab_user: GITLAB-USERNAME
15
+ :gitlab_token: GITLAB-TOKEN
16
+ ```
17
+
18
+ To view the help screen, run:
19
+ ```bash
20
+ ghelper --help
21
+ ```
22
+
23
+ To see what version of git_helper you're running, run:
24
+ ```bash
25
+ ghelper --version
26
+ ```
27
+
28
+ ## Commands
29
+
30
+ ### `change-remote`
31
+
32
+ This can be used when switching the owners of a GitHub repo. When you switch a username, GitHub only makes some changes for you. With this command, you no longer have to manually walk through local repo and switch the remotes from each one into a remote with the new username.
33
+
34
+ This command will go through every directory in a directory, see if it is a git directory, and then will check to see if the old username is included in the remote URL of that git directory. If it is, then the command will change the remote URL to instead point to the new username's remote URL. To run the command, run:
35
+
36
+ ```bash
37
+ ghelper change-remote OLD-OWNER NEW-OWNER
38
+ ```
39
+
40
+ ### `checkout-default`
41
+
42
+ This command will check out the default branch of whatever repository you're currently in. It looks at what branch the `origin/HEAD` remote is pointed to on your local machine, versus querying GitHub/GitLab for that, so if your local machine's remotes aren't up to date, then this command won't work as expected. To run this command, run:
43
+
44
+ ```bash
45
+ ghelper checkout-default
46
+ ```
47
+
48
+ If your local branches aren't right (run `git branch --remote` to see), then run:
49
+
50
+ ```bash
51
+ git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/CORRECT-DEFAULT-BRANCH-GOES-HERE
52
+ ```
53
+
54
+ ### `clean-git`
55
+
56
+ This command will bring you to the repository's default branch, `git pull`, `git fetch -p`, and will clean up your local branches on your machine by seeing which ones are existing on the remote, and updating yours accordingly. To run:
57
+
58
+ ```bash
59
+ ghelper clean-git
60
+ ```
61
+
62
+ ### `empty-commit`
63
+
64
+ For some reason, I'm always forgetting the commands to create an empty commit. So with this command, it becomes easy. The commit message of this commit will be `'Empty commit'`. To run the command, run:
65
+
66
+ ```bash
67
+ ghelper empty-commit
68
+ ```
69
+
70
+ ### `new-branch`
71
+
72
+ This command is useful for making new branches in a repository on the command line. To run the command, run:
73
+
74
+ ```bash
75
+ ghelper new-branch
76
+ # OR
77
+ ghelper new-branch NEW_BRANCH_NAME
78
+ ```
79
+
80
+ The command either accepts a branch name right away or it will ask you for the name of your new branch. Make sure your input does not contain any spaces or special characters.
81
+
82
+ ### `pull-request`
83
+
84
+ This command can be used to handily make new GitHub pull requests and to merge pull requests from the command line. The command uses the [`Octokit::Client`](https://octokit.github.io/octokit.rb/Octokit/Client.html) to do this, so make sure you have a `.git_config.yml` file set up in the home directory of your computer. For instructions on how to do that, see [Usage](#usage).
85
+
86
+ After setup is complete, you can call the file, and send in a flag indicating whether to create a pull request, `-c`, or to merge a pull request, `-m`.
87
+
88
+ ```bash
89
+ ghelper pull-request -c
90
+ # OR
91
+ ghelper pull-request -m
92
+ ```
93
+
94
+ If you're trying to create a pull request, the command will provide an autogenerated pull request title based on your branch name (separated by `_`). You can respond 'yes' or 'no'. If you respond 'no', you can provide your own pull request title. The command will also ask you if the default branch of the repository is the proper base branch to use. You can say 'yes' or 'no'. If you respond 'no', then you can give the command your chosen base base. Lastly, it'll ask the user to apply any pull request templates found at any `*/pull_request_template.md` file or any file in `.github/PULL_REQUEST_TEMPLATE/*.md`. Applying any template is optional, and a user can make an empty pull request if they desire.
95
+
96
+ If you're requesting to merge a pull request, the command will ask you the number ID of the pull request you wish to merge. The command will also ask you what type of merge to do: regular merging, squashing, or rebasing. The commit message to use during the merge/squash/rebase will be the title of the pull request.
97
+
98
+ If you're getting stuck, you can run the command with a `--help` flag instead, to get some more information.
99
+
100
+ ### `merge-request`
101
+
102
+ This command can be used to handily make new GitLab merge requests and to accept merge requests from the command line. The command uses the Ruby wrapper [`Gitlab`](https://github.com/NARKOZ/gitlab) to do this, so make sure you have a `.git_config.yml` file set up in the home directory of your computer. For instructions on how to do that, see [Usage](#usage).
103
+
104
+ After setup is complete, you can call the file, and send in a flag indicating whether to create a pull request, `-c`, or to merge a pull request, `-m`.
105
+
106
+ ```bash
107
+ ghelper merge-request -c
108
+ # OR
109
+ ghelper merge-request -m
110
+ ```
111
+
112
+ If you're trying to create a merge request, the command will provide an autogenerated merge request title based on your branch name (separated by `_`). You can respond 'yes' or 'no'. If you respond 'no', you can provide your own merge request title. The command will also ask you if the default branch of the repository is the proper base branch to use. You can say 'yes' or 'no'. If you respond 'no', then you can give the command your chosen base base. Lastly, it'll ask the user to apply any merge request templates found at any `*/merge_request_template.md` file or any file in `.gitlab/merge_request_templates/*.md`. Applying any template is optional, and from the command's standpoint, a user can make an empty merge request if they desire. If a GitLab project automatically adds a template, then it may create a merge request with a default template anyway.
113
+
114
+ If you're requesting to merge a merge request, the command will ask you the number ID of the merge request you wish to accept. The command will also ask you what type of merge to do: regular merging or squashing. The commit message to use during the merge/squash will be the title of the pull request.
115
+
116
+ If you're getting stuck, you can run the command with a `--help` flag instead, to get some more information.
117
+
118
+ ## Contributing
119
+
120
+ To submit a feature request, bug ticket, etc, please submit an official [GitHub Issue](https://github.com/emmasax4/git_helper/issues/new).
121
+
122
+ To report any security vulnerabilities, please view this project's [Security Policy](https://github.com/emmasax4/git_helper/security/policy).
123
+
124
+ This repository does have a standard [Code of Conduct](https://github.com/emmasax4/git_helper/blob/main/.github/code_of_conduct.md).
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Hubstats'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib'
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+ task :default => :test
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'gli'
4
+ require_relative '../lib/git_helper'
5
+
6
+ include GLI::App
7
+
8
+ program_desc 'GitHub and GitLab workflow scripts.'
9
+ version GitHelper::VERSION
10
+
11
+ autocomplete_commands false
12
+ wrap_help_text :verbatim
13
+
14
+ program_long_desc """
15
+ DOCUMENTATION
16
+ For documentation and help in setting up your Git configuration files,
17
+ see Git Helper's GitHub repo: https://github.com/emmasax4/git_helper
18
+ """
19
+
20
+ arg :old_owner
21
+ arg :new_owner
22
+ desc "Update a repository's remote URLs from an old GitHub owner to a new owner."
23
+ command 'change-remote' do |c|
24
+ c.action do |global_options, options, args|
25
+ require_relative '../lib/git_helper/change_remote.rb'
26
+ raise ArgumentError, 'You must specify an old owner and a new owner' unless args.count == 2
27
+ GitHelper::ChangeRemote.new.execute(args[0], args[1])
28
+ end
29
+ end
30
+
31
+ desc "Checks out the default branch of the repository based on the repository's local remote branches."
32
+ command 'checkout-default' do |c|
33
+ c.action do |global_options, options, args|
34
+ require_relative '../lib/git_helper/checkout_default.rb'
35
+ GitHelper::CheckoutDefault.new.execute
36
+ end
37
+ end
38
+
39
+ desc "Clean a repository's git branches."
40
+ command 'clean-git' do |c|
41
+ c.action do |global_options, options, args|
42
+ require_relative '../lib/git_helper/clean_git.rb'
43
+ GitHelper::CleanGit.new.execute
44
+ end
45
+ end
46
+
47
+ desc "Creates an empty commit with a basic commit message."
48
+ command 'empty-commit' do |c|
49
+ c.action do |global_options, options, args|
50
+ require_relative '../lib/git_helper/empty_commit.rb'
51
+ GitHelper::EmptyCommit.new.execute
52
+ end
53
+ end
54
+
55
+ desc 'Create a GitLab merge request from the current branch.'
56
+ command 'merge-request' do |c|
57
+ c.switch [:c, :create], desc: 'Create a new pull request'
58
+ c.switch [:m, :merge], desc: 'Merge an existing pull request'
59
+
60
+ c.action do |global_options, options, args|
61
+ require_relative '../lib/git_helper/merge_request.rb'
62
+ raise ArgumentError, 'You must specify an action (either --merge or --create)' unless options[:create] || options[:merge]
63
+
64
+ options = global_options.merge(options)
65
+
66
+ if options[:create]
67
+ GitHelper::GitLabMergeRequest.new.create
68
+ elsif options[:merge]
69
+ GitHelper::GitLabMergeRequest.new.merge
70
+ end
71
+ end
72
+ end
73
+
74
+ arg :new_branch_name, optional: true
75
+ desc 'Create a new branch for features, bug fixes, or experimentation.'
76
+ command 'new-branch' do |c|
77
+ c.action do |global_options, options, args|
78
+ require_relative '../lib/git_helper/new_branch.rb'
79
+ GitHelper::NewBranch.new.execute(args[0])
80
+ end
81
+ end
82
+
83
+ desc 'Create a GitHub pull request from the current branch.'
84
+ command 'pull-request' do |c|
85
+ c.switch [:c, :create], desc: 'Create a new pull request'
86
+ c.switch [:m, :merge], desc: 'Merge an existing pull request'
87
+
88
+ c.action do |global_options, options, args|
89
+ require_relative '../lib/git_helper/pull_request.rb'
90
+ raise ArgumentError, 'You must specify an action (either --merge or --create)' unless options[:create] || options[:merge]
91
+
92
+ options = global_options.merge(options)
93
+
94
+ if options[:create]
95
+ GitHelper::GitHubPullRequest.new.create
96
+ elsif options[:merge]
97
+ GitHelper::GitHubPullRequest.new.merge
98
+ end
99
+ end
100
+ end
101
+
102
+ exit run(ARGV)
@@ -0,0 +1,7 @@
1
+ require 'highline'
2
+ require 'yaml'
3
+ require 'gitlab'
4
+ require 'octokit'
5
+ require_relative 'git_helper/version'
6
+
7
+ module GitHelper; end
@@ -0,0 +1,55 @@
1
+ module GitHelper
2
+ class ChangeRemote
3
+ def execute(old_owner, new_owner)
4
+ current_dir = Dir.pwd
5
+ nested_dirs = Dir.entries(current_dir).select do |entry|
6
+ entry_dir = File.join(current_dir, entry)
7
+ File.directory?(entry_dir) && !(entry == '.' || entry == '..')
8
+ end
9
+
10
+ nested_dirs.each do |dir|
11
+ Dir.chdir dir
12
+ if File.exist?('.git')
13
+ puts "Found git directory: #{dir}."
14
+ remotes = `git remote -v`.split("\n")
15
+ remotes.each do |remote|
16
+ if resp.include?(old_owner)
17
+ puts " Git directory's remote is pointing to: '#{old_owner}'."
18
+ swap_ssh(old_owner, new_owner, remote) if remote.scan(/(git@)/).any?
19
+ swap_https(old_owner, new_owner, remote) if remote.scan(/(https:\/\/)/).any?
20
+ else
21
+ puts " No need to update remote."
22
+ end
23
+ end
24
+ end
25
+ Dir.chdir current_dir
26
+ end
27
+ end
28
+
29
+ private def swap_https(old_owner, new_owner, remote)
30
+ repo = remote.scan(/https:\/\/[\S]+\/([\S]*).git/).first.first
31
+ remote_name = remote.scan(/([a-zA-z]+)/).first.first
32
+ source_name = scan(/https:\/\/([a-zA-z.]+)\//).first.first
33
+ puts " Changing the remote URL #{remote_name} to be 'https://#{source_name}/#{new_owner}/#{repo}.git'."
34
+ begin
35
+ `git remote set-url #{remote_name} https://#{source_name}:#{new_owner}/#{repo}.git`
36
+ puts " Done."
37
+ rescue Exception => e
38
+ puts " Could not complete: #{e.message}"
39
+ end
40
+ end
41
+
42
+ private def swap_ssh(old_owner, new_owner, remote)
43
+ repo = remote.scan(/\/([\S]*).git/).first.first
44
+ remote_name = remote.scan(/([a-zA-z]+)/).first.first
45
+ source_name = remote.scan(/git@([a-zA-z.]+):/).first.first
46
+ puts " Changing the remote URL #{remote_name} to be 'git@#{source_name}:#{new_owner}/#{repo}.git'."
47
+ begin
48
+ `git remote set-url #{remote_name} git@#{source_name}:#{new_owner}/#{repo}.git`
49
+ puts " Done."
50
+ rescue Exception => e
51
+ puts " Could not complete: #{e.message}"
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,7 @@
1
+ module GitHelper
2
+ class CheckoutDefault
3
+ def execute
4
+ system("git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed \"s@^refs/remotes/origin/@@\")")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module GitHelper
2
+ class CleanGit
3
+ def execute
4
+ system("git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed \"s@^refs/remotes/origin/@@\")")
5
+ system("git pull")
6
+ system("git fetch -p")
7
+ system("git branch -vv | grep \"origin/.*: gone]\" | awk \"{print \$1}\" | grep -v \"*\" | xargs git branch -D")
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module GitHelper
2
+ class EmptyCommit
3
+ def execute
4
+ system("git commit --allow-empty -m \"Empty commit\"")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ require 'yaml'
2
+
3
+ module GitHelper
4
+ class GitConfigReader
5
+ def gitlab_token
6
+ config_file[:gitlab_token]
7
+ end
8
+
9
+ def github_token
10
+ config_file[:github_token]
11
+ end
12
+
13
+ private def config_file
14
+ YAML.load_file(git_config_file_path)
15
+ end
16
+
17
+ private def git_config_file_path
18
+ Dir.pwd.scan(/\A\/[\w]*\/[\w]*\//).first << git_config_file
19
+ end
20
+
21
+ private def git_config_file
22
+ '.git_config.yml'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ require 'gitlab'
2
+ require_relative './git_config_reader.rb'
3
+
4
+ module GitHelper
5
+ class GitLabClient
6
+ def client
7
+ Gitlab.client(endpoint: gitlab_endpoint, private_token: git_config_reader.gitlab_token)
8
+ end
9
+
10
+ private def git_config_reader
11
+ @git_config_reader ||= GitConfigReader.new
12
+ end
13
+
14
+ private def gitlab_endpoint
15
+ 'https://gitlab.com/api/v4'
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ require 'highline'
2
+
3
+ module GitHelper
4
+ class HighlineCli
5
+ def ask(prompt)
6
+ cli.ask(prompt) do |conf|
7
+ conf.readline = true
8
+ end.to_s
9
+ end
10
+
11
+ def ask_options(prompt, choices)
12
+ choices_as_string_options = ''
13
+ choices.each { |choice| choices_as_string_options << "#{choices.index(choice) + 1}. #{choice}\n" }
14
+ compiled_prompt = "#{prompt}\n#{choices_as_string_options.strip}"
15
+
16
+ cli.ask(compiled_prompt) do |conf|
17
+ conf.readline = true
18
+ end.to_i - 1
19
+ end
20
+
21
+ private def cli
22
+ @cli ||= HighLine.new
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,154 @@
1
+ require_relative './gitlab_client.rb'
2
+ require_relative './highline_cli.rb'
3
+
4
+ module GitHelper
5
+ class GitLabMergeRequest
6
+ def create
7
+ begin
8
+ # Ask these questions right away
9
+ base_branch
10
+ new_mr_title
11
+ options = {
12
+ source_branch: local_branch,
13
+ target_branch: base_branch,
14
+ description: new_mr_body
15
+ }
16
+
17
+ puts "Creating merge request: #{new_mr_title}"
18
+ mr = gitlab_client.create_merge_request(local_repo, new_mr_title, options)
19
+
20
+ if mr.diff_refs.base_sha == mr.diff_refs.head_sha
21
+ puts "Merge request was created, but no commits have been pushed to GitLab: #{mr.web_url}"
22
+ else
23
+ puts "Merge request successfully created: #{mr.web_url}"
24
+ end
25
+ rescue Gitlab::Error::Conflict => e
26
+ puts 'Could not create merge request:'
27
+ puts ' A merge request already exists for this branch'
28
+ rescue Exception => e
29
+ puts 'Could not create merge request:'
30
+ puts e.message
31
+ end
32
+ end
33
+
34
+ def merge
35
+ begin
36
+ # Ask these questions right away
37
+ mr_id
38
+ options = {}
39
+ options[:should_remove_source_branch] = remove_source_branch?
40
+ options[:squash] = squash_merge_request?
41
+ options[:squash_commit_message] = existing_mr_title
42
+
43
+ puts "Merging merge request: #{mr_id}"
44
+ merge = gitlab_client.accept_merge_request(local_repo, mr_id, options)
45
+ puts "Merge request successfully merged: #{merge.merge_commit_sha}"
46
+ rescue Gitlab::Error::MethodNotAllowed => e
47
+ puts 'Could not merge merge request:'
48
+ puts ' The merge request is not mergeable'
49
+ rescue Gitlab::Error::NotFound => e
50
+ puts 'Could not merge merge request:'
51
+ puts " Could not a locate a merge request to merge with ID #{mr_id}"
52
+ rescue Exception => e
53
+ puts 'Could not merge merge request:'
54
+ puts e.message
55
+ end
56
+ end
57
+
58
+ private def local_repo
59
+ # Get the repository by looking in the remote URLs for the full repository name
60
+ remotes = `git remote -v`
61
+ return remotes.scan(/\S[\s]*[\S]+.com[\S]{1}([\S]*).git/).first.first
62
+ end
63
+
64
+ private def local_branch
65
+ # Get the current branch by looking in the list of branches for the *
66
+ branches = `git branch`
67
+ return branches.scan(/\*\s([\S]*)/).first.first
68
+ end
69
+
70
+ private def read_template
71
+ if mr_template_options.count == 1
72
+ apply_template?(mr_template_options.first) ? File.open(mr_template_options.first).read : ''
73
+ else
74
+ template_file_name_to_apply = template_to_apply
75
+ template_file_name_to_apply == "None" ? '' : File.open(template_file_name_to_apply).read
76
+ end
77
+ end
78
+
79
+ private def mr_id
80
+ @mr_id ||= cli.ask('Merge Request ID?')
81
+ end
82
+
83
+ private def existing_mr_title
84
+ @existing_mr_title ||= gitlab_client.merge_request(local_repo, mr_id).title
85
+ end
86
+
87
+ private def new_mr_title
88
+ @new_mr_title ||= accept_autogenerated_title? ? autogenerated_title : cli.ask('Title?')
89
+ end
90
+
91
+ private def new_mr_body
92
+ @new_mr_body ||= mr_template_options.empty? ? '' : read_template
93
+ end
94
+
95
+ private def base_branch
96
+ @base_branch ||= base_branch_default? ? default_branch : cli.ask('Base branch?')
97
+ end
98
+
99
+ private def autogenerated_title
100
+ @autogenerated_title ||= local_branch.split('_')[0..-1].join(' ').capitalize
101
+ end
102
+
103
+ private def default_branch
104
+ @default_branch ||= gitlab_client.branches(local_repo).select { |branch| branch.default }.first.name
105
+ end
106
+
107
+ private def template_to_apply
108
+ return @template_to_apply if @template_to_apply
109
+ complete_options = mr_template_options << 'None'
110
+ index = cli.ask_options("Which merge request template should be applied?", complete_options)
111
+ @template_to_apply = complete_options[index]
112
+ end
113
+
114
+ private def mr_template_options
115
+ return @mr_template_options if @mr_template_options
116
+ nested_templates = Dir.glob(File.join("**/merge_request_templates", "*.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
117
+ non_nested_templates = Dir.glob(File.join("**", "merge_request_template.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
118
+ @mr_template_options = nested_templates.concat(non_nested_templates)
119
+ end
120
+
121
+ private def base_branch_default?
122
+ answer = cli.ask("Is '#{default_branch}' the correct base branch for your new merge request? (y/n)")
123
+ !!(answer =~ /^y/i)
124
+ end
125
+
126
+ private def accept_autogenerated_title?
127
+ answer = cli.ask("Accept the autogenerated merge request title '#{autogenerated_title}'? (y/n)")
128
+ !!(answer =~ /^y/i)
129
+ end
130
+
131
+ private def squash_merge_request?
132
+ answer = cli.ask('Squash merge request? (y/n)')
133
+ !!(answer =~ /^y/i)
134
+ end
135
+
136
+ private def remove_source_branch?
137
+ answer = cli.ask('Remove source branch after merging? (y/n)')
138
+ !!(answer =~ /^y/i)
139
+ end
140
+
141
+ private def apply_template?(template_file_name)
142
+ answer = cli.ask("Apply the merge request template from #{template_file_name}? (y/n)")
143
+ !!(answer =~ /^y/i)
144
+ end
145
+
146
+ private def gitlab_client
147
+ @gitlab_client ||= GitHelper::GitLabClient.new.client
148
+ end
149
+
150
+ private def cli
151
+ @cli ||= GitHelper::HighlineCli.new
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,18 @@
1
+ require_relative './highline_cli.rb'
2
+
3
+ module GitHelper
4
+ class NewBranch
5
+ def execute(new_branch_name = nil)
6
+ branch_name = new_branch_name || cli.ask('New branch name?')
7
+ puts "Attempting to create a new branch: #{branch_name}"
8
+ system("git pull")
9
+ system("git branch --no-track #{branch_name}")
10
+ system("git checkout #{branch_name}")
11
+ system("git push --set-upstream origin #{branch_name}")
12
+ end
13
+
14
+ private def cli
15
+ @cli ||= GitHelper::HighlineCli.new
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require 'octokit'
2
+ require_relative './git_config_reader.rb'
3
+
4
+ module GitHelper
5
+ class OctokitClient
6
+ def client
7
+ Octokit::Client.new(access_token: git_config_reader.github_token)
8
+ end
9
+
10
+ private def git_config_reader
11
+ @git_config_reader ||= GitHelper::GitConfigReader.new
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,165 @@
1
+ require_relative './octokit_client.rb'
2
+ require_relative './highline_cli.rb'
3
+
4
+ module GitHelper
5
+ class GitHubPullRequest
6
+ def create
7
+ begin
8
+ # Ask these questions right away
9
+ base_branch
10
+ new_pr_title
11
+ new_pr_body
12
+
13
+ puts "Creating pull request: #{new_pr_title}"
14
+ pr = octokit_client.create_pull_request(local_repo, base_branch, local_branch, new_pr_title, new_pr_body)
15
+ puts "Pull request successfully created: #{pr.html_url}"
16
+ rescue Octokit::UnprocessableEntity => e
17
+ puts 'Could not create pull request:'
18
+ if e.message.include?('pull request already exists')
19
+ puts ' A pull request already exists for this branch'
20
+ elsif e.message.include?('No commits between')
21
+ puts ' No commits have been pushed to GitHub'
22
+ else
23
+ puts e.message
24
+ end
25
+ rescue Exception => e
26
+ puts 'Could not create pull request:'
27
+ puts e.message
28
+ end
29
+ end
30
+
31
+ def merge
32
+ begin
33
+ # Ask these questions right away
34
+ pr_id
35
+ merge_method
36
+
37
+ puts "Merging pull request: #{pr_id}"
38
+ merge = octokit_client.merge_pull_request(local_repo, pr_id, existing_pr_title, { merge_method: merge_method })
39
+ puts "Pull request successfully merged: #{merge.sha}"
40
+ rescue Octokit::UnprocessableEntity => e
41
+ puts 'Could not merge pull request:'
42
+ puts e.message
43
+ rescue Octokit::NotFound => e
44
+ puts 'Could not merge pull request:'
45
+ puts " Could not a locate a pull request to merge with ID #{pr_id}"
46
+ rescue Octokit::MethodNotAllowed => e
47
+ puts 'Could not merge pull request:'
48
+ if e.message.include?('405 - Required status check')
49
+ puts ' A required status check has not passed'
50
+ elsif e.message.include?('405 - Base branch was modified')
51
+ puts ' The base branch has been modified'
52
+ elsif e.message.include?('405 - Pull Request is not mergeable')
53
+ puts ' The pull request is not mergeable'
54
+ elsif e.message.include?('405 - Rebase merges are not allowed on this repository')
55
+ puts ' Rebase merges are not allowed on this repository'
56
+ elsif e.message.include?('405 - Merge commits are not allowed on this repository')
57
+ puts ' Merge commits are not allowed on this repository'
58
+ elsif e.message.include?('405 - Squash commits are not allowed on this repository')
59
+ puts ' Squash merges are not allowed on this repository'
60
+ else
61
+ puts e.message
62
+ end
63
+ rescue Exception => e
64
+ puts 'Could not merge pull request:'
65
+ puts e.message
66
+ end
67
+ end
68
+
69
+ private def local_repo
70
+ # Get the repository by looking in the remote URLs for the full repository name
71
+ remotes = `git remote -v`
72
+ return remotes.scan(/\S[\s]*[\S]+.com[\S]{1}([\S]*).git/).first.first
73
+ end
74
+
75
+ private def local_branch
76
+ # Get the current branch by looking in the list of branches for the *
77
+ branches = `git branch`
78
+ return branches.scan(/\*\s([\S]*)/).first.first
79
+ end
80
+
81
+ private def read_template
82
+ if pr_template_options.count == 1
83
+ apply_template?(pr_template_options.first) ? File.open(pr_template_options.first).read : ''
84
+ else
85
+ template_file_name_to_apply = template_to_apply
86
+ template_file_name_to_apply == "None" ? '' : File.open(template_file_name_to_apply).read
87
+ end
88
+ end
89
+
90
+ private def merge_options
91
+ [ 'merge', 'squash', 'rebase' ]
92
+ end
93
+
94
+ private def pr_id
95
+ @pr_id ||= cli.ask('Pull Request ID?')
96
+ end
97
+
98
+ private def existing_pr_title
99
+ @existing_pr_title ||= octokit_client.pull_request(local_repo, pr_id).title
100
+ end
101
+
102
+ private def new_pr_title
103
+ @new_pr_title ||= accept_autogenerated_title? ? autogenerated_title : cli.ask('Title?')
104
+ end
105
+
106
+ private def new_pr_body
107
+ @new_pr_body ||= pr_template_options.empty? ? '' : read_template
108
+ end
109
+
110
+ private def base_branch
111
+ @base_branch ||= base_branch_default? ? default_branch : cli.ask('Base branch?')
112
+ end
113
+
114
+ private def autogenerated_title
115
+ @autogenerated_title ||= local_branch.split('_')[0..-1].join(' ').capitalize
116
+ end
117
+
118
+ private def default_branch
119
+ @default_branch ||= octokit_client.repository(local_repo).default_branch
120
+ end
121
+
122
+ private def merge_method
123
+ return @merge_method if @merge_method
124
+ index = cli.ask_options("Merge method?", merge_options)
125
+ @merge_method = merge_options[index]
126
+ end
127
+
128
+ private def template_to_apply
129
+ return @template_to_apply if @template_to_apply
130
+ complete_options = pr_template_options << 'None'
131
+ index = cli.ask_options("Which pull request template should be applied?", complete_options)
132
+ @template_to_apply = complete_options[index]
133
+ end
134
+
135
+ private def pr_template_options
136
+ return @pr_template_options if @pr_template_options
137
+ nested_templates = Dir.glob(File.join("**/PULL_REQUEST_TEMPLATE", "*.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
138
+ non_nested_templates = Dir.glob(File.join("**", "pull_request_template.md"), File::FNM_DOTMATCH | File::FNM_CASEFOLD)
139
+ @pr_template_options = nested_templates.concat(non_nested_templates)
140
+ end
141
+
142
+ private def base_branch_default?
143
+ answer = cli.ask("Is '#{default_branch}' the correct base branch for your new pull request? (y/n)")
144
+ !!(answer =~ /^y/i)
145
+ end
146
+
147
+ private def accept_autogenerated_title?
148
+ answer = cli.ask("Accept the autogenerated pull request title '#{autogenerated_title}'? (y/n)")
149
+ !!(answer =~ /^y/i)
150
+ end
151
+
152
+ private def apply_template?(template_file_name)
153
+ answer = cli.ask("Apply the pull request template from #{template_file_name}? (y/n)")
154
+ !!(answer =~ /^y/i)
155
+ end
156
+
157
+ private def octokit_client
158
+ @octokit_client ||= GitHelper::OctokitClient.new.client
159
+ end
160
+
161
+ private def cli
162
+ @cli ||= GitHelper::HighlineCli.new
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,3 @@
1
+ module GitHelper
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git_helper
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Emma Sax
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-09-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gitlab
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.16'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: gli
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.13'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: highline
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: octokit
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.18'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.18'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.6'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.3'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.3'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.99'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.99'
139
+ description: A set of GitHub and GitLab workflow scripts.
140
+ email:
141
+ - emma.sax4@gmail.com
142
+ executables:
143
+ - ghelper
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - Gemfile
148
+ - Gemfile.lock
149
+ - Guardfile
150
+ - LICENSE
151
+ - README.md
152
+ - Rakefile
153
+ - bin/ghelper
154
+ - lib/git_helper.rb
155
+ - lib/git_helper/change_remote.rb
156
+ - lib/git_helper/checkout_default.rb
157
+ - lib/git_helper/clean_git.rb
158
+ - lib/git_helper/empty_commit.rb
159
+ - lib/git_helper/git_config_reader.rb
160
+ - lib/git_helper/gitlab_client.rb
161
+ - lib/git_helper/highline_cli.rb
162
+ - lib/git_helper/merge_request.rb
163
+ - lib/git_helper/new_branch.rb
164
+ - lib/git_helper/octokit_client.rb
165
+ - lib/git_helper/pull_request.rb
166
+ - lib/git_helper/version.rb
167
+ homepage: https://github.com/emmasax4/git_helper
168
+ licenses:
169
+ - MIT
170
+ metadata: {}
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - lib
175
+ required_ruby_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ requirements: []
186
+ rubygems_version: 3.1.2
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: A set of GitHub and GitLab workflow scripts to provide a smoother development
190
+ process for your git projects.
191
+ test_files: []