git-maintain 0.12.0 → 0.14.2
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 +23 -0
- data/README.md +14 -14
- data/bin/git-maintain +7 -55
- data/git-maintain-completion.sh +1 -1
- data/lib/{addons → git-maintain/addons}/RDMACore.rb +79 -20
- data/lib/{addons → git-maintain/addons}/git-maintain.rb +61 -6
- data/lib/git-maintain/addons/healthd.rb +73 -0
- data/lib/git-maintain/addons/hpc-testing.rb +102 -0
- data/lib/git-maintain/azure.rb +200 -0
- data/lib/{branch.rb → git-maintain/branch.rb} +356 -237
- data/lib/git-maintain/branch_iterator.rb +148 -0
- data/lib/git-maintain/ci.rb +163 -0
- data/lib/git-maintain/common.rb +117 -0
- data/lib/git-maintain/error.rb +81 -0
- data/lib/{repo.rb → git-maintain/repo.rb} +199 -104
- data/lib/git-maintain/travis.rb +179 -0
- data/lib/git-maintain.rb +2 -0
- metadata +30 -14
- data/lib/azure.rb +0 -98
- data/lib/ci.rb +0 -88
- data/lib/common.rb +0 -259
- data/lib/travis.rb +0 -80
|
@@ -1,52 +1,58 @@
|
|
|
1
1
|
require 'octokit'
|
|
2
2
|
require 'io/console'
|
|
3
3
|
|
|
4
|
+
# Main module for git-maintain repository maintenance tool.
|
|
4
5
|
module GitMaintain
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
class Repo
|
|
6
|
+
# Class representing a git repository being maintained by git-maintain.
|
|
7
|
+
# Handles parsing and caching configuration, querying branches/tags, and executing git commands.
|
|
8
|
+
class Repo < Common
|
|
9
|
+
# Default name of the validation/upstream repository.
|
|
11
10
|
@@VALID_REPO = "github"
|
|
11
|
+
# Default name of the stable release repository.
|
|
12
12
|
@@STABLE_REPO = "stable"
|
|
13
|
+
# Default path to the release package submission helper command.
|
|
13
14
|
@@SUBMIT_BINARY="git-release"
|
|
14
15
|
|
|
16
|
+
# List of available actions for Repo class.
|
|
15
17
|
ACTION_LIST = [
|
|
16
18
|
:list_branches,
|
|
17
19
|
:summary,
|
|
18
20
|
# Internal commands for completion
|
|
19
21
|
:list_suffixes, :submit_release
|
|
20
22
|
]
|
|
23
|
+
# Description map of actions for CLI help output.
|
|
21
24
|
ACTION_HELP = {
|
|
22
25
|
:submit_release => "Push the tags to 'stable' remote and create the release packages",
|
|
23
26
|
:summary => "Displays a summary of the configuration and the branches git-maintain sees"
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
# Factory method to load an instance of the Repo class or its repository-specific subclass.
|
|
30
|
+
#
|
|
31
|
+
# @param path [String] Repository directory path (defaults to current working directory)
|
|
32
|
+
# @return [Repo] The loaded Repo instance (or subclass)
|
|
33
|
+
# @raise [GitMaintainError] If class loading fails
|
|
34
|
+
def self.load()
|
|
35
|
+
(repo_path, repo_name) = GitMaintain::getRepoInfos()
|
|
36
|
+
return GitMaintain::loadClass(Repo, repo_name, repo_path)
|
|
30
37
|
end
|
|
31
38
|
|
|
39
|
+
# Validate parsed options for repository-level actions.
|
|
40
|
+
#
|
|
41
|
+
# @param opts [Hash] Options hash to validate
|
|
42
|
+
# @raise [GitMaintainError] If options are invalid or conflicting
|
|
32
43
|
def self.check_opts(opts)
|
|
33
44
|
if opts[:action] == :submit_release then
|
|
34
45
|
if opts[:br_suff] != "master" then
|
|
35
|
-
raise "Action #{opts[:action]} can only be done on 'master' suffixed branches"
|
|
46
|
+
raise GitMaintainError.new("Action #{opts[:action]} can only be done on 'master' suffixed branches")
|
|
36
47
|
end
|
|
37
48
|
end
|
|
38
49
|
end
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
end
|
|
46
|
-
repo.send(action, opts)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def initialize(path=nil)
|
|
51
|
+
# Initialize the Repo instance, parsing git configurations and detecting remotes/formats.
|
|
52
|
+
#
|
|
53
|
+
# @param path [String, nil] Repository directory path (defaults to current working directory)
|
|
54
|
+
# @raise [GitMaintainError] If configuration format or values are invalid
|
|
55
|
+
def initialize(path)
|
|
50
56
|
GitMaintain::checkDirectConstructor(self.class)
|
|
51
57
|
|
|
52
58
|
@path = path
|
|
@@ -79,11 +85,11 @@ module GitMaintain
|
|
|
79
85
|
when "false", "no", "off"
|
|
80
86
|
@auto_fetch = false
|
|
81
87
|
else
|
|
82
|
-
raise("Invalid value '#{@auto_fetch}' in git config for maintain.autofetch")
|
|
88
|
+
raise GitMaintainError.new("Invalid value '#{@auto_fetch}' in git config for maintain.autofetch")
|
|
83
89
|
end
|
|
84
90
|
|
|
85
91
|
@branch_format_raw = getGitConfig("maintain.branch-format")
|
|
86
|
-
@branch_format = Regexp.new(
|
|
92
|
+
@branch_format = Regexp.new(@branch_format_raw)
|
|
87
93
|
@stable_branch_format = getGitConfig("maintain.stable-branch-format")
|
|
88
94
|
@stable_base_format = getGitConfig("maintain.stable-base-format")
|
|
89
95
|
|
|
@@ -103,7 +109,7 @@ module GitMaintain
|
|
|
103
109
|
case @mail_format
|
|
104
110
|
when "imap_send", "send_email"
|
|
105
111
|
else
|
|
106
|
-
raise("Invalid mail-format #{@mail_format}")
|
|
112
|
+
raise GitMaintainError.new("Invalid mail-format #{@mail_format}")
|
|
107
113
|
end
|
|
108
114
|
|
|
109
115
|
@mail_format = @mail_format.to_sym()
|
|
@@ -111,47 +117,25 @@ module GitMaintain
|
|
|
111
117
|
end
|
|
112
118
|
attr_reader :path, :name, :remote_valid, :remote_stable, :valid_repo, :stable_repo
|
|
113
119
|
|
|
114
|
-
def log(lvl, str)
|
|
115
|
-
GitMaintain::log(lvl, str)
|
|
116
|
-
end
|
|
117
120
|
|
|
118
|
-
def _run_check_ret(ret, opts)
|
|
119
|
-
raise(RuntimeError.new(ret)) if $?.exitstatus != 0 && opts.fetch(:check_err, true) == true
|
|
120
|
-
end
|
|
121
|
-
def run(cmd, opts = {})
|
|
122
|
-
ret = `cd #{@path} && #{cmd}`
|
|
123
|
-
_run_check_ret(ret, opts)
|
|
124
|
-
return ret
|
|
125
|
-
end
|
|
126
|
-
def runSystem(cmd, opts = {})
|
|
127
|
-
ret = system("cd #{@path} && #{cmd}")
|
|
128
|
-
_run_check_ret(nil, opts)
|
|
129
|
-
return ret
|
|
130
|
-
|
|
131
|
-
end
|
|
132
|
-
def runGit(cmd, opts = {})
|
|
133
|
-
log(:DEBUG, "Called from #{caller[1]}")
|
|
134
|
-
log(:DEBUG, "Running git command '#{cmd}'")
|
|
135
|
-
ret = `git --work-tree=#{@path} #{cmd}`.chomp()
|
|
136
|
-
_run_check_ret(ret, opts)
|
|
137
|
-
return ret
|
|
138
|
-
end
|
|
139
|
-
def runGitInteractive(cmd, opts = {})
|
|
140
|
-
log(:DEBUG, "Called from #{caller[1]}")
|
|
141
|
-
log(:DEBUG, "Running interactive git command '#{cmd}'")
|
|
142
|
-
ret = system("git --work-tree=#{@path} #{cmd}")
|
|
143
|
-
_run_check_ret(nil, opts)
|
|
144
|
-
return ret
|
|
145
121
|
|
|
146
|
-
|
|
122
|
+
# Check if the specified git reference exists.
|
|
123
|
+
#
|
|
124
|
+
# @param ref [String] Git reference to verify (e.g. 'HEAD' or 'origin/master')
|
|
125
|
+
# @return [String] Resolved SHA-1 string of the reference
|
|
126
|
+
# @raise [NoRefError] If the reference does not exist
|
|
147
127
|
def ref_exist?(ref)
|
|
148
128
|
begin
|
|
149
129
|
return runGit("rev-parse --verify --quiet '#{ref}'")
|
|
150
|
-
rescue
|
|
130
|
+
rescue RunError
|
|
151
131
|
raise(NoRefError.new(ref))
|
|
152
132
|
end
|
|
153
133
|
end
|
|
154
134
|
|
|
135
|
+
# Run a git imap-send command setting GIT_ASKPASS environment variables.
|
|
136
|
+
#
|
|
137
|
+
# @param cmd [String] The command arguments
|
|
138
|
+
# @return [String] Output of the command execution
|
|
155
139
|
def runGitImap(cmd)
|
|
156
140
|
return `export GIT_ASKPASS=$(dirname $(dirname $(which git)))/lib/git-core/git-gui--askpass;
|
|
157
141
|
if [ ! -f $GIT_ASKPASS ]; then
|
|
@@ -161,68 +145,112 @@ module GitMaintain
|
|
|
161
145
|
export GIT_ASKPASS=/usr/lib/ssh/ssh-askpass;
|
|
162
146
|
fi; git --work-tree=#{@path} imap-send #{cmd}`
|
|
163
147
|
end
|
|
148
|
+
|
|
149
|
+
# Retrieve a git configuration value, with caching.
|
|
150
|
+
#
|
|
151
|
+
# @param entry [String] Config key name (e.g., 'user.name')
|
|
152
|
+
# @return [String] Cached or fetched config value string
|
|
153
|
+
# @raise [RunError] If running git config fails unexpectedly
|
|
164
154
|
def getGitConfig(entry)
|
|
165
|
-
return @config_cache[entry] ||= runGit("config #{entry} 2> /dev/null",
|
|
155
|
+
return @config_cache[entry] ||= runGit("config #{entry} 2> /dev/null", {}, false).chomp()
|
|
166
156
|
end
|
|
167
157
|
|
|
158
|
+
# Spawn an interactive subshell (bash), wrapping error conditions into a GitMaintainError.
|
|
159
|
+
#
|
|
160
|
+
# @param env [String] Optional environment variables string to prepend
|
|
161
|
+
# @raise [GitMaintainError] If the shell exits with a non-zero code and is cancelled by the user
|
|
168
162
|
def runBash(env="")
|
|
169
163
|
begin
|
|
170
164
|
runSystem(env + " bash")
|
|
171
|
-
rescue
|
|
165
|
+
rescue RunError
|
|
172
166
|
log(:ERROR, "Shell exited with code #{$?}. Exiting")
|
|
173
|
-
raise("Cancelled by user")
|
|
167
|
+
raise GitMaintainError.new("Cancelled by user")
|
|
174
168
|
end
|
|
175
169
|
log(:INFO, "Continuing...")
|
|
176
170
|
|
|
177
171
|
end
|
|
178
172
|
|
|
173
|
+
# Get the single-line headline/oneline description of a commit SHA.
|
|
174
|
+
#
|
|
175
|
+
# @param sha [String] Commit SHA
|
|
176
|
+
# @return [String] Single-line description (oneline)
|
|
177
|
+
# @raise [RunError] If git command execution fails
|
|
179
178
|
def getCommitHeadline(sha)
|
|
180
179
|
return runGit("show --format=oneline --no-patch --no-decorate #{sha}")
|
|
181
180
|
end
|
|
181
|
+
|
|
182
|
+
# Get the commit subject text.
|
|
183
|
+
#
|
|
184
|
+
# @param sha [String] Commit SHA
|
|
185
|
+
# @return [String] The commit subject string
|
|
186
|
+
# @raise [RunError] If git command execution fails
|
|
182
187
|
def getCommitSubj(sha)
|
|
183
188
|
return runGit("log -1 --pretty=\"%s\" #{sha}")
|
|
184
189
|
end
|
|
185
190
|
|
|
191
|
+
# Fetch stable updates if auto-fetching is enabled.
|
|
192
|
+
#
|
|
193
|
+
# @param fetch [Boolean, nil] Override to bypass or force fetch configuration
|
|
194
|
+
# @raise [RunError] If git fetch execution fails
|
|
186
195
|
def stableUpdate(fetch=nil)
|
|
187
196
|
fetch = @auto_fetch if fetch == nil
|
|
188
197
|
return if fetch == false
|
|
189
198
|
log(:VERBOSE, "Fetching stable updates...")
|
|
190
199
|
runGit("fetch #{@stable_repo}")
|
|
191
200
|
end
|
|
201
|
+
|
|
202
|
+
# List all local branches matching the configured stable suffix name.
|
|
203
|
+
#
|
|
204
|
+
# @param br_suff [String] Branch suffix (e.g. 'master')
|
|
205
|
+
# @return [Array<String>] List of matching branch version strings (e.g., ['1.0'])
|
|
206
|
+
# @raise [RunError] If git branch execution fails
|
|
192
207
|
def getBranchList(br_suff)
|
|
193
208
|
return @branch_list if @branch_list != nil
|
|
194
209
|
|
|
195
210
|
@branch_list=runGit("branch").split("\n").map(){|x|
|
|
196
|
-
x=~
|
|
211
|
+
x=~ Regexp.new("#{@branch_format_raw}/#{br_suff}$") ?
|
|
197
212
|
$1 : nil
|
|
198
213
|
}.compact().uniq()
|
|
199
214
|
|
|
200
215
|
return @branch_list
|
|
201
216
|
end
|
|
202
217
|
|
|
218
|
+
# List all remote stable branches from the stable remote.
|
|
219
|
+
#
|
|
220
|
+
# @return [Array<String>] List of remote stable branch version strings
|
|
221
|
+
# @raise [RunError] If git branch execution fails
|
|
203
222
|
def getStableBranchList()
|
|
204
223
|
return @stable_branches if @stable_branches != nil
|
|
205
224
|
|
|
206
225
|
@stable_branches=runGit("branch -a").split("\n").map(){|x|
|
|
207
|
-
x=~
|
|
226
|
+
x=~ Regexp.new("remotes/#{@@STABLE_REPO}/#{@stable_branch_format.gsub(/\\1/, '([0-9]+)')}$") ?
|
|
208
227
|
$1 : nil
|
|
209
228
|
}.compact().uniq()
|
|
210
229
|
|
|
211
230
|
return @stable_branches
|
|
212
231
|
end
|
|
213
232
|
|
|
233
|
+
# Get all local branch suffix values.
|
|
234
|
+
#
|
|
235
|
+
# @return [Array<String>] List of local branch suffixes
|
|
236
|
+
# @raise [RunError] If git branch execution fails
|
|
214
237
|
def getSuffixList()
|
|
215
238
|
return @suffix_list if @suffix_list != nil
|
|
216
239
|
|
|
217
240
|
@suffix_list = runGit("branch").split("\n").map(){|x|
|
|
218
|
-
x=~ @branch_format ?
|
|
219
|
-
|
|
241
|
+
x=~ @branch_format ?
|
|
242
|
+
Regexp.new("^\\*?\\s*#{@branch_format_raw}/([a-zA-Z0-9_-]+)\\s*$").match(x)[-1] :
|
|
220
243
|
nil
|
|
221
244
|
}.compact().uniq()
|
|
222
245
|
|
|
223
246
|
return @suffix_list
|
|
224
247
|
end
|
|
225
248
|
|
|
249
|
+
# Find local release tags that have not yet been pushed to the remote stable repository.
|
|
250
|
+
#
|
|
251
|
+
# @param opts [Hash] Options hash
|
|
252
|
+
# @return [Array<String>] List of unreleased version tags (e.g. ['v1.0.1'])
|
|
253
|
+
# @raise [RunError] If running git ls-remote or git tag fails
|
|
226
254
|
def getUnreleasedTags(opts)
|
|
227
255
|
remote_tags=runGit("ls-remote --tags #{@stable_repo} |
|
|
228
256
|
grep -E 'refs/tags/v[0-9.]*$'").split("\n").map(){
|
|
@@ -233,6 +261,12 @@ module GitMaintain
|
|
|
233
261
|
new_tags = local_tags - remote_tags
|
|
234
262
|
return new_tags
|
|
235
263
|
end
|
|
264
|
+
|
|
265
|
+
# Generate and transmit or save a release announcement email.
|
|
266
|
+
#
|
|
267
|
+
# @param opts [Hash] Options hash
|
|
268
|
+
# @param new_tags [Array<String>] List of newly released tags
|
|
269
|
+
# @raise [RunError] If running git show or other execution commands fails
|
|
236
270
|
def genReleaseNotif(opts, new_tags)
|
|
237
271
|
return if @NOTIFY_RELEASE == false
|
|
238
272
|
|
|
@@ -281,12 +315,25 @@ module GitMaintain
|
|
|
281
315
|
end
|
|
282
316
|
run("rm -f #{mail_path}")
|
|
283
317
|
end
|
|
318
|
+
|
|
319
|
+
# Submit release tags by delegating to `createRelease`.
|
|
320
|
+
#
|
|
321
|
+
# @param opts [Hash] Options hash
|
|
322
|
+
# @param new_tags [Array<String>] List of release tags to submit
|
|
323
|
+
# @raise [GitMaintainError] If submitting release fails
|
|
284
324
|
def submitReleases(opts, new_tags)
|
|
285
325
|
new_tags.each(){|tag|
|
|
286
326
|
createRelease(opts, tag)
|
|
287
327
|
}
|
|
288
328
|
end
|
|
289
329
|
|
|
330
|
+
# Create a release for the specified tag on GitHub.
|
|
331
|
+
# Pushes the tag to the remote stable repository and optionally calls GitHub release API.
|
|
332
|
+
#
|
|
333
|
+
# @param opts [Hash] Options hash
|
|
334
|
+
# @param tag [String] Release tag name (e.g. 'v1.0.1')
|
|
335
|
+
# @param github_rel [Boolean] True to create a GitHub release via API
|
|
336
|
+
# @raise [GitMaintainError] If git pushing or API release creation fails
|
|
290
337
|
def createRelease(opts, tag, github_rel=true)
|
|
291
338
|
log(:INFO, "Creating a release for #{tag}")
|
|
292
339
|
runGit("push #{@stable_repo} refs/tags/#{tag}")
|
|
@@ -302,19 +349,33 @@ module GitMaintain
|
|
|
302
349
|
end
|
|
303
350
|
end
|
|
304
351
|
|
|
352
|
+
# Map a version number and suffix to a local branch name.
|
|
353
|
+
#
|
|
354
|
+
# @param version [String] Version string (e.g., '1.0')
|
|
355
|
+
# @param suff [String] Suffix string
|
|
356
|
+
# @return [String] Local branch name (e.g., 'stable/1.0/master')
|
|
305
357
|
def versionToLocalBranch(version, suff)
|
|
306
358
|
return @branch_format_raw.gsub(/\\\//, '/').
|
|
307
359
|
gsub(/\(.*\)/, version) + "/#{suff}"
|
|
308
360
|
end
|
|
309
361
|
|
|
362
|
+
# Map a version number to a stable branch name.
|
|
363
|
+
#
|
|
364
|
+
# @param version [String] Version string
|
|
365
|
+
# @return [String] Stable branch name
|
|
310
366
|
def versionToStableBranch(version)
|
|
311
367
|
return version.gsub(/^(.*)$/, @stable_branch_format)
|
|
312
368
|
end
|
|
313
369
|
|
|
370
|
+
# Resolve the stable base commit/reference for a given branch.
|
|
371
|
+
#
|
|
372
|
+
# @param branch [String] Local branch name
|
|
373
|
+
# @return [String] Resolvable stable base reference
|
|
374
|
+
# @raise [GitMaintainError] If no stable base can be resolved for the branch
|
|
314
375
|
def findStableBase(branch)
|
|
315
376
|
base=nil
|
|
316
377
|
if branch =~ @branch_format then
|
|
317
|
-
base = branch.gsub(
|
|
378
|
+
base = branch.gsub(Regexp.new("^\\*?\\s*#{@branch_format_raw}/.*$"), @stable_base_format)
|
|
318
379
|
end
|
|
319
380
|
|
|
320
381
|
@stable_base_patterns.each(){|pattern, b|
|
|
@@ -323,16 +384,30 @@ module GitMaintain
|
|
|
323
384
|
break
|
|
324
385
|
end
|
|
325
386
|
}
|
|
326
|
-
raise("Could not a find a stable base for branch #{branch}") if base == nil
|
|
387
|
+
raise GitMaintainError.new("Could not a find a stable base for branch #{branch}") if base == nil
|
|
327
388
|
return base
|
|
328
389
|
end
|
|
329
390
|
|
|
391
|
+
# Print the local stable branches list to standard output.
|
|
392
|
+
#
|
|
393
|
+
# @param opts [Hash] Options hash
|
|
394
|
+
# @raise [RunError] If running git branch fails
|
|
330
395
|
def list_branches(opts)
|
|
331
396
|
puts getBranchList(opts[:br_suff])
|
|
332
397
|
end
|
|
398
|
+
|
|
399
|
+
# Print all detected branch suffixes to standard output.
|
|
400
|
+
#
|
|
401
|
+
# @param opts [Hash] Options hash
|
|
402
|
+
# @raise [RunError] If running git branch fails
|
|
333
403
|
def list_suffixes(opts)
|
|
334
404
|
puts getSuffixList()
|
|
335
405
|
end
|
|
406
|
+
|
|
407
|
+
# Find and submit any unreleased tags, generating announcements and creating GitHub releases.
|
|
408
|
+
#
|
|
409
|
+
# @param opts [Hash] Options hash
|
|
410
|
+
# @raise [GitMaintainError] If the release submission is aborted or fails
|
|
336
411
|
def submit_release(opts)
|
|
337
412
|
new_tags = getUnreleasedTags(opts)
|
|
338
413
|
if new_tags.empty? then
|
|
@@ -341,9 +416,9 @@ module GitMaintain
|
|
|
341
416
|
end
|
|
342
417
|
|
|
343
418
|
log(:WARNING, "This will officially release these tags: #{new_tags.join(", ")}")
|
|
344
|
-
rep =
|
|
419
|
+
rep = confirm(opts, "release them", true)
|
|
345
420
|
if rep != 'y' then
|
|
346
|
-
raise "Aborting.."
|
|
421
|
+
raise GitMaintainError.new("Aborting..")
|
|
347
422
|
end
|
|
348
423
|
|
|
349
424
|
if @NOTIFY_RELEASE != false
|
|
@@ -351,12 +426,17 @@ module GitMaintain
|
|
|
351
426
|
end
|
|
352
427
|
|
|
353
428
|
log(:WARNING, "Last chance to cancel before submitting")
|
|
354
|
-
rep=
|
|
429
|
+
rep= confirm(opts, "submit these releases", true)
|
|
355
430
|
if rep != 'y' then
|
|
356
|
-
raise "Aborting.."
|
|
431
|
+
raise GitMaintainError.new("Aborting..")
|
|
357
432
|
end
|
|
358
433
|
submitReleases(opts, new_tags)
|
|
359
434
|
end
|
|
435
|
+
|
|
436
|
+
# Print a diagnostic summary of the repository configuration and detected local/upstream branches.
|
|
437
|
+
#
|
|
438
|
+
# @param opts [Hash] Options hash
|
|
439
|
+
# @raise [RunError] If running git commands fails
|
|
360
440
|
def summary(opts)
|
|
361
441
|
log(:INFO, "Configuration summary:")
|
|
362
442
|
if self.class != GitMaintain::Repo then
|
|
@@ -409,12 +489,18 @@ module GitMaintain
|
|
|
409
489
|
}
|
|
410
490
|
end
|
|
411
491
|
end
|
|
492
|
+
|
|
493
|
+
# Search remote stable branches for alternative commit SHAs with the same commit subject.
|
|
494
|
+
#
|
|
495
|
+
# @param commit [String] Original commit SHA
|
|
496
|
+
# @return [Array<String>] List of alternative commit SHAs with identical subject
|
|
497
|
+
# @raise [RunError] If running git commands fails
|
|
412
498
|
def find_alts(commit)
|
|
413
499
|
alts=[]
|
|
414
500
|
|
|
415
501
|
begin
|
|
416
502
|
subj=runGit("log -1 --pretty='%s' #{commit}")
|
|
417
|
-
rescue
|
|
503
|
+
rescue RunError
|
|
418
504
|
return []
|
|
419
505
|
end
|
|
420
506
|
|
|
@@ -430,49 +516,58 @@ module GitMaintain
|
|
|
430
516
|
return alts
|
|
431
517
|
end
|
|
432
518
|
|
|
519
|
+
# Get or initialize the Octokit GitHub API client.
|
|
433
520
|
#
|
|
434
|
-
#
|
|
521
|
+
# @return [Octokit::Client] The initialized API client instance
|
|
522
|
+
def api
|
|
523
|
+
@api ||= Octokit::Client.new(:access_token => token, :auto_paginate => true)
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# Get or fetch the cached GitHub OAuth API token.
|
|
435
527
|
#
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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")
|
|
528
|
+
# @return [String] The API token string
|
|
529
|
+
def token
|
|
530
|
+
@token ||= begin
|
|
531
|
+
# We cannot use the 'defaults' functionality of git_config here,
|
|
532
|
+
# because get_new_token would be evaluated before git_config ran
|
|
533
|
+
tok = getGitConfig("maintain.api-token")
|
|
445
534
|
tok.to_s() == "" ? get_new_token : tok
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# Interactively prompt the user for GitHub credentials to create and save a new OAuth token.
|
|
539
|
+
# Supports two-factor authentication (OneTimePasswordRequired).
|
|
540
|
+
#
|
|
541
|
+
# @return [String] The newly generated API token
|
|
542
|
+
# @raise [Octokit::Unauthorized] If username/password is incorrect
|
|
543
|
+
def get_new_token
|
|
544
|
+
puts "Requesting a new OAuth token from Github..."
|
|
545
|
+
print "Github username: "
|
|
546
|
+
user = $stdin.gets.chomp
|
|
547
|
+
print "Github password: "
|
|
548
|
+
pass = $stdin.noecho(&:gets).chomp
|
|
549
|
+
puts
|
|
550
|
+
|
|
551
|
+
api = Octokit::Client.new(:login => user, :password => pass)
|
|
552
|
+
|
|
553
|
+
begin
|
|
554
|
+
res = api.create_authorization(:scopes => [:repo], :note => "git-maintain")
|
|
555
|
+
rescue Octokit::Unauthorized
|
|
556
|
+
puts "Username or password incorrect. Please try again."
|
|
557
|
+
return get_new_token
|
|
463
558
|
rescue Octokit::OneTimePasswordRequired
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
559
|
+
print "Github OTP: "
|
|
560
|
+
otp = $stdin.noecho(&:gets).chomp
|
|
561
|
+
res = api.create_authorization(:scopes => [:repo], :note => "git-maintain",
|
|
467
562
|
:headers => {"X-GitHub-OTP" => otp})
|
|
468
|
-
|
|
563
|
+
end
|
|
469
564
|
|
|
470
|
-
|
|
471
|
-
|
|
565
|
+
token = res[:token]
|
|
566
|
+
runGit("config --global maintain.api-token '#{token}'")
|
|
472
567
|
|
|
473
568
|
# Now reopen with the token so OTP does not bother us
|
|
474
569
|
@api=nil
|
|
475
570
|
token
|
|
476
|
-
|
|
571
|
+
end
|
|
477
572
|
end
|
|
478
573
|
end
|