git-maintain 0.11.0 → 0.13.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/lib/repo.rb DELETED
@@ -1,447 +0,0 @@
1
- require 'octokit'
2
- require 'io/console'
3
-
4
- module GitMaintain
5
- class Repo
6
- @@VALID_REPO = "github"
7
- @@STABLE_REPO = "stable"
8
- @@SUBMIT_BINARY="git-release"
9
-
10
- ACTION_LIST = [
11
- :list_branches,
12
- :summary,
13
- # Internal commands for completion
14
- :list_suffixes, :submit_release
15
- ]
16
- ACTION_HELP = {
17
- :submit_release => "Push the tags to 'stable' remote and create the release packages",
18
- :summary => "Displays a summary of the configuration and the branches git-maintain sees"
19
- }
20
-
21
- def self.load(path=".")
22
- dir = Dir.pwd()
23
- repo_name = File.basename(dir)
24
- return GitMaintain::loadClass(Repo, repo_name, dir)
25
- end
26
-
27
- def self.check_opts(opts)
28
- if opts[:action] == :submit_release then
29
- if opts[:br_suff] != "master" then
30
- raise "Action #{opts[:action]} can only be done on 'master' suffixed branches"
31
- end
32
- end
33
- end
34
-
35
- def self.execAction(opts, action)
36
- repo = Repo::load()
37
-
38
- if action == :submit_release then
39
- repo.stableUpdate()
40
- end
41
- repo.send(action, opts)
42
- end
43
-
44
- def initialize(path=nil)
45
- GitMaintain::checkDirectConstructor(self.class)
46
-
47
- @path = path
48
- @branch_list=nil
49
- @stable_branches=nil
50
- @suffix_list=nil
51
- @config_cache={}
52
-
53
- if path == nil
54
- @path = Dir.pwd()
55
- end
56
- @name = File.basename(@path)
57
-
58
- @valid_repo = getGitConfig("maintain.valid-repo")
59
- @valid_repo = @@VALID_REPO if @valid_repo == ""
60
- @stable_repo = getGitConfig("maintain.stable-repo")
61
- @stable_repo = @@STABLE_REPO if @stable_repo == ""
62
-
63
- @remote_valid=runGit("remote -v | egrep '^#{@valid_repo}' | grep fetch |
64
- awk '{ print $2}' | sed -e 's/.*://' -e 's/\\.git//'")
65
- @remote_stable=runGit("remote -v | egrep '^#{@stable_repo}' | grep fetch |
66
- awk '{ print $2}' | sed -e 's/.*://' -e 's/\\.git//'")
67
-
68
- @auto_fetch = getGitConfig("maintain.autofetch")
69
- case @auto_fetch
70
- when ""
71
- @auto_fetch = nil
72
- when "true", "yes", "on"
73
- @auto_fetch = true
74
- when "false", "no", "off"
75
- @auto_fetch = false
76
- else
77
- raise("Invalid value '#{@auto_fetch}' in git config for maintain.autofetch")
78
- end
79
-
80
- @branch_format_raw = getGitConfig("maintain.branch-format")
81
- @branch_format = Regexp.new(/#{@branch_format_raw}/)
82
- @stable_branch_format = getGitConfig("maintain.stable-branch-format")
83
- @stable_base_format = getGitConfig("maintain.stable-base-format")
84
-
85
- @stable_base_patterns=
86
- runGit("config --get-regexp stable-base | egrep '^stable-base\.' | "+
87
- "sed -e 's/stable-base\.//' -e 's/---/\\//g'").split("\n").inject({}){ |m, x|
88
- y=x.split(" ");
89
- m[y[0]] = y[1]
90
- m
91
- }
92
-
93
- @mail_format = getGitConfig("maintain.mail-format")
94
- if @mail_format == "" then
95
- @mail_format = :imap_send
96
- else
97
- # Check that the format is valid
98
- case @mail_format
99
- when "imap_send", "send_email"
100
- else
101
- raise("Invalid mail-format #{@mail_format}")
102
- end
103
-
104
- @mail_format = @mail_format.to_sym()
105
- end
106
- end
107
- attr_reader :path, :name, :remote_valid, :remote_stable, :valid_repo, :stable_repo
108
-
109
- def log(lvl, str)
110
- GitMaintain::log(lvl, str)
111
- end
112
-
113
- def run(cmd)
114
- return `cd #{@path} && #{cmd}`
115
- end
116
- def runSystem(cmd)
117
- return system("cd #{@path} && #{cmd}")
118
- end
119
-
120
- def runGit(cmd)
121
- log(:DEBUG, "Called from #{caller[1]}")
122
- log(:DEBUG, "Running git command '#{cmd}'")
123
- return `git --work-tree=#{@path} #{cmd}`.chomp()
124
- end
125
- def runGitInteractive(cmd)
126
- log(:DEBUG, "Called from #{caller[1]}")
127
- log(:DEBUG, "Running interactive git command '#{cmd}'")
128
- return system("git --work-tree=#{@path} #{cmd}")
129
- end
130
-
131
- def runGitImap(cmd)
132
- return `export GIT_ASKPASS=$(dirname $(dirname $(which git)))/lib/git-core/git-gui--askpass;
133
- if [ ! -f $GIT_ASKPASS ]; then
134
- export GIT_ASKPASS=$(dirname $(which git))/git-gui--askpass;
135
- fi;
136
- if [ ! -f $GIT_ASKPASS ]; then
137
- export GIT_ASKPASS=/usr/lib/ssh/ssh-askpass;
138
- fi; git --work-tree=#{@path} imap-send #{cmd}`
139
- end
140
- def getGitConfig(entry)
141
- return @config_cache[entry] ||= runGit("config #{entry} 2> /dev/null").chomp()
142
- end
143
-
144
- def runBash(env="")
145
- runSystem(env + " bash")
146
- if $? == 0 then
147
- log(:INFO, "Continuing...")
148
- else
149
- log(:ERROR, "Shell exited with code #{$?}. Exiting")
150
- raise("Cancelled by user")
151
- end
152
- end
153
-
154
- def getCommitHeadline(sha)
155
- return runGit("show --format=oneline --no-patch --no-decorate #{sha}")
156
- end
157
- def getCommitSubj(sha)
158
- return runGit("log -1 --pretty=\"%s\" #{sha}")
159
- end
160
-
161
- def stableUpdate(fetch=nil)
162
- fetch = @auto_fetch if fetch == nil
163
- return if fetch == false
164
- log(:VERBOSE, "Fetching stable updates...")
165
- runGit("fetch #{@stable_repo}")
166
- end
167
- def getBranchList(br_suff)
168
- return @branch_list if @branch_list != nil
169
-
170
- @branch_list=runGit("branch").split("\n").map(){|x|
171
- x=~ /#{@branch_format_raw}\/#{br_suff}$/ ?
172
- $1 : nil
173
- }.compact().uniq()
174
-
175
- return @branch_list
176
- end
177
-
178
- def getStableBranchList()
179
- return @stable_branches if @stable_branches != nil
180
-
181
- @stable_branches=runGit("branch -a").split("\n").map(){|x|
182
- x=~ /remotes\/#{@@STABLE_REPO}\/#{@stable_branch_format.gsub(/\\1/, '([0-9]+)')}$/ ?
183
- $1 : nil
184
- }.compact().uniq()
185
-
186
- return @stable_branches
187
- end
188
-
189
- def getSuffixList()
190
- return @suffix_list if @suffix_list != nil
191
-
192
- @suffix_list = runGit("branch").split("\n").map(){|x|
193
- x=~ @branch_format ?
194
- /^\*?\s*#{@branch_format_raw}\/([a-zA-Z0-9_-]+)\s*$/.match(x)[-1] :
195
- nil
196
- }.compact().uniq()
197
-
198
- return @suffix_list
199
- end
200
-
201
- def getUnreleasedTags(opts)
202
- remote_tags=runGit("ls-remote --tags #{@stable_repo} |
203
- egrep 'refs/tags/v[0-9.]*$'").split("\n").map(){
204
- |x| x.gsub(/.*refs\/tags\//, '')
205
- }
206
- local_tags =runGit("tag -l | egrep '^v[0-9.]*$'").split("\n")
207
-
208
- new_tags = local_tags - remote_tags
209
- return new_tags
210
- end
211
- def genReleaseNotif(opts, new_tags)
212
- return if @NOTIFY_RELEASE == false
213
-
214
- mail_path=`mktemp`.chomp()
215
- mail = File.open(mail_path, "w+")
216
- mail.puts "From " + runGit("rev-parse HEAD") + " " + `date`.chomp()
217
- mail.puts "From: " + getGitConfig("user.name") +
218
- " <" + getGitConfig("user.email") +">"
219
- mail.puts "To: " + getGitConfig("patch.target")
220
- mail.puts "Date: " + `date -R`.chomp()
221
-
222
- if new_tags.length > 4 then
223
- mail.puts "Subject: [ANNOUNCE] " + File.basename(@path) + ": new stable releases"
224
- mail.puts ""
225
- mail.puts "These version were tagged/released:\n * " +
226
- new_tags.join("\n * ")
227
- mail.puts ""
228
- else
229
- mail.puts "Subject: [ANNOUNCE] " + File.basename(@path) + " " +
230
- (new_tags.length > 1 ?
231
- (new_tags[0 .. -2].join(", ") + " and " + new_tags[-1] + " have") :
232
- (new_tags.join(" ") + " has")) +
233
- " been tagged/released"
234
- mail.puts ""
235
- end
236
- mail.puts "It's available at the normal places:"
237
- mail.puts ""
238
- mail.puts "git://github.com/#{@remote_stable}"
239
- mail.puts "https://github.com/#{@remote_stable}/releases"
240
- mail.puts ""
241
- mail.puts "---"
242
- mail.puts ""
243
- mail.puts "Here's the information from the tags:"
244
- new_tags.sort().each(){|tag|
245
- mail.puts `git show #{tag} --no-decorate -q | awk '!p;/^-----END PGP SIGNATURE-----/{p=1}'`
246
- mail.puts ""
247
- }
248
- mail.close()
249
-
250
- case @mail_format
251
- when :imap_send
252
- puts runGitImap("< #{mail_path}")
253
- when :send_email
254
- run("cp #{mail_path} announce-release.eml")
255
- log(:INFO, "Generated annoucement email in #{@path}/announce-release.eml")
256
- end
257
- run("rm -f #{mail_path}")
258
- end
259
- def submitReleases(opts, new_tags)
260
- new_tags.each(){|tag|
261
- createRelease(opts, tag)
262
- }
263
- end
264
-
265
- def createRelease(opts, tag, github_rel=true)
266
- log(:INFO, "Creating a release for #{tag}")
267
- runGit("push #{@stable_repo} refs/tags/#{tag}")
268
-
269
- if github_rel == true then
270
- msg = runGit("tag -l -n1000 '#{tag}'") + "\n"
271
-
272
- # Ye ghods is is a horrific format to parse
273
- name, body = msg.split("\n", 2)
274
- name = name.gsub(/^#{tag}/, '').strip
275
- body = body.split("\n").map { |l| l.sub(/^ /, '') }.join("\n")
276
- api.create_release(@remote_stable, tag, :name => name, :body => body)
277
- end
278
- end
279
-
280
- def versionToLocalBranch(version, suff)
281
- return @branch_format_raw.gsub(/\\\//, '/').
282
- gsub(/\(.*\)/, version) + "/#{suff}"
283
- end
284
-
285
- def versionToStableBranch(version)
286
- return version.gsub(/^(.*)$/, @stable_branch_format)
287
- end
288
-
289
- def findStableBase(branch)
290
- base=nil
291
- if branch =~ @branch_format then
292
- base = branch.gsub(/^\*?\s*#{@branch_format_raw}\/.*$/, @stable_base_format)
293
- end
294
-
295
- @stable_base_patterns.each(){|pattern, b|
296
- if branch =~ /#{pattern}\// || branch =~ /#{pattern}$/
297
- base = b
298
- break
299
- end
300
- }
301
- raise("Could not a find a stable base for branch #{branch}") if base == nil
302
- return base
303
- end
304
-
305
- def list_branches(opts)
306
- puts getBranchList(opts[:br_suff])
307
- end
308
- def list_suffixes(opts)
309
- puts getSuffixList()
310
- end
311
- def submit_release(opts)
312
- new_tags = getUnreleasedTags(opts)
313
- if new_tags.empty? then
314
- log(:INFO, "All tags are already submitted.")
315
- return
316
- end
317
-
318
- log(:WARNING, "This will officially release these tags: #{new_tags.join(", ")}")
319
- rep = GitMaintain::confirm(opts, "release them", true)
320
- if rep != 'y' then
321
- raise "Aborting.."
322
- end
323
-
324
- if @NOTIFY_RELEASE != false
325
- genReleaseNotif(opts, new_tags)
326
- end
327
-
328
- log(:WARNING, "Last chance to cancel before submitting")
329
- rep= GitMaintain::confirm(opts, "submit these releases", true)
330
- if rep != 'y' then
331
- raise "Aborting.."
332
- end
333
- submitReleases(opts, new_tags)
334
- end
335
- def summary(opts)
336
- log(:INFO, "Configuration summary:")
337
- if self.class != GitMaintain::Repo then
338
- log(:INFO, "Using custom repo class: #{self.class.to_s()}")
339
- end
340
- log(:INFO, "Stable remote: #{@stable_repo}")
341
- log(:INFO, "Validation remote: #{@valid_repo}")
342
- log(:INFO, "")
343
- log(:INFO, "Branch config:")
344
- log(:INFO, "Local branch format: /#{@branch_format_raw}/")
345
- log(:INFO, "Remote stable branch format: #{@stable_branch_format}")
346
- log(:INFO, "Remote stable base format: #{@stable_base_format}")
347
-
348
- if @stable_base_patterns.length > 0 then
349
- log(:INFO, "")
350
- log(:INFO, "Stable base rules:")
351
- @stable_base_patterns.each(){|name, base|
352
- log(:INFO, "\t#{name} -> #{base}")
353
- }
354
- end
355
- brList = getBranchList(opts[:br_suff])
356
- brStList = getStableBranchList()
357
-
358
- if brList.length > 0 then
359
- log(:INFO, "")
360
- log(:INFO, "Local branches:")
361
- brList.each(){|br|
362
- branch = Branch.load(self, br, nil, opts[:branch_suff])
363
- localBr = branch.local_branch
364
- stableBr = @@STABLE_REPO + "/" + branch.remote_branch
365
- stableBase = branch.stable_base
366
- runGit("rev-parse --verify --quiet #{stableBr}")
367
- stableBr = "<MISSING>" if $? != 0
368
- log(:INFO, "\t#{localBr} -> #{stableBr} (#{stableBase})")
369
- brStList.delete(br)
370
- }
371
- end
372
-
373
- if brStList.length > 0 then
374
- log(:INFO, "")
375
- log(:INFO, "Upstream branches:")
376
- brStList.each(){|br|
377
- branch = Branch.load(self, br, nil, opts[:branch_suff])
378
- stableBr = @@STABLE_REPO + "/" + branch.remote_branch
379
- stableBase = branch.stable_base
380
- log(:INFO, "\t<MISSING> -> #{stableBr} (#{stableBase})")
381
- }
382
- end
383
- end
384
- def find_alts(commit)
385
- alts=[]
386
-
387
- subj=runGit("log -1 --pretty='%s' #{commit}")
388
- return alts if $? != 0
389
-
390
- branches = getStableBranchList().map(){|v| @@STABLE_REPO + "/" + versionToStableBranch(v)}
391
-
392
- runGit("log -F --grep \"$#{subj}\" --format=\"%H\" #{branches.join(" ")}").
393
- split("\n").each(){|c|
394
- next if c == commit
395
- cursubj=runGit("log -1 --pretty='%s' #{c}")
396
- alts << c if subj == cursubj
397
- }
398
-
399
- return alts
400
- end
401
-
402
- #
403
- # Github API stuff
404
- #
405
- def api
406
- @api ||= Octokit::Client.new(:access_token => token, :auto_paginate => true)
407
- end
408
-
409
- def token
410
- @token ||= begin
411
- # We cannot use the 'defaults' functionality of git_config here,
412
- # because get_new_token would be evaluated before git_config ran
413
- tok = getGitConfig("maintain.api-token")
414
- tok.to_s() == "" ? get_new_token : tok
415
- end
416
- end
417
- def get_new_token
418
- puts "Requesting a new OAuth token from Github..."
419
- print "Github username: "
420
- user = $stdin.gets.chomp
421
- print "Github password: "
422
- pass = $stdin.noecho(&:gets).chomp
423
- puts
424
-
425
- api = Octokit::Client.new(:login => user, :password => pass)
426
-
427
- begin
428
- res = api.create_authorization(:scopes => [:repo], :note => "git-maintain")
429
- rescue Octokit::Unauthorized
430
- puts "Username or password incorrect. Please try again."
431
- return get_new_token
432
- rescue Octokit::OneTimePasswordRequired
433
- print "Github OTP: "
434
- otp = $stdin.noecho(&:gets).chomp
435
- res = api.create_authorization(:scopes => [:repo], :note => "git-maintain",
436
- :headers => {"X-GitHub-OTP" => otp})
437
- end
438
-
439
- token = res[:token]
440
- runGit("config --global maintain.api-token '#{token}'")
441
-
442
- # Now reopen with the token so OTP does not bother us
443
- @api=nil
444
- token
445
- end
446
- end
447
- end
data/lib/travis.rb DELETED
@@ -1,80 +0,0 @@
1
- module GitMaintain
2
- class TravisCI < CI
3
- TRAVIS_URL='https://api.travis-ci.com/'
4
-
5
- def initialize(repo)
6
- super(repo)
7
- @url = TRAVIS_URL
8
- end
9
-
10
- private
11
- def getState(sha1, resp)
12
- br = findBranch(sha1, resp)
13
- return "not found" if br == nil
14
-
15
- return br["state"]
16
- end
17
- def getLog(sha1, resp)
18
- br = findBranch(sha1, resp)
19
- raise("Travis build not found") if br == nil
20
- job_id = br["job_ids"].last().to_s()
21
- return getJson(@url, "travis_log_" + job_id, 'jobs/' + job_id + '/log', false)
22
- end
23
- def getTS(sha1, resp)
24
- br = findBranch(sha1, resp)
25
- raise("Travis build not found") if br == nil
26
- return br["started_at"]
27
- end
28
- def checkState(sha1, resp)
29
- return getState(sha1, resp) == "passed"
30
- end
31
-
32
- def getBrValidJson()
33
- return getJson(@url, :travis_br_valid, 'repos/' + @repo.remote_valid + '/branches')
34
- end
35
- def getBrStableJson()
36
- return getJson(@url, :travis_br_stable, 'repos/' + @repo.remote_stable + '/branches')
37
- end
38
- def findBranch(sha1, resp)
39
- log(:DEBUG_CI, "Looking for build for #{sha1}")
40
- resp["branches"].each(){|br|
41
- commit=resp["commits"].select(){|e| e["id"] == br["commit_id"]}.first()
42
- raise("Incomplete JSON received from Travis") if commit == nil
43
- log(:DEBUG_CI, "Found entry for sha #{commit["sha"]}")
44
- next if commit["sha"] != sha1
45
- return br
46
- }
47
- return nil
48
- end
49
-
50
- public
51
- def getValidState(br, sha1)
52
- return getState(sha1, getBrValidJson())
53
- end
54
- def checkValidState(br, sha1)
55
- return checkState(sha1, getBrValidJson())
56
- end
57
- def getValidLog(br, sha1)
58
- return getLog(sha1, getBrValidJson())
59
- end
60
- def getValidTS(br, sha1)
61
- return getTS(sha1, getBrValidJson())
62
- end
63
-
64
- def getStableState(br, sha1)
65
- return getState(sha1, getBrStableJson())
66
- end
67
- def checkStableState(br, sha1)
68
- return checkState(sha1, getBrStableJson())
69
- end
70
- def getStableLog(br, sha1)
71
- return getLog(sha1, getBrStableJson())
72
- end
73
- def getStableTS(br, sha1)
74
- return getTS(sha1, getBrStableJson())
75
- end
76
- def isErrored(br, status)
77
- return status == "failed" || status == "errored"
78
- end
79
- end
80
- end