gitx 2.13.1

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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/CONTRIBUTING.md +67 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +8 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +87 -0
  11. data/Rakefile +5 -0
  12. data/bin/git-buildtag +6 -0
  13. data/bin/git-cleanup +7 -0
  14. data/bin/git-integrate +6 -0
  15. data/bin/git-nuke +6 -0
  16. data/bin/git-release +6 -0
  17. data/bin/git-review +6 -0
  18. data/bin/git-share +6 -0
  19. data/bin/git-start +6 -0
  20. data/bin/git-track +6 -0
  21. data/bin/git-update +6 -0
  22. data/gitx.gemspec +37 -0
  23. data/lib/gitx.rb +8 -0
  24. data/lib/gitx/cli/base_command.rb +56 -0
  25. data/lib/gitx/cli/buildtag_command.rb +39 -0
  26. data/lib/gitx/cli/cleanup_command.rb +45 -0
  27. data/lib/gitx/cli/integrate_command.rb +94 -0
  28. data/lib/gitx/cli/nuke_command.rb +62 -0
  29. data/lib/gitx/cli/release_command.rb +40 -0
  30. data/lib/gitx/cli/review_command.rb +93 -0
  31. data/lib/gitx/cli/share_command.rb +15 -0
  32. data/lib/gitx/cli/start_command.rb +37 -0
  33. data/lib/gitx/cli/track_command.rb +14 -0
  34. data/lib/gitx/cli/update_command.rb +35 -0
  35. data/lib/gitx/configuration.rb +44 -0
  36. data/lib/gitx/extensions/string.rb +12 -0
  37. data/lib/gitx/extensions/thor.rb +39 -0
  38. data/lib/gitx/github.rb +178 -0
  39. data/lib/gitx/version.rb +3 -0
  40. data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_failure_status.yml +135 -0
  41. data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_success_status.yml +149 -0
  42. data/spec/fixtures/vcr_cassettes/pull_request_does_not_exist.yml +133 -0
  43. data/spec/gitx/cli/base_command_spec.rb +41 -0
  44. data/spec/gitx/cli/buildtag_command_spec.rb +70 -0
  45. data/spec/gitx/cli/cleanup_command_spec.rb +37 -0
  46. data/spec/gitx/cli/integrate_command_spec.rb +290 -0
  47. data/spec/gitx/cli/nuke_command_spec.rb +165 -0
  48. data/spec/gitx/cli/release_command_spec.rb +172 -0
  49. data/spec/gitx/cli/review_command_spec.rb +356 -0
  50. data/spec/gitx/cli/share_command_spec.rb +32 -0
  51. data/spec/gitx/cli/start_command_spec.rb +96 -0
  52. data/spec/gitx/cli/track_command_spec.rb +31 -0
  53. data/spec/gitx/cli/update_command_spec.rb +79 -0
  54. data/spec/spec_helper.rb +86 -0
  55. data/spec/support/global_config.rb +26 -0
  56. data/spec/support/home_env.rb +11 -0
  57. data/spec/support/matchers/meet_expectations_matcher.rb +7 -0
  58. data/spec/support/pry.rb +1 -0
  59. data/spec/support/stub_execution.rb +14 -0
  60. data/spec/support/timecop.rb +9 -0
  61. data/spec/support/vcr.rb +6 -0
  62. data/spec/support/webmock.rb +1 -0
  63. metadata +348 -0
@@ -0,0 +1,12 @@
1
+ class String
2
+ # @see http://api.rubyonrails.org/classes/String.html#method-i-strip_heredoc
3
+ def undent
4
+ indent = scan(/^[ \t]*(?=\S)/).min.size || 0
5
+ gsub(/^[ \t]{#{indent}}/, '')
6
+ end
7
+ alias :dedent :undent
8
+
9
+ def blank?
10
+ self.to_s == ''
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ require 'English'
2
+
3
+ class Thor
4
+ module Actions
5
+ # execute a shell command and raise an error if non-zero exit code is returned
6
+ # return the string output from the command
7
+ def run_cmd(cmd, options = {})
8
+ say "$ #{cmd}"
9
+ output = `#{cmd}`
10
+ success = $CHILD_STATUS.to_i == 0
11
+ fail "#{cmd} failed" unless success || options[:allow_failure]
12
+ output
13
+ end
14
+
15
+ # launch configured editor to retreive message/string
16
+ # see http://osdir.com/ml/ruby-talk/2010-06/msg01424.html
17
+ # see https://gist.github.com/rkumar/456809
18
+ # see http://rdoc.info/github/visionmedia/commander/master/Commander/UI.ask_editor
19
+ def ask_editor(initial_text = '', editor = nil)
20
+ editor ||= ENV['EDITOR'] || 'vi'
21
+ Tempfile.open('comment.md') do |f|
22
+ f << initial_text
23
+ f.flush
24
+
25
+ flags = case editor
26
+ when 'mate', 'emacs', 'subl'
27
+ '-w'
28
+ when 'mvim'
29
+ '-f'
30
+ else
31
+ ''
32
+ end
33
+ pid = fork { exec([editor, flags, f.path].join(' ')) }
34
+ Process.waitpid(pid)
35
+ File.read(f.path)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,178 @@
1
+ require 'octokit'
2
+ require 'fileutils'
3
+ require 'yaml'
4
+ require 'gitx/cli/update_command'
5
+
6
+ module Gitx
7
+ module Github
8
+ GLOBAL_CONFIG_FILE = '~/.config/gitx/github.yml'
9
+ REVIEW_CONTEXT = 'peer_review'
10
+ CLIENT_URL = 'https://github.com/wireframe/gitx'
11
+ PULL_REQUEST_FOOTER = <<-EOS.dedent
12
+ # Pull Request Protips(tm):
13
+ # * Include description of how this change accomplishes the task at hand.
14
+ # * Use GitHub flavored Markdown http://github.github.com/github-flavored-markdown/
15
+ # * Review CONTRIBUTING.md for recommendations of artifacts, links, images, screencasts, etc.
16
+ #
17
+ # This footer will automatically be stripped from the pull request description
18
+ EOS
19
+
20
+ def find_or_create_pull_request(branch)
21
+ pull_request = find_pull_request(branch)
22
+ pull_request ||= begin
23
+ checkout_branch(branch)
24
+ execute_command(Gitx::Cli::UpdateCommand, :update)
25
+ pull_request = create_pull_request(branch)
26
+ say 'Created pull request: '
27
+ say pull_request.html_url, :green
28
+
29
+ pull_request
30
+ end
31
+ pull_request
32
+ end
33
+
34
+ # @return [Sawyer::Resource] data structure of pull request info if found
35
+ # @return nil if no pull request found
36
+ def find_pull_request(branch)
37
+ head_reference = "#{github_organization}:#{branch}"
38
+ params = {
39
+ head: head_reference,
40
+ state: 'open'
41
+ }
42
+ pull_requests = github_client.pull_requests(github_slug, params)
43
+ pull_requests.first
44
+ end
45
+
46
+ # Get the current commit status of a branch
47
+ # @see https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
48
+ def branch_status(branch)
49
+ response = github_client.status(github_slug, branch)
50
+ response.state
51
+ end
52
+
53
+ # Update build status with peer review status
54
+ def update_review_status(pull_request, state, description)
55
+ commit_sha = pull_request.head.sha
56
+ github_client.create_status(github_slug, commit_sha, state, context: REVIEW_CONTEXT, description: description)
57
+ end
58
+
59
+ # @see http://developer.github.com/v3/pulls/
60
+ def create_pull_request(branch)
61
+ say 'Creating pull request for '
62
+ say "#{branch} ", :green
63
+ say 'against '
64
+ say "#{Gitx::BASE_BRANCH} ", :green
65
+ say 'in '
66
+ say github_slug, :green
67
+
68
+ title = branch
69
+ body = pull_request_body(branch)
70
+ github_client.create_pull_request(github_slug, Gitx::BASE_BRANCH, branch, title, body)
71
+ end
72
+
73
+ def pull_request_body(branch)
74
+ changelog = run_cmd("git log #{Gitx::BASE_BRANCH}...#{branch} --reverse --no-merges --pretty=format:'* %s%n%b'")
75
+ description = options[:description]
76
+
77
+ description_template = []
78
+ description_template << "#{description}\n" if description
79
+ description_template << '### Changelog'
80
+ description_template << changelog
81
+ description_template << PULL_REQUEST_FOOTER
82
+
83
+ body = ask_editor(description_template.join("\n"), repo.config['core.editor'])
84
+ body.gsub(PULL_REQUEST_FOOTER, '').chomp.strip
85
+ end
86
+
87
+ # authorization token used for github API calls
88
+ # the token is cached on the filesystem for future use
89
+ # @return [String] auth token stored in git (current repo, user config or installed global settings)
90
+ # @see http://developer.github.com/v3/oauth/#scopes
91
+ # @see http://developer.github.com/v3/#user-agent-required
92
+ def authorization_token
93
+ auth_token = ENV['GITX_GITHUB_TOKEN'] || global_config['token']
94
+ auth_token ||= begin
95
+ new_token = create_authorization
96
+ save_global_config('token' => new_token)
97
+ new_token
98
+ end
99
+ auth_token
100
+ end
101
+
102
+ def create_authorization
103
+ password = ask_without_echo("Github password for #{username}: ")
104
+ client = Octokit::Client.new(login: username, password: password)
105
+ options = {
106
+ :scopes => ['repo'],
107
+ :note => github_client_name,
108
+ :note_url => CLIENT_URL
109
+ }
110
+ two_factor_auth_token = ask_without_echo('Github two factor authorization token (if enabled): ')
111
+ options[:headers] = {'X-GitHub-OTP' => two_factor_auth_token} if two_factor_auth_token
112
+ response = client.create_authorization(options)
113
+ response.token
114
+ rescue Octokit::ClientError => e
115
+ say "Error creating authorization: #{e.message}", :red
116
+ retry
117
+ end
118
+
119
+ def github_client_name
120
+ timestamp = Time.now.utc.strftime('%FT%R:%S%z')
121
+ client_name = "The Garage Git eXtensions #{timestamp}"
122
+ end
123
+
124
+ def github_client
125
+ @client ||= Octokit::Client.new(:access_token => authorization_token)
126
+ end
127
+
128
+ # @return [String] github username (ex: 'wireframe') of the current github.user
129
+ # @raise error if github.user is not configured
130
+ def username
131
+ username = repo.config['github.user']
132
+ fail "Github user not configured. Run: `git config --global github.user 'me@email.com'`" unless username
133
+ username
134
+ end
135
+
136
+ # @return the github slug for the current repository's remote origin url.
137
+ # @example
138
+ # git@github.com:socialcast/wireframe/gitx.git #=> wireframe/gitx
139
+ # @example
140
+ # https://github.com/wireframe/gitx.git #=> wireframe/gitx
141
+ def github_slug
142
+ remote = repo.config['remote.origin.url']
143
+ remote.to_s.gsub(/\.git$/,'').split(/[:\/]/).last(2).join('/')
144
+ end
145
+
146
+ def github_organization
147
+ github_slug.split('/').first
148
+ end
149
+
150
+ def global_config_file
151
+ File.expand_path(GLOBAL_CONFIG_FILE)
152
+ end
153
+
154
+ def global_config
155
+ @config ||= begin
156
+ File.exists?(global_config_file) ? YAML.load_file(global_config_file) : {}
157
+ end
158
+ end
159
+
160
+ def save_global_config(options)
161
+ config_dir = File.dirname(global_config_file)
162
+ ::FileUtils.mkdir_p(config_dir, mode: 0700) unless File.exists?(config_dir)
163
+
164
+ @config = global_config.merge(options)
165
+ File.open(global_config_file, 'a+') do |file|
166
+ file.truncate(0)
167
+ file.write(@config.to_yaml)
168
+ end
169
+ File.chmod(0600, global_config_file)
170
+ end
171
+
172
+ def ask_without_echo(message)
173
+ value = ask(message, echo: false)
174
+ say ''
175
+ value
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,3 @@
1
+ module Gitx
2
+ VERSION = '2.13.1'
3
+ end
@@ -0,0 +1,135 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.github.com/repos/wireframe/gitx/pulls?head=wireframe:feature-branch&state=open
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/vnd.github.v3+json
12
+ User-Agent:
13
+ - Octokit Ruby Gem 3.4.0
14
+ Content-Type:
15
+ - application/json
16
+ Authorization:
17
+ - token 123123
18
+ Accept-Encoding:
19
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Server:
26
+ - GitHub.com
27
+ Date:
28
+ - Wed, 10 Dec 2014 19:14:11 GMT
29
+ Content-Type:
30
+ - application/json; charset=utf-8
31
+ Status:
32
+ - 200 OK
33
+ X-Github-Media-Type:
34
+ - github.v3; format=json
35
+ X-Ratelimit-Limit:
36
+ - '60'
37
+ X-Ratelimit-Remaining:
38
+ - '58'
39
+ X-Ratelimit-Reset:
40
+ - '1418242282'
41
+ X-Xss-Protection:
42
+ - 1; mode=block
43
+ X-Frame-Options:
44
+ - deny
45
+ Content-Security-Policy:
46
+ - default-src 'none'
47
+ Content-Length:
48
+ - '83'
49
+ Access-Control-Allow-Credentials:
50
+ - 'true'
51
+ Access-Control-Expose-Headers:
52
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
53
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
54
+ Access-Control-Allow-Origin:
55
+ - "*"
56
+ X-Github-Request-Id:
57
+ - C6CBAFAF:141F:450BDAC:54889B83
58
+ Strict-Transport-Security:
59
+ - max-age=31536000; includeSubdomains; preload
60
+ X-Content-Type-Options:
61
+ - nosniff
62
+ body:
63
+ encoding: UTF-8
64
+ string: '[{"html_url":"https://path/to/html/pull/request","issue_url":"https://api/path/to/issue/url","number":10,"head":{"ref":"branch_name"}}]'
65
+ http_version:
66
+ recorded_at: Wed, 10 Dec 2014 19:14:11 GMT
67
+ - request:
68
+ method: get
69
+ uri: https://api.github.com/repos/wireframe/gitx/commits/feature-branch/status
70
+ body:
71
+ encoding: US-ASCII
72
+ string: ''
73
+ headers:
74
+ Accept:
75
+ - application/vnd.github.v3+json
76
+ User-Agent:
77
+ - Octokit Ruby Gem 3.4.0
78
+ Content-Type:
79
+ - application/json
80
+ Authorization:
81
+ - token 123123
82
+ Accept-Encoding:
83
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
84
+ response:
85
+ status:
86
+ code: 200
87
+ message: OK
88
+ headers:
89
+ Server:
90
+ - GitHub.com
91
+ Date:
92
+ - Wed, 10 Dec 2014 19:01:27 GMT
93
+ Content-Type:
94
+ - application/json; charset=utf-8
95
+ Transfer-Encoding:
96
+ - chunked
97
+ Status:
98
+ - 200 OK
99
+ X-Ratelimit-Limit:
100
+ - '5000'
101
+ X-Ratelimit-Remaining:
102
+ - '4993'
103
+ X-Ratelimit-Reset:
104
+ - '1418241620'
105
+ X-Oauth-Scopes:
106
+ - repo
107
+ X-Accepted-Oauth-Scopes:
108
+ - repo, repo:status
109
+ X-Github-Media-Type:
110
+ - github.v3; format=json
111
+ X-Xss-Protection:
112
+ - 1; mode=block
113
+ X-Frame-Options:
114
+ - deny
115
+ Content-Security-Policy:
116
+ - default-src 'none'
117
+ Access-Control-Allow-Credentials:
118
+ - 'true'
119
+ Access-Control-Expose-Headers:
120
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
121
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
122
+ Access-Control-Allow-Origin:
123
+ - "*"
124
+ X-Github-Request-Id:
125
+ - C6CBAFAF:141D:2F27072:54889887
126
+ Strict-Transport-Security:
127
+ - max-age=31536000; includeSubdomains; preload
128
+ X-Content-Type-Options:
129
+ - nosniff
130
+ body:
131
+ encoding: UTF-8
132
+ string: '{"state":"failure"}'
133
+ http_version:
134
+ recorded_at: Wed, 10 Dec 2014 19:14:11 GMT
135
+ recorded_with: VCR 2.9.2
@@ -0,0 +1,149 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.github.com/repos/wireframe/gitx/pulls?head=wireframe:feature-branch&state=open
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/vnd.github.v3+json
12
+ User-Agent:
13
+ - Octokit Ruby Gem 3.2.0
14
+ Authorization:
15
+ - token 123123
16
+ Accept-Encoding:
17
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Server:
24
+ - GitHub.com
25
+ Date:
26
+ - Tue, 05 Aug 2014 16:36:03 GMT
27
+ Content-Type:
28
+ - application/json; charset=utf-8
29
+ Status:
30
+ - 200 OK
31
+ X-Ratelimit-Limit:
32
+ - '5000'
33
+ X-Ratelimit-Remaining:
34
+ - '4991'
35
+ X-Ratelimit-Reset:
36
+ - '1407257585'
37
+ Cache-Control:
38
+ - private, max-age=60, s-maxage=60
39
+ Etag:
40
+ - '"6d00d48abf2adf1877c8244700cd4c6f"'
41
+ X-Oauth-Scopes:
42
+ - repo
43
+ X-Accepted-Oauth-Scopes:
44
+ - ''
45
+ Vary:
46
+ - Accept, Authorization, Cookie, X-GitHub-OTP
47
+ - Accept-Encoding
48
+ X-Github-Media-Type:
49
+ - github.v3; format=json
50
+ Link:
51
+ - <https://api.github.com/repositories/17608725/pulls?head=wireframe%3Afeature-branch&state=open&page=0>;
52
+ rel="last"
53
+ X-Xss-Protection:
54
+ - 1; mode=block
55
+ X-Frame-Options:
56
+ - deny
57
+ Content-Security-Policy:
58
+ - default-src 'none'
59
+ Content-Length:
60
+ - '2'
61
+ Access-Control-Allow-Credentials:
62
+ - 'true'
63
+ Access-Control-Expose-Headers:
64
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
65
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
66
+ Access-Control-Allow-Origin:
67
+ - "*"
68
+ X-Github-Request-Id:
69
+ - 46C5E25C:1E4F:1642AD9:53E107F3
70
+ Strict-Transport-Security:
71
+ - max-age=31536000; includeSubdomains
72
+ X-Content-Type-Options:
73
+ - nosniff
74
+ X-Served-By:
75
+ - d818ddef80f4c7d10683dd483558952a
76
+ body:
77
+ encoding: UTF-8
78
+ string: '[{"html_url":"https://path/to/html/pull/request","issue_url":"https://api/path/to/issue/url","number":10,"head":{"ref":"branch_name", "sha": "e12da4"}}]'
79
+ http_version:
80
+ recorded_at: Tue, 05 Aug 2014 16:36:03 GMT
81
+ - request:
82
+ method: get
83
+ uri: https://api.github.com/repos/wireframe/gitx/commits/feature-branch/status
84
+ body:
85
+ encoding: US-ASCII
86
+ string: ''
87
+ headers:
88
+ Accept:
89
+ - application/vnd.github.v3+json
90
+ User-Agent:
91
+ - Octokit Ruby Gem 3.4.0
92
+ Content-Type:
93
+ - application/json
94
+ Authorization:
95
+ - token 123123
96
+ Accept-Encoding:
97
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
98
+ response:
99
+ status:
100
+ code: 200
101
+ message: OK
102
+ headers:
103
+ Server:
104
+ - GitHub.com
105
+ Date:
106
+ - Wed, 10 Dec 2014 19:01:27 GMT
107
+ Content-Type:
108
+ - application/json; charset=utf-8
109
+ Transfer-Encoding:
110
+ - chunked
111
+ Status:
112
+ - 200 OK
113
+ X-Ratelimit-Limit:
114
+ - '5000'
115
+ X-Ratelimit-Remaining:
116
+ - '4993'
117
+ X-Ratelimit-Reset:
118
+ - '1418241620'
119
+ X-Oauth-Scopes:
120
+ - repo
121
+ X-Accepted-Oauth-Scopes:
122
+ - repo, repo:status
123
+ X-Github-Media-Type:
124
+ - github.v3; format=json
125
+ X-Xss-Protection:
126
+ - 1; mode=block
127
+ X-Frame-Options:
128
+ - deny
129
+ Content-Security-Policy:
130
+ - default-src 'none'
131
+ Access-Control-Allow-Credentials:
132
+ - 'true'
133
+ Access-Control-Expose-Headers:
134
+ - ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
135
+ X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
136
+ Access-Control-Allow-Origin:
137
+ - "*"
138
+ X-Github-Request-Id:
139
+ - C6CBAFAF:141D:2F27072:54889887
140
+ Strict-Transport-Security:
141
+ - max-age=31536000; includeSubdomains; preload
142
+ X-Content-Type-Options:
143
+ - nosniff
144
+ body:
145
+ encoding: UTF-8
146
+ string: '{"state":"success"}'
147
+ http_version:
148
+ recorded_at: Wed, 10 Dec 2014 19:14:11 GMT
149
+ recorded_with: VCR 2.9.2