git-maintain 0.9.0 → 0.12.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.
- checksums.yaml +4 -4
- data/CHANGELOG +29 -0
- data/README.md +9 -7
- data/bin/git-maintain +9 -0
- data/git-maintain-completion.sh +2 -2
- data/lib/addons/RDMACore.rb +71 -16
- data/lib/addons/git-maintain.rb +32 -12
- data/lib/azure.rb +2 -2
- data/lib/branch.rb +313 -232
- data/lib/common.rb +54 -14
- data/lib/repo.rb +144 -110
- metadata +7 -13
data/lib/common.rb
CHANGED
@@ -57,6 +57,7 @@ module GitMaintain
|
|
57
57
|
|
58
58
|
def registerCustom(repo_name, classes)
|
59
59
|
raise("Multiple class for repo #{repo_name}") if @@custom_classes[repo_name] != nil
|
60
|
+
classes[:name] = repo_name if classes[:name] == nil
|
60
61
|
@@custom_classes[repo_name] = classes
|
61
62
|
end
|
62
63
|
module_function :registerCustom
|
@@ -73,6 +74,11 @@ module GitMaintain
|
|
73
74
|
end
|
74
75
|
module_function :getClass
|
75
76
|
|
77
|
+
def getCustomClasses()
|
78
|
+
return @@custom_classes
|
79
|
+
end
|
80
|
+
module_function :getCustomClasses
|
81
|
+
|
76
82
|
def loadClass(default_class, repo_name, *more)
|
77
83
|
@@load_class.push(default_class)
|
78
84
|
obj = GitMaintain::getClass(default_class, repo_name).new(*more)
|
@@ -95,49 +101,64 @@ module GitMaintain
|
|
95
101
|
|
96
102
|
def getActionAttr(attr)
|
97
103
|
if Common.const_get(attr).class == Hash
|
98
|
-
return ACTION_CLASS.inject({}){|h, x| h.merge(x.const_get(attr))}
|
104
|
+
return ACTION_CLASS.inject({}){|h, x| h.merge(getClass(x).const_get(attr))}
|
99
105
|
else
|
100
|
-
return ACTION_CLASS.map(){|x| x.const_get(attr)}.flatten()
|
106
|
+
return ACTION_CLASS.map(){|x| getClass(x).const_get(attr)}.flatten()
|
101
107
|
end
|
102
108
|
end
|
103
109
|
module_function :getActionAttr
|
104
110
|
|
105
111
|
def setOpts(action, optsParser, opts)
|
106
112
|
ACTION_CLASS.each(){|x|
|
107
|
-
|
108
|
-
|
113
|
+
if x::ACTION_LIST.index(action) != nil &&
|
114
|
+
x.singleton_methods().index(:set_opts) != nil then
|
115
|
+
matched=true
|
109
116
|
x.set_opts(action, optsParser, opts)
|
110
117
|
end
|
111
118
|
# Try to add repo specific opts
|
112
119
|
y = getClass(x)
|
113
|
-
if x != y && y.
|
120
|
+
if x != y && y::ACTION_LIST.index(action) != nil &&
|
121
|
+
y.singleton_methods().index(:set_opts) != nil then
|
122
|
+
matched=true
|
123
|
+
x.set_opts(action, optsParser, opts) if x.singleton_methods().index(:set_opts) != nil
|
114
124
|
y.set_opts(action, optsParser, opts)
|
115
125
|
end
|
116
|
-
break
|
126
|
+
break if matched == true
|
117
127
|
}
|
118
128
|
end
|
119
129
|
module_function :setOpts
|
120
130
|
|
121
131
|
def checkOpts(opts)
|
122
132
|
ACTION_CLASS.each(){|x|
|
123
|
-
|
124
|
-
|
125
|
-
|
133
|
+
if x::ACTION_LIST.index(opts[:action]) != nil &&
|
134
|
+
x.singleton_methods().index(:check_opts) != nil then
|
135
|
+
matched=true
|
136
|
+
x.check_opts(opts)
|
137
|
+
end
|
126
138
|
|
127
139
|
# Try to add repo specific opts
|
128
140
|
y = getClass(x)
|
129
|
-
if x != y && y.
|
141
|
+
if x != y && y::ACTION_LIST.index(opts[:action]) != nil &&
|
142
|
+
y.singleton_methods().index(:check_opts) != nil then
|
143
|
+
matched=true
|
144
|
+
x.check_opts(opts) if x.singleton_methods().index(:check_opts) != nil
|
130
145
|
y.check_opts(opts)
|
131
146
|
end
|
132
|
-
|
147
|
+
break if matched == true
|
148
|
+
}
|
133
149
|
end
|
134
150
|
module_function :checkOpts
|
135
151
|
|
136
152
|
def execAction(opts, action)
|
137
153
|
ACTION_CLASS.each(){|x|
|
138
|
-
|
139
|
-
|
140
|
-
|
154
|
+
if x::ACTION_LIST.index(action) != nil
|
155
|
+
return x.execAction(opts, action)
|
156
|
+
end
|
157
|
+
# Try to add repo specific opts
|
158
|
+
y = getClass(x)
|
159
|
+
if x != y && y::ACTION_LIST.index(opts[:action]) != nil then
|
160
|
+
return y.execAction(opts, action)
|
161
|
+
end
|
141
162
|
}
|
142
163
|
end
|
143
164
|
module_function :execAction
|
@@ -202,6 +223,12 @@ module GitMaintain
|
|
202
223
|
end
|
203
224
|
module_function :log
|
204
225
|
|
226
|
+
def crit(msg)
|
227
|
+
log(:ERROR, msg)
|
228
|
+
raise msg
|
229
|
+
end
|
230
|
+
module_function :crit
|
231
|
+
|
205
232
|
def setVerbose(val)
|
206
233
|
@@verbose_log = val
|
207
234
|
end
|
@@ -217,3 +244,16 @@ Dir.entries(BACKPORT_LIB_DIR + "/addons/").each(){|entry|
|
|
217
244
|
require entry.sub(/.rb$/, "")
|
218
245
|
}
|
219
246
|
$LOAD_PATH.pop()
|
247
|
+
|
248
|
+
if ENV["GIT_MAINTAIN_ADDON_DIR"].to_s() != "" then
|
249
|
+
ADDON_DIR=ENV["GIT_MAINTAIN_ADDON_DIR"].to_s()
|
250
|
+
if Dir.exist?(ADDON_DIR) then
|
251
|
+
$LOAD_PATH.push(ADDON_DIR)
|
252
|
+
Dir.entries(ADDON_DIR).each(){|entry|
|
253
|
+
next if (!File.file?(ADDON_DIR + "/" + entry) || entry !~ /\.rb$/ );
|
254
|
+
require entry.sub(/.rb$/, "")
|
255
|
+
}
|
256
|
+
$LOAD_PATH.pop()
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
data/lib/repo.rb
CHANGED
@@ -2,6 +2,11 @@ require 'octokit'
|
|
2
2
|
require 'io/console'
|
3
3
|
|
4
4
|
module GitMaintain
|
5
|
+
class NoRefError < RuntimeError
|
6
|
+
def initialize(ref)
|
7
|
+
super("Reference '#{ref}' was not found")
|
8
|
+
end
|
9
|
+
end
|
5
10
|
class Repo
|
6
11
|
@@VALID_REPO = "github"
|
7
12
|
@@STABLE_REPO = "stable"
|
@@ -19,7 +24,7 @@ module GitMaintain
|
|
19
24
|
}
|
20
25
|
|
21
26
|
def self.load(path=".")
|
22
|
-
dir =
|
27
|
+
dir = File.realdirpath(path)
|
23
28
|
repo_name = File.basename(dir)
|
24
29
|
return GitMaintain::loadClass(Repo, repo_name, dir)
|
25
30
|
end
|
@@ -60,9 +65,9 @@ module GitMaintain
|
|
60
65
|
@stable_repo = getGitConfig("maintain.stable-repo")
|
61
66
|
@stable_repo = @@STABLE_REPO if @stable_repo == ""
|
62
67
|
|
63
|
-
@remote_valid=runGit("remote -v |
|
68
|
+
@remote_valid=runGit("remote -v | grep -E '^#{@valid_repo}' | grep fetch |
|
64
69
|
awk '{ print $2}' | sed -e 's/.*://' -e 's/\\.git//'")
|
65
|
-
@remote_stable=runGit("remote -v |
|
70
|
+
@remote_stable=runGit("remote -v | grep -E '^#{@stable_repo}' | grep fetch |
|
66
71
|
awk '{ print $2}' | sed -e 's/.*://' -e 's/\\.git//'")
|
67
72
|
|
68
73
|
@auto_fetch = getGitConfig("maintain.autofetch")
|
@@ -83,7 +88,7 @@ module GitMaintain
|
|
83
88
|
@stable_base_format = getGitConfig("maintain.stable-base-format")
|
84
89
|
|
85
90
|
@stable_base_patterns=
|
86
|
-
runGit("config --get-regexp stable-base |
|
91
|
+
runGit("config --get-regexp stable-base | grep -E '^stable-base\.' | "+
|
87
92
|
"sed -e 's/stable-base\.//' -e 's/---/\\//g'").split("\n").inject({}){ |m, x|
|
88
93
|
y=x.split(" ");
|
89
94
|
m[y[0]] = y[1]
|
@@ -110,22 +115,41 @@ module GitMaintain
|
|
110
115
|
GitMaintain::log(lvl, str)
|
111
116
|
end
|
112
117
|
|
113
|
-
def
|
114
|
-
|
118
|
+
def _run_check_ret(ret, opts)
|
119
|
+
raise(RuntimeError.new(ret)) if $?.exitstatus != 0 && opts.fetch(:check_err, true) == true
|
115
120
|
end
|
116
|
-
def
|
117
|
-
|
121
|
+
def run(cmd, opts = {})
|
122
|
+
ret = `cd #{@path} && #{cmd}`
|
123
|
+
_run_check_ret(ret, opts)
|
124
|
+
return ret
|
118
125
|
end
|
126
|
+
def runSystem(cmd, opts = {})
|
127
|
+
ret = system("cd #{@path} && #{cmd}")
|
128
|
+
_run_check_ret(nil, opts)
|
129
|
+
return ret
|
119
130
|
|
120
|
-
|
131
|
+
end
|
132
|
+
def runGit(cmd, opts = {})
|
121
133
|
log(:DEBUG, "Called from #{caller[1]}")
|
122
134
|
log(:DEBUG, "Running git command '#{cmd}'")
|
123
|
-
|
135
|
+
ret = `git --work-tree=#{@path} #{cmd}`.chomp()
|
136
|
+
_run_check_ret(ret, opts)
|
137
|
+
return ret
|
124
138
|
end
|
125
|
-
def runGitInteractive(cmd)
|
139
|
+
def runGitInteractive(cmd, opts = {})
|
126
140
|
log(:DEBUG, "Called from #{caller[1]}")
|
127
141
|
log(:DEBUG, "Running interactive git command '#{cmd}'")
|
128
|
-
|
142
|
+
ret = system("git --work-tree=#{@path} #{cmd}")
|
143
|
+
_run_check_ret(nil, opts)
|
144
|
+
return ret
|
145
|
+
|
146
|
+
end
|
147
|
+
def ref_exist?(ref)
|
148
|
+
begin
|
149
|
+
return runGit("rev-parse --verify --quiet '#{ref}'")
|
150
|
+
rescue RuntimeError
|
151
|
+
raise(NoRefError.new(ref))
|
152
|
+
end
|
129
153
|
end
|
130
154
|
|
131
155
|
def runGitImap(cmd)
|
@@ -138,17 +162,18 @@ module GitMaintain
|
|
138
162
|
fi; git --work-tree=#{@path} imap-send #{cmd}`
|
139
163
|
end
|
140
164
|
def getGitConfig(entry)
|
141
|
-
return @config_cache[entry] ||= runGit("config #{entry} 2> /dev/null").chomp()
|
165
|
+
return @config_cache[entry] ||= runGit("config #{entry} 2> /dev/null", :check_err => false).chomp()
|
142
166
|
end
|
143
167
|
|
144
|
-
def runBash()
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
else
|
168
|
+
def runBash(env="")
|
169
|
+
begin
|
170
|
+
runSystem(env + " bash")
|
171
|
+
rescue RuntimeError
|
149
172
|
log(:ERROR, "Shell exited with code #{$?}. Exiting")
|
150
173
|
raise("Cancelled by user")
|
151
174
|
end
|
175
|
+
log(:INFO, "Continuing...")
|
176
|
+
|
152
177
|
end
|
153
178
|
|
154
179
|
def getCommitHeadline(sha)
|
@@ -200,10 +225,10 @@ module GitMaintain
|
|
200
225
|
|
201
226
|
def getUnreleasedTags(opts)
|
202
227
|
remote_tags=runGit("ls-remote --tags #{@stable_repo} |
|
203
|
-
|
228
|
+
grep -E 'refs/tags/v[0-9.]*$'").split("\n").map(){
|
204
229
|
|x| x.gsub(/.*refs\/tags\//, '')
|
205
230
|
}
|
206
|
-
local_tags =runGit("tag -l |
|
231
|
+
local_tags =runGit("tag -l | grep -E '^v[0-9.]*$'").split("\n")
|
207
232
|
|
208
233
|
new_tags = local_tags - remote_tags
|
209
234
|
return new_tags
|
@@ -264,22 +289,22 @@ module GitMaintain
|
|
264
289
|
|
265
290
|
def createRelease(opts, tag, github_rel=true)
|
266
291
|
log(:INFO, "Creating a release for #{tag}")
|
267
|
-
|
292
|
+
runGit("push #{@stable_repo} refs/tags/#{tag}")
|
268
293
|
|
269
294
|
if github_rel == true then
|
270
|
-
|
295
|
+
msg = runGit("tag -l -n1000 '#{tag}'") + "\n"
|
271
296
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
297
|
+
# Ye ghods is is a horrific format to parse
|
298
|
+
name, body = msg.split("\n", 2)
|
299
|
+
name = name.gsub(/^#{tag}/, '').strip
|
300
|
+
body = body.split("\n").map { |l| l.sub(/^ /, '') }.join("\n")
|
301
|
+
api.create_release(@remote_stable, tag, :name => name, :body => body)
|
277
302
|
end
|
278
|
-
|
303
|
+
end
|
279
304
|
|
280
305
|
def versionToLocalBranch(version, suff)
|
281
306
|
return @branch_format_raw.gsub(/\\\//, '/').
|
282
|
-
|
307
|
+
gsub(/\(.*\)/, version) + "/#{suff}"
|
283
308
|
end
|
284
309
|
|
285
310
|
def versionToStableBranch(version)
|
@@ -333,56 +358,65 @@ module GitMaintain
|
|
333
358
|
submitReleases(opts, new_tags)
|
334
359
|
end
|
335
360
|
def summary(opts)
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
361
|
+
log(:INFO, "Configuration summary:")
|
362
|
+
if self.class != GitMaintain::Repo then
|
363
|
+
log(:INFO, "Using custom repo class: #{self.class.to_s()}")
|
364
|
+
end
|
365
|
+
log(:INFO, "Stable remote: #{@stable_repo}")
|
366
|
+
log(:INFO, "Validation remote: #{@valid_repo}")
|
367
|
+
log(:INFO, "")
|
368
|
+
log(:INFO, "Branch config:")
|
369
|
+
log(:INFO, "Local branch format: /#{@branch_format_raw}/")
|
370
|
+
log(:INFO, "Remote stable branch format: #{@stable_branch_format}")
|
371
|
+
log(:INFO, "Remote stable base format: #{@stable_base_format}")
|
372
|
+
|
373
|
+
if @stable_base_patterns.length > 0 then
|
374
|
+
log(:INFO, "")
|
375
|
+
log(:INFO, "Stable base rules:")
|
376
|
+
@stable_base_patterns.each(){|name, base|
|
377
|
+
log(:INFO, "\t#{name} -> #{base}")
|
378
|
+
}
|
379
|
+
end
|
380
|
+
brList = getBranchList(opts[:br_suff])
|
381
|
+
brStList = getStableBranchList()
|
382
|
+
|
383
|
+
if brList.length > 0 then
|
384
|
+
log(:INFO, "")
|
385
|
+
log(:INFO, "Local branches:")
|
386
|
+
brList.each(){|br|
|
387
|
+
branch = Branch.load(self, br, nil, opts[:br_suff])
|
388
|
+
localBr = branch.local_branch
|
389
|
+
stableBr = @@STABLE_REPO + "/" + branch.remote_branch
|
390
|
+
stableBase = branch.stable_base
|
391
|
+
begin
|
392
|
+
ref_exist?(stableBr)
|
393
|
+
rescue NoRefError
|
394
|
+
stableBr = "<MISSING>"
|
395
|
+
end
|
396
|
+
log(:INFO, "\t#{localBr} -> #{stableBr} (#{stableBase})")
|
397
|
+
brStList.delete(br)
|
398
|
+
}
|
399
|
+
end
|
400
|
+
|
401
|
+
if brStList.length > 0 then
|
402
|
+
log(:INFO, "")
|
403
|
+
log(:INFO, "Upstream branches:")
|
404
|
+
brStList.each(){|br|
|
405
|
+
branch = Branch.load(self, br, nil, opts[:branch_suff])
|
406
|
+
stableBr = @@STABLE_REPO + "/" + branch.remote_branch
|
407
|
+
stableBase = branch.stable_base
|
408
|
+
log(:INFO, "\t<MISSING> -> #{stableBr} (#{stableBase})")
|
409
|
+
}
|
410
|
+
end
|
411
|
+
end
|
381
412
|
def find_alts(commit)
|
382
413
|
alts=[]
|
383
414
|
|
384
|
-
|
385
|
-
|
415
|
+
begin
|
416
|
+
subj=runGit("log -1 --pretty='%s' #{commit}")
|
417
|
+
rescue RuntimeError
|
418
|
+
return []
|
419
|
+
end
|
386
420
|
|
387
421
|
branches = getStableBranchList().map(){|v| @@STABLE_REPO + "/" + versionToStableBranch(v)}
|
388
422
|
|
@@ -399,46 +433,46 @@ module GitMaintain
|
|
399
433
|
#
|
400
434
|
# Github API stuff
|
401
435
|
#
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
436
|
+
def api
|
437
|
+
@api ||= Octokit::Client.new(:access_token => token, :auto_paginate => true)
|
438
|
+
end
|
439
|
+
|
440
|
+
def token
|
441
|
+
@token ||= begin
|
442
|
+
# We cannot use the 'defaults' functionality of git_config here,
|
443
|
+
# because get_new_token would be evaluated before git_config ran
|
444
|
+
tok = getGitConfig("maintain.api-token")
|
411
445
|
tok.to_s() == "" ? get_new_token : tok
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
446
|
+
end
|
447
|
+
end
|
448
|
+
def get_new_token
|
449
|
+
puts "Requesting a new OAuth token from Github..."
|
450
|
+
print "Github username: "
|
451
|
+
user = $stdin.gets.chomp
|
452
|
+
print "Github password: "
|
453
|
+
pass = $stdin.noecho(&:gets).chomp
|
454
|
+
puts
|
455
|
+
|
456
|
+
api = Octokit::Client.new(:login => user, :password => pass)
|
457
|
+
|
458
|
+
begin
|
459
|
+
res = api.create_authorization(:scopes => [:repo], :note => "git-maintain")
|
460
|
+
rescue Octokit::Unauthorized
|
461
|
+
puts "Username or password incorrect. Please try again."
|
462
|
+
return get_new_token
|
429
463
|
rescue Octokit::OneTimePasswordRequired
|
430
|
-
|
431
|
-
|
432
|
-
|
464
|
+
print "Github OTP: "
|
465
|
+
otp = $stdin.noecho(&:gets).chomp
|
466
|
+
res = api.create_authorization(:scopes => [:repo], :note => "git-maintain",
|
433
467
|
:headers => {"X-GitHub-OTP" => otp})
|
434
|
-
|
468
|
+
end
|
435
469
|
|
436
|
-
|
437
|
-
|
470
|
+
token = res[:token]
|
471
|
+
runGit("config --global maintain.api-token '#{token}'")
|
438
472
|
|
439
473
|
# Now reopen with the token so OTP does not bother us
|
440
474
|
@api=nil
|
441
475
|
token
|
442
|
-
|
443
|
-
|
476
|
+
end
|
477
|
+
end
|
444
478
|
end
|
metadata
CHANGED
@@ -1,35 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git-maintain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolas Morey-Chaisemartin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: octokit
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '5'
|
19
|
+
version: '5.0'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- - "
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '3.0'
|
30
|
-
- - "<"
|
24
|
+
- - "~>"
|
31
25
|
- !ruby/object:Gem::Version
|
32
|
-
version: '5'
|
26
|
+
version: '5.0'
|
33
27
|
description: |-
|
34
28
|
Be lazy and let git-maintain do all the heavy lifting for maintaining stable branches.
|
35
29
|
Leaves you only with the essential: reviewing the selected patches and decide where they should go.
|
@@ -71,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
65
|
- !ruby/object:Gem::Version
|
72
66
|
version: '0'
|
73
67
|
requirements: []
|
74
|
-
rubygems_version: 3.
|
68
|
+
rubygems_version: 3.2.33
|
75
69
|
signing_key:
|
76
70
|
specification_version: 4
|
77
71
|
summary: Your ultimate script for maintaining stable branches and releasing your project.
|