github_helper 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in github_helper.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tristan Foureur
2
+
3
+ MIT License
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.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # GithubHelper
2
+ This useful little gem allows you **in a single command** to commit your staged changes, push them to your remote repository, and open a pull request for those changes.
3
+
4
+ Usually, when you have a quick fix to submit for an upstream repository, you would have to :
5
+
6
+ * Commit your fix
7
+ * Push your fix to your repository
8
+ * Go to your repository on Github
9
+ * Open a pullrequest and type the title/description
10
+ * Submit the pullrequest *(And you won't know if it's mergeable)*
11
+
12
+ GithubHelper does all that for you just with :
13
+
14
+ ghh -fm "Fixing a bug"
15
+ Or
16
+
17
+ git commit -m "Fixing a bug"
18
+ ghh -f
19
+
20
+ ## Installation
21
+
22
+ Just install the gem using :
23
+
24
+ gem install github_helper
25
+
26
+ ## Usage
27
+ To add an issue ID to the Pull request's description, just use the `-i` option.
28
+
29
+ To get some help, just run `ghh -h`
30
+
31
+ Here are the detailed options :
32
+
33
+ -f, --fast Fast mode : push to origin & pull-request
34
+ -r, --request Creates a pull request
35
+ -c, --commit Commit staged changes
36
+ -m, --message [message] Your commit message
37
+ -i, --id [id] Issue ID
38
+ --reset-config Resets your YML config file and enter your data again.
39
+ -l, --pulls Lists the open pull requests
40
+ -t, --title [TITLE] Pull request title. (Defaults to latest commit)
41
+ -d, --description [BODY] Pull request description. (Defaults to an empty string)
42
+ -b, --base [master] Pull request base. (Defaults to master)
43
+ -p, --push Push branch to remote before doing the pullrequest(To username/currentbranch)
44
+ -n, --head [head] Pull request head (Defaults to current branch)
45
+ -v, --verbose Enables verbose mode
46
+ -h, --help Displays this screen
47
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/ghh ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env ruby
2
+ require 'github_helper'
3
+ require 'github_helper/first_time'
4
+ require 'optparse'
5
+
6
+ # Basic class to know that we have an error with the arguments
7
+ class ArgException < Exception
8
+ end
9
+
10
+ options = {}
11
+ action = false
12
+
13
+ optparse = OptionParser.new do|opts|
14
+
15
+ # Set the banner
16
+ opts.banner = "GitHelper #{GithubHelper::VERSION} - Easy & fast commandline pull-request - Tristan Foureur (2012)\nUsage : ghh\nList of options:"
17
+
18
+ # Define the options
19
+ options[:fastmode] = false
20
+ opts.on( '-f', '--fast', 'Fast mode : push to origin & pull-request' ) do
21
+ options[:fastmode] = true
22
+ action = true
23
+ end
24
+
25
+ options[:createpull] = false
26
+ opts.on( '-r', '--request', 'Creates a pull request' ) do
27
+ options[:createpull] = true
28
+ action = true;
29
+ end
30
+
31
+ options[:commit] = false
32
+ opts.on( '-c', '--commit', 'Commit staged changes' ) do
33
+ options[:commit] = true
34
+ action = true;
35
+ end
36
+
37
+ options[:commitmessage] = false
38
+ opts.on( '-m', '--message [message]', 'Your commit message' ) do |m|
39
+ options[:commitmessage] = m
40
+ end
41
+
42
+ options[:issueid] = false
43
+ opts.on( '-i', '--id [id]', 'Issue ID' ) do |i|
44
+ options[:issueid] = i
45
+ end
46
+
47
+ options[:regenconfig] = false
48
+ opts.on( nil, '--reset-config', 'Resets your YML config file and enter your data again.' ) do
49
+ options[:regenconfig] = true
50
+ end
51
+
52
+ options[:listpulls] = false
53
+ opts.on( '-l', '--pulls', 'Lists the open pull requests' ) do
54
+ options[:listpulls] = true
55
+ action = true;
56
+ end
57
+
58
+ options[:pulltitle] = false
59
+ opts.on( '-t', '--title [TITLE]', 'Pull request title. (Defaults to latest commit)' ) do |f|
60
+ options[:pulltitle] = f
61
+ end
62
+
63
+
64
+ options[:pullbody] = ''
65
+ opts.on( '-d', '--description [BODY]', 'Pull request description. (Defaults to an empty string)' ) do |b|
66
+ options[:pullbody] = b
67
+ end
68
+
69
+ options[:base] = "master"
70
+ opts.on( '-b', '--base [master]', 'Pull request base. (Defaults to master)' ) do |b|
71
+ options[:base] = b
72
+ end
73
+
74
+ options[:push] = false
75
+ opts.on( '-p', '--push', 'Push branch to remote before doing the pullrequest(To username/currentbranch)' ) do |p|
76
+ options[:push] = p
77
+ action = true;
78
+ end
79
+
80
+ options[:head] = false
81
+ opts.on( '-n', '--head [head]', 'Pull request head (Defaults to current branch)' ) do |h|
82
+ options[:head] = h
83
+ end
84
+
85
+ options[:verbose] = false
86
+ opts.on( '-v', '--verbose', 'Enables verbose mode' ) do
87
+ options[:verbose] = true
88
+ end
89
+
90
+ # This displays the help screen, all programs are
91
+ # assumed to have this option.
92
+ opts.on( '-h', '--help', 'Displays this screen' ) do
93
+ action = true;
94
+ puts opts
95
+ end
96
+
97
+ options[:help] = opts
98
+ end
99
+
100
+ begin
101
+ optparse.parse!
102
+
103
+ # Do we have a config file ? If we don't, lets make one !
104
+ f = GithubHelper::FirstTime.new() if !File.exists?('.git/githelper.config.yml') || options[:regenconfig]
105
+
106
+ raise ArgException, "No action specified" if !action
107
+ raise ArgException, "Your commit needs a message" if (options[:commit]) && !options[:commitmessage]
108
+
109
+ GH = GithubHelper::Github.new(options)
110
+
111
+ GH.displaypulls! if options[:listpulls]
112
+ GH.commit! if (options[:fastmode] || options[:commit]) && options[:commitmessage]
113
+ GH.pushBranch! if options[:push] || options[:fastmode]
114
+ GH.createPull! if options[:createpull] || options[:fastmode]
115
+
116
+ # Initialization exceptions management
117
+ rescue OptionParser::InvalidOption,ArgException => e
118
+ puts e.message
119
+ puts options[:help]
120
+ puts e.backtrace if options[:verbose]
121
+
122
+ # Run-time exceptions management
123
+ rescue Exception => e
124
+ puts e.message
125
+ puts e.backtrace if options[:verbose]
126
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'github_helper/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "github_helper"
8
+ gem.version = GithubHelper::VERSION
9
+ gem.authors = ["Tristan Foureur"]
10
+ gem.email = ["tristan.foureur@gmail.com"]
11
+ gem.description = %q{A small gem to commit, push to remote, and open a pull request, in a single line. It can also handle issue ids and add them to the Pullrequest's description.}
12
+ gem.summary = %q{A useful tool to commit, push to remote and open a pull request, in a single line.}
13
+ gem.homepage = "https://github.com/Esya/GithubHelper"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables << 'ghh'
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ gem.add_dependency "httparty"
20
+ gem.add_dependency "git"
21
+ gem.add_dependency "json"
22
+ end
@@ -0,0 +1,60 @@
1
+ # First time run ? Let's make the config file !
2
+ class GithubHelper::FirstTime
3
+ include HTTParty
4
+ base_uri 'https://api.github.com'
5
+
6
+ # Outputs the string as a question then waits for the user input
7
+ def ask(string,default=nil)
8
+ puts string
9
+ input = gets.strip
10
+ puts ""
11
+ input = (input == "") ? default : input
12
+ end
13
+
14
+ # This let us get the OAuth token
15
+ def getToken(user,password)
16
+ data = {:scopes => ["repo"], :note => "GithubHelper Gem"}
17
+ resp = self.class.post("/authorizations",:body => data.to_json)
18
+
19
+ if resp.headers['status'][0..2].to_i != 201
20
+ raise "Can't get the OAuth token : Wrong credentials, please try again"
21
+ end
22
+
23
+ resp.parsed_response["token"]
24
+ end
25
+
26
+ def initialize()
27
+ @config = Hash.new()
28
+ @config = {'credentials' => {'user' => '', 'token' => ''}, 'remote' => {'user' => '', 'repo' => ''}}
29
+
30
+ fillArray!
31
+ end
32
+
33
+ # Interactive building of the YAML file.
34
+ def fillArray!
35
+ puts "Setting your credentials"
36
+ user = ask("What's your github user name (Won't work with your email) ?")
37
+ password = ask("What's your git password ?\n(This won't be logged, used to get an OAuth token)")
38
+ self.class.basic_auth user, password
39
+
40
+ @config['credentials']['user'] = user
41
+ @config['credentials']['token'] = getToken(user,password)
42
+
43
+ puts "Now let's set up your repo you want to send your pull requests to"
44
+ @config['remote']['user'] = ask("What's the user you want to send your pull requests to ? ")
45
+ @config['remote']['repo'] = ask("What's the user's repo ?")
46
+
47
+ puts "Writing to config file : .git/githelper.config.yml"
48
+ header = "# GitHelper.rb config file\n# You can edit your credentials here\n"
49
+ yaml = @config.to_yaml
50
+
51
+ output = header + yaml
52
+ begin
53
+ File.open('.git/githelper.config.yml', 'w') {|f| f.write(output) }
54
+ rescue Exception
55
+ puts "Can't write to .git/githelper.config.yml, are you sure this a git directory?"
56
+ raise
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,27 @@
1
+ # This is a little hack to the Git gem to add a way to check the staged files
2
+ # And also to forcepush a branch.
3
+ module Git
4
+ class Status
5
+ public
6
+
7
+ # A file about to be commited has a type and a sha_index
8
+ def staged
9
+ @files.select { |k, f| f.sha_index != "0000000000000000000000000000000000000000" && f.type != nil }
10
+ end
11
+ end
12
+
13
+ #The git gem doesn't include a way to force push, let's implement it !
14
+ class Base
15
+ public
16
+ def forcepush(remote = 'origin', branch = 'master')
17
+ self.lib.forcepush(remote, branch)
18
+ end
19
+ end
20
+
21
+ class Lib
22
+ public
23
+ def forcepush(remote, branch)
24
+ command('push -f', [remote, branch])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module GithubHelper
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,161 @@
1
+ require "github_helper/version"
2
+ require "github_helper/git"
3
+ require "json"
4
+ require "HTTParty"
5
+ require "git"
6
+ require "pp"
7
+
8
+ module GithubHelper
9
+ class Github
10
+ include HTTParty
11
+
12
+ base_uri 'https://api.github.com'
13
+
14
+ @current_branch = ''
15
+
16
+ @verbose = false
17
+ @@target_user = ''
18
+ @@target_repo = ''
19
+
20
+ attr_accessor :options
21
+
22
+ # Display the open pull requests on the target repo
23
+ def displaypulls!
24
+ puts "Pull requests for #{@@target_user}/#{@@target_repo}\n"
25
+ pulls = get("/repos/#{@@target_user}/#{@@target_repo}/pulls")
26
+ pp pulls if @verbose
27
+ checkHeaders(pulls)
28
+ pulls.each { |pull| print "*\t"+pull['title'] + "\t" + pull['user']['login'] + "\n" }
29
+ end
30
+
31
+ def initialize(options)
32
+ @config = YAML::load( File.open( '.git/githelper.config.yml' ) )
33
+
34
+ # Credentials setting from YML
35
+ @@my_user = @config['credentials']['user']
36
+ @@my_token = @config['credentials']['token']
37
+
38
+ # Remote settings from YML
39
+ @@target_user = @config['remote']['user']
40
+ @@target_repo = @config['remote']['repo']
41
+
42
+ @options = options
43
+ @verbose = true if options[:verbose]
44
+ @currentBranch = ''
45
+ @Git = Git.open('.');
46
+ @@my_name = @Git.config['user.name']
47
+ end
48
+
49
+ def get(url)
50
+ self.class.get(url+"?access_token=#{@@my_token}")
51
+ end
52
+
53
+ def post(url,data)
54
+ self.class.post(url+"?access_token=#{@@my_token}",data)
55
+ end
56
+
57
+ # Creates a pull request
58
+ def createPull!
59
+ # Formats the HEAD to user:branch
60
+ head = @@my_user+':'+currentBranch?
61
+
62
+ # Do we have a title ? If not, takes it from the last commit
63
+ pullTitle = (@options[:pulltitle] == false) ? lastCommit? : @options[:pulltitle]
64
+
65
+ body = @options[:pullbody]
66
+ body = "#{body}\n##{options[:issueid]}\n" if (options[:issueid] != false)
67
+
68
+ data = {:title => pullTitle, :body => body, :base => @options[:base], :head => head}
69
+ pp data if @verbose
70
+ puts "Asking for #{data[:head]} to be merged into #{@@target_user}/#{data[:base]}"
71
+
72
+ # Let's send the request
73
+ resp = post("/repos/#{@@target_user}/#{@@target_repo}/pulls", :body => data.to_json)
74
+ pp resp if @verbose
75
+ checkHeaders(resp)
76
+
77
+ # So far so good. Lets check if it's mergeable and give the user the proper link
78
+ puts "Pull request succeeded. Getting more informations..\n"
79
+
80
+ infos_resp = get("/repos/#{@@target_user}/#{@@target_repo}/pulls/"+resp.parsed_response['number'].to_s)
81
+ pp infos_resp if @verbose
82
+ checkHeaders(infos_resp)
83
+
84
+ infos = infos_resp.parsed_response
85
+
86
+ puts 'Url : '+infos['_links']['html']['href']
87
+ if(infos['mergeable'])
88
+ puts 'Your pull request is mergeable.'
89
+ else
90
+ puts "/!\\ ..But your pull request cannot be merged. Try to rebase then push again. /!\\"
91
+ end
92
+ end
93
+
94
+ # Commits staged changes
95
+ def commit!
96
+ if(options[:commitmessage])
97
+ #Do we have files to commit ?
98
+ count = @Git.status.staged.count
99
+
100
+ raise Exception, "You have no staged files, aborting your commit" if (count == 0)
101
+
102
+ message = options[:commitmessage]
103
+
104
+ ci = @Git.commit(message)
105
+ pp ci
106
+ else
107
+ raise Exception, "Commit needs a message"
108
+ end
109
+ end
110
+
111
+ # Do we have a good HTTP status ?
112
+ def checkHeaders(resp)
113
+ code = resp.headers['status'][0..2];
114
+
115
+ #If the status isnt 200 (OK) or 201 (Created), raise an exception
116
+ good = case code.to_i
117
+ when 200..201 then true
118
+ when 401 then
119
+ raise Exception, "Bad credentials, maybe your OAuth token isn't valid anymore.\nReset your config with ghh --reset-config and try again"
120
+ else
121
+ error = formatError(resp)
122
+ raise Exception, "Error (HTTP #{code}) : "+error
123
+ end
124
+ good
125
+ end
126
+
127
+ # Format errors from the github api
128
+ def formatError(resp)
129
+ output = "";
130
+
131
+ output << resp.parsed_response['message'];
132
+ if(resp.parsed_response['errors'] != nil)
133
+ resp.parsed_response['errors'].each { |e|
134
+ output << "\n" + e['message'] if e['message'] != nil
135
+ }
136
+ end
137
+ output
138
+ end
139
+
140
+ # Push the branch to origin
141
+ def pushBranch!
142
+ @Git.forcepush('origin',currentBranch?)
143
+ puts "Pushed #{@currentBranch} to origin"
144
+ end
145
+
146
+ # Return your last commit's title
147
+ def lastCommit?
148
+ message = ''
149
+ @Git.log(1).author(@@my_name).each { |c| message = c.message }
150
+ message
151
+ end
152
+
153
+ # Return your current branch's name
154
+ def currentBranch?
155
+ if(@currentBranch == '')
156
+ @currentBranch = `git branch | grep '*' | cut -d " " -f2`.strip!
157
+ end
158
+ @currentBranch
159
+ end
160
+ end
161
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github_helper
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tristan Foureur
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: git
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: json
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A small gem to commit, push to remote, and open a pull request, in a
63
+ single line. It can also handle issue ids and add them to the Pullrequest's description.
64
+ email:
65
+ - tristan.foureur@gmail.com
66
+ executables:
67
+ - ghh
68
+ extensions: []
69
+ extra_rdoc_files: []
70
+ files:
71
+ - .gitignore
72
+ - Gemfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - Rakefile
76
+ - bin/ghh
77
+ - github_helper.gemspec
78
+ - lib/github_helper.rb
79
+ - lib/github_helper/first_time.rb
80
+ - lib/github_helper/git.rb
81
+ - lib/github_helper/version.rb
82
+ homepage: https://github.com/Esya/GithubHelper
83
+ licenses: []
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 1.8.24
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: A useful tool to commit, push to remote and open a pull request, in a single
106
+ line.
107
+ test_files: []