git-pr-release 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 98451853b6d661fc267e971bc26da386c77db073
4
+ data.tar.gz: 2437ff8212312fae03bbdfc28f2bae9e1b2806a7
5
+ SHA512:
6
+ metadata.gz: e03be692ee0bbb5a4ac1dd4cffd68fb625a39601cda7171d2c93770f4162b162e6cd5ae287877f4030ca2c4d65dafd442e8bd42803f0389c9f58178f5171aec5
7
+ data.tar.gz: d8831448a215b076fdd69782ccfdcbc5c22eeb979623f7cb0818754dbdc311fb07bb681eae75f0608af00dfdb89e592e8c0529746ecf3180dc5828966f8dcf3f
@@ -0,0 +1,2 @@
1
+ /.bundle/
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,28 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ git-pr-release (0.0.1)
5
+ colorize (~> 0.6)
6
+ highline (~> 1.6)
7
+ octokit (~> 2.7)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.3.5)
13
+ colorize (0.6.0)
14
+ faraday (0.9.0)
15
+ multipart-post (>= 1.2, < 3)
16
+ highline (1.6.20)
17
+ multipart-post (2.0.0)
18
+ octokit (2.7.0)
19
+ sawyer (~> 0.5.2)
20
+ sawyer (0.5.2)
21
+ addressable (~> 2.3.5)
22
+ faraday (~> 0.8, < 0.10)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ git-pr-release!
@@ -0,0 +1,35 @@
1
+ git-pr-release
2
+ ==============
3
+
4
+ Creates a pull request which summarizes feature branches that are to be
5
+ released into production. Useful if your branching storategy is like below:
6
+
7
+ * Feature branches are first merged into "staging" (or release, development)
8
+ branch.
9
+ * Then the staging branch is merged into "production" branch, which is for
10
+ production release.
11
+
12
+ Configuration
13
+ -------------
14
+
15
+ All configuration are taken using `git config`.
16
+
17
+ ### `pr-release.token`
18
+
19
+ Token for GitHub API.
20
+
21
+ If not set, you will be asked to input username/password for one time only,
22
+ and this configuration variable will be stored.
23
+
24
+ ### `pr-release.branch.production`
25
+
26
+ The branch name that is deployed in production environment.
27
+
28
+ Default value: `master`.
29
+
30
+ ### `pr-release.branch.staging`
31
+
32
+ The branch name that the feature branches are merged into and is going to be
33
+ merged into the "production" branch.
34
+
35
+ Default value: `staging`.
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'uri'
5
+ require 'open3'
6
+ require 'octokit'
7
+ require 'colorize'
8
+
9
+ def say(message, level)
10
+ color = case level
11
+ when :trace
12
+ return unless ENV['DEBUG']
13
+ nil
14
+ when :debug
15
+ return unless ENV['DEBUG']
16
+ :blue
17
+ when :info
18
+ :green
19
+ when :notice
20
+ :yellow
21
+ when :warn
22
+ :magenta
23
+ when :error
24
+ :red
25
+ end
26
+
27
+ puts message.colorize(color)
28
+ end
29
+
30
+ def git(*command)
31
+ command = [ 'git', *command.map(&:to_s) ]
32
+ say "Executing `#{command.join(' ')}`", :trace
33
+ out, status = Open3.capture2(*command)
34
+ unless status.success?
35
+ raise "Executing `#{command.join(' ')}` failed: #{status}"
36
+ end
37
+ out.each_line
38
+ end
39
+
40
+
41
+ def git_config(key)
42
+ begin
43
+ git('config', key).first.chomp
44
+ rescue
45
+ nil
46
+ end
47
+ end
48
+
49
+ def generate_description(pull_requests)
50
+ pull_requests.map do |pr|
51
+ "- [ ] ##{pr.number} #{pr.title}" + (pr.assignee ? " @#{pr.assignee.login}" : '')
52
+ end.join("\n")
53
+ end
54
+
55
+ def obtain_token!
56
+ token = git_config('pr-release.token')
57
+
58
+ unless token
59
+ require 'highline/import'
60
+ STDERR.puts 'Could not obtain config variable pr-release.token.'
61
+ STDERR.puts 'Trying to generate token...'
62
+
63
+ username = ask('username? ') { |q| q.default = ENV['USER'] }
64
+ password = ask('password? (not saved) ') { |q| q.echo = '*' }
65
+
66
+ temporary_client = Octokit::Client.new :login => username, :password => password
67
+
68
+ auth = temporary_client.create_authorization({ :scopes => [ 'public_repo', 'repo' ], :note => 'summerize-release' })
69
+ token = auth.token
70
+ git :config, '--global', 'pr-release.token', token
71
+ end
72
+
73
+ token
74
+ end
75
+
76
+ remote = git_config 'remote.origin.url'
77
+ unless %r(^\w+://) === remote
78
+ remote = "ssh://#{remote.sub(':', '/')}"
79
+ end
80
+
81
+ remote_url = URI.parse(remote)
82
+ repository = remote_url.path.sub(%r(^/), '').sub(/\.git$/, '')
83
+
84
+ if remote_url.host != 'github.com'
85
+ # GitHub:Enterprise
86
+ OpenSSL::SSL.const_set :VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE # XXX
87
+
88
+ Octokit.configure do |c|
89
+ c.api_endpoint = "https://#{remote_url.host}/api/v3"
90
+ c.web_endpoint = "https://#{remote_url.host}/"
91
+ end
92
+ end
93
+
94
+ ## Project specific configuration
95
+ production_branch = git_config('pr-release.branch.production') || 'master'
96
+ staging_branch = git_config('pr-release.branch.staging') || 'staging'
97
+
98
+ say "Repository: #{repository}", :debug
99
+ say "Production branch: #{production_branch}", :debug
100
+ say "Staging branch: #{staging_branch}", :debug
101
+
102
+ client = Octokit::Client.new :access_token => obtain_token!
103
+
104
+ git :remote, 'update', 'origin'
105
+
106
+ merged_feature_head_sha1s = git(
107
+ :log, '--merges', '--pretty=format:%P', "origin/#{production_branch}..origin/#{staging_branch}"
108
+ ).map do |line|
109
+ main_sha1, feature_sha1 = line.chomp.split /\s+/
110
+ feature_sha1
111
+ end
112
+
113
+ merged_pull_request_numbers = git('ls-remote', 'origin', 'refs/pull/*/head').map do |line|
114
+ sha1, ref = line.chomp.split /\s+/
115
+
116
+ if merged_feature_head_sha1s.include? sha1
117
+ if %r<^refs/pull/(\d+)/head$>.match ref
118
+ pr_number = $1.to_i
119
+
120
+ if git('merge-base', sha1, "origin/#{production_branch}").first.chomp == sha1
121
+ say "##{pr_number} (#{sha1}) is already merged into #{production_branch}", :debug
122
+ else
123
+ pr_number
124
+ end
125
+ else
126
+ say "Bad pull request head ref format: #{ref}", :warn
127
+ nil
128
+ end
129
+ end
130
+ end.compact
131
+
132
+ if merged_pull_request_numbers.empty?
133
+ say 'No pull requests to be released', :error
134
+ exit 1
135
+ end
136
+
137
+ pull_requests = merged_pull_request_numbers.map do |nr|
138
+ pr = client.pull_request repository, nr
139
+ say "To be released: ##{pr.number} #{pr.title}", :notice
140
+ pr
141
+ end
142
+
143
+ say 'Searching for existing release pull requests...', :info
144
+ release_pr = client.pull_requests(repository).find do |pr|
145
+ pr.head.ref == staging_branch && pr.base.ref == production_branch
146
+ end
147
+
148
+ created = false
149
+
150
+ if release_pr
151
+ say "Updating release pull request ##{release_pr.number}...", :info
152
+ client.update_pull_request(
153
+ repository, release_pr.number, :body => generate_description(pull_requests)
154
+ )
155
+ else
156
+ say 'Creating release pull request...', :info
157
+ release_pr = client.create_pull_request(
158
+ repository, production_branch, staging_branch, "release #{Time.now}", generate_description(pull_requests)
159
+ )
160
+ created = true
161
+ end
162
+
163
+ unless release_pr
164
+ say 'No pull request found/created', :error
165
+ exit 1
166
+ end
167
+
168
+ say "#{created ? 'Created' : 'Updated'} pull request: #{release_pr.rels[:html].href}", :notice
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "git-pr-release"
7
+ spec.version = '0.0.1'
8
+ spec.authors = ["motemen"]
9
+ spec.email = ["motemen@gmail.com"]
10
+ spec.summary = 'Creates a release pull request'
11
+ spec.description = 'git-pr-release creates a pull request which summarizes feature branches that are to be released into production'
12
+ spec.homepage = 'https://github.com/motemen/git-pr-release'
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency 'octokit', '~> 2.7'
20
+ spec.add_dependency 'highline', '~> 1.6'
21
+ spec.add_dependency 'colorize', '~> 0.6'
22
+
23
+ spec.license = 'MIT'
24
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-pr-release
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - motemen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: octokit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.6'
55
+ description: git-pr-release creates a pull request which summarizes feature branches
56
+ that are to be released into production
57
+ email:
58
+ - motemen@gmail.com
59
+ executables:
60
+ - git-pr-release
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - .gitignore
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - README.md
68
+ - bin/git-pr-release
69
+ - git-pr-release.gemspec
70
+ homepage: https://github.com/motemen/git-pr-release
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.0.3
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Creates a release pull request
94
+ test_files: []
95
+ has_rdoc: