git-maintain 0.7.0 → 0.9.0rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -0
- data/LICENSE +674 -22
- data/README.md +26 -5
- data/bin/git-maintain +8 -5
- data/lib/addons/RDMACore.rb +67 -7
- data/lib/addons/git-maintain.rb +66 -0
- data/lib/azure.rb +98 -0
- data/lib/branch.rb +141 -78
- data/lib/ci.rb +88 -0
- data/lib/common.rb +29 -16
- data/lib/repo.rb +143 -72
- data/lib/travis.rb +19 -62
- metadata +19 -10
data/README.md
CHANGED
@@ -16,9 +16,9 @@ The idea is to script most of the maintenance tasks so the maintainer can focus
|
|
16
16
|
- **list_stable**: List commit present in the stable branch but not in the latest associated relase
|
17
17
|
- **merge**: Merge branch with suffix specified in -m <suff> into the main branch
|
18
18
|
- **push**: Push branches to github for validation
|
19
|
-
- **monitor**: Check the
|
19
|
+
- **monitor**: Check the CI state of all branches
|
20
20
|
- **push_stable**: Push to stable repo
|
21
|
-
- **monitor_stable**: Check the
|
21
|
+
- **monitor_stable**: Check the CI state of all stable branches
|
22
22
|
- **release**: Create new release on all concerned branches
|
23
23
|
- **reset**: Reset branch against upstream
|
24
24
|
- **submit_release**: Push the to stable and create the release packages
|
@@ -157,7 +157,7 @@ Apply them to the appropriate branches
|
|
157
157
|
|
158
158
|
```git maintain cp -s deadbeef --version '1[789]'```
|
159
159
|
|
160
|
-
And push them to my own github repo so that
|
160
|
+
And push them to my own github repo so that CI will check everything out
|
161
161
|
|
162
162
|
```git maintain push --version '1[789]'```
|
163
163
|
|
@@ -179,7 +179,7 @@ Push it to my own github too.
|
|
179
179
|
|
180
180
|
```git maintain push --version '1[789]' -b pending```
|
181
181
|
|
182
|
-
Once this gets accepted (and
|
182
|
+
Once this gets accepted (and CI is OK too), I merge this branch back to my 'master'
|
183
183
|
|
184
184
|
```git maintain merge --version '1[789]' -m pending```
|
185
185
|
|
@@ -239,7 +239,7 @@ The tag will not have been propagated anywhere else and can be deleted manually.
|
|
239
239
|
|
240
240
|
```git maintain push_stable --version '1[789]'```
|
241
241
|
|
242
|
-
You can then monitor the status on
|
242
|
+
You can then monitor the status on CI
|
243
243
|
|
244
244
|
```git maintain monitor_stable --version '1[789]'```
|
245
245
|
|
@@ -258,6 +258,8 @@ Enjoy, and feel free to report bugs, missing features and/or send patches
|
|
258
258
|
|
259
259
|
This is a summary of all the settings that can be set in the git config:
|
260
260
|
|
261
|
+
- `maintain.valid-repo`: Remote github repo to test out branches before submitting. Default = `github`
|
262
|
+
- `maintain.stable-repo`: Remote stable github repository t submit validated branches and new releases. Default = `stable`
|
261
263
|
- `maintain.autofetch`: Enable/Disable auto fetching.
|
262
264
|
Can be overriden by the --[no-]fetch option on the CLI.
|
263
265
|
If unset, autofetch is enabled
|
@@ -274,3 +276,22 @@ This is a summary of all the settings that can be set in the git config:
|
|
274
276
|
- `maintain.mail-format`: Specify how release annoucement emails are sent. Can be:
|
275
277
|
- `imap_send`: Store prepared email in an IMAP folder. See `main git-imap-send` for more infos. This is the default value.
|
276
278
|
- `send_email`: Generates a file which is compatible with git send-email
|
279
|
+
|
280
|
+
# License
|
281
|
+
|
282
|
+
Unless otherwise stated, everything in this repo is covered by the following
|
283
|
+
copyright notice:
|
284
|
+
|
285
|
+
Copyright (c) 2022 SUSE
|
286
|
+
|
287
|
+
This program is free software: you can redistribute it and/or modify it
|
288
|
+
under the terms of the GNU General Public License version 3, as
|
289
|
+
published by the Free Software Foundation.
|
290
|
+
|
291
|
+
This program is distributed in the hope that it will be useful,
|
292
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
293
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
294
|
+
GNU General Public License for more details.
|
295
|
+
|
296
|
+
You should have received a copy of the GNU General Public License
|
297
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/bin/git-maintain
CHANGED
@@ -11,17 +11,17 @@ $LOAD_PATH.pop()
|
|
11
11
|
|
12
12
|
opts = {
|
13
13
|
:br_suff => "master",
|
14
|
-
:
|
14
|
+
:yn_default => nil,
|
15
15
|
}
|
16
|
-
|
16
|
+
ACTION_HELPS = GitMaintain::getActionAttr("ACTION_HELP")
|
17
17
|
actionParser = OptionParser.new(nil, 60)
|
18
18
|
actionParser.banner = "Usage: #{__FILE__} <action> [action options]"
|
19
19
|
actionParser.separator ""
|
20
20
|
actionParser.separator "Options:"
|
21
21
|
actionParser.on("-h", "--help", "Display usage.") { |val| puts actionParser.to_s; exit 0 }
|
22
22
|
actionParser.separator "Possible actions:"
|
23
|
-
|
24
|
-
actionParser.separator "\t " + x
|
23
|
+
ACTION_HELPS.each(){|k, x|
|
24
|
+
actionParser.separator "\t * " + k.to_s() + ": " + x
|
25
25
|
}
|
26
26
|
rest = actionParser.order!(ARGV);
|
27
27
|
if rest.length <= 0 then
|
@@ -38,13 +38,16 @@ ARGV.shift()
|
|
38
38
|
|
39
39
|
optsParser = OptionParser.new(nil, 60)
|
40
40
|
optsParser.banner = "Usage: #{__FILE__} #{action_s} "
|
41
|
+
optsParser.separator "# " + ACTION_HELPS[action].to_s()
|
41
42
|
optsParser.separator ""
|
42
43
|
optsParser.separator "Options:"
|
43
44
|
optsParser.on("-h", "--help", "Display usage.") { |val| puts optsParser.to_s; exit 0 }
|
44
45
|
optsParser.on("-b", "--branch-suffix [SUFFIX]", "Branch suffix. Default is 'master'.") {
|
45
46
|
|val| opts[:br_suff] = val}
|
46
47
|
optsParser.on("-n", "--no", "Assume no to all questions.") {
|
47
|
-
|val| opts[:
|
48
|
+
|val| opts[:yn_default] = :no}
|
49
|
+
optsParser.on("-y", "--yes", "Assume yes to all questions.") {
|
50
|
+
|val| opts[:yn_default] = :yes}
|
48
51
|
optsParser.on("--verbose", "Displays more informations.") {
|
49
52
|
|val| GitMaintain::setVerbose(true)}
|
50
53
|
GitMaintain::setOpts(action, optsParser, opts)
|
data/lib/addons/RDMACore.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module GitMaintain
|
2
2
|
class RDMACoreBranch < Branch
|
3
3
|
REPO_NAME = "rdma-core"
|
4
|
+
AZURE_MIN_VERSION = 18
|
4
5
|
|
5
6
|
def self.set_opts(action, optsParser, opts)
|
6
7
|
opts[:rel_type] = nil
|
@@ -15,8 +16,14 @@ module GitMaintain
|
|
15
16
|
end
|
16
17
|
def self.check_opts(opts)
|
17
18
|
if opts[:action] == :release then
|
18
|
-
|
19
|
+
case opts[:rel_type]
|
20
|
+
when nil
|
19
21
|
raise "No release type specified use --stable or --major"
|
22
|
+
when :major
|
23
|
+
if opts[:manual_branch] == nil then
|
24
|
+
GitMaintain::log(:INFO, "Major release selected. Auto-forcing branch to master")
|
25
|
+
opts[:manual_branch] = "master"
|
26
|
+
end
|
20
27
|
end
|
21
28
|
end
|
22
29
|
end
|
@@ -64,16 +71,19 @@ module GitMaintain
|
|
64
71
|
tag_file.puts `git log HEAD ^#{git_prev_ver} --no-merges --format=' * %s'`
|
65
72
|
tag_file.close()
|
66
73
|
|
74
|
+
edit_flag = ""
|
75
|
+
edit_flag = "--edit" if opts[:no_edit] == false
|
76
|
+
|
67
77
|
if opts[:rel_type] == :major
|
68
78
|
# For major, tag the current version first
|
69
|
-
@repo.runGitInteractive("tag -a -s v#{rel_ver}
|
79
|
+
@repo.runGitInteractive("tag -a -s v#{rel_ver} #{edit_flag} -F #{tag_path}")
|
70
80
|
if $? != 0 then
|
71
81
|
raise("Failed to tag branch #{local_branch}")
|
72
82
|
end
|
73
83
|
end
|
74
84
|
|
75
85
|
# Update version number in relevant files
|
76
|
-
@repo.run("sed -i -e 's/\\(Version:[[:space:]]*\\)[0-9.]\\+/\\1#{new_ver}/g'
|
86
|
+
@repo.run("sed -i -e 's/\\(Version:[[:space:]]*\\)[0-9.]\\+/\\1#{new_ver}/g' */*.spec")
|
77
87
|
@repo.run("sed -i -e 's/\\([sS][eE][tT](PACKAGE_VERSION[[:space:]]*\"\\)[0-9.]*\"/\\1#{new_ver}\"/g' CMakeLists.txt")
|
78
88
|
|
79
89
|
case opts[:rel_type]
|
@@ -93,14 +103,14 @@ mv debian/changelog.new debian/changelog")
|
|
93
103
|
end
|
94
104
|
|
95
105
|
# Add and commit
|
96
|
-
@repo.runGit("add
|
97
|
-
@repo.runGitInteractive("commit -m '#{commit_msg} #{new_ver}' --verbose
|
106
|
+
@repo.runGit("add */*.spec CMakeLists.txt debian/changelog")
|
107
|
+
@repo.runGitInteractive("commit -m '#{commit_msg} #{new_ver}' --verbose #{edit_flag} --signoff")
|
98
108
|
if $? != 0 then
|
99
109
|
raise("Failed to commit on branch #{local_branch}")
|
100
110
|
end
|
101
111
|
|
102
112
|
if opts[:rel_type] == :stable
|
103
|
-
@repo.runGitInteractive("tag -a -s v#{rel_ver}
|
113
|
+
@repo.runGitInteractive("tag -a -s v#{rel_ver} #{edit_flag} -F #{tag_path}")
|
104
114
|
if $? != 0 then
|
105
115
|
raise("Failed to tag branch #{local_branch}")
|
106
116
|
end
|
@@ -108,7 +118,57 @@ mv debian/changelog.new debian/changelog")
|
|
108
118
|
`rm -f #{tag_path}`
|
109
119
|
end
|
110
120
|
end
|
121
|
+
class RDMACoreRepo < Repo
|
122
|
+
AZURE_MIN_VERSION = 18
|
123
|
+
def submitReleases(opts, new_tags)
|
124
|
+
new_tags.each(){|tag|
|
125
|
+
next if tag !~ /v([0-9]*)\.[0-9]*/
|
126
|
+
major=$1.to_i
|
127
|
+
# Starting from v27, do not create the github release ourself as this is done by Azure
|
128
|
+
createRelease(opts, tag, major < AZURE_MIN_VERSION)
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class RDMACoreCI < CI
|
134
|
+
AZURE_MIN_VERSION = 18
|
135
|
+
def initialize(repo)
|
136
|
+
super(repo)
|
137
|
+
@travis = GitMaintain::TravisCI.new(repo)
|
138
|
+
@azure = GitMaintain::AzureCI.new(repo, 'ucfconsort', 'ucfconsort')
|
111
139
|
|
140
|
+
# Auto generate all CI required methods
|
141
|
+
# Wicked ruby tricker to find all the public methods of CI but not of inherited classes
|
142
|
+
# to dynamically define these method in the object being created
|
143
|
+
(GitMaintain::CI.new(repo).public_methods() - Object.new.public_methods()).each(){|method|
|
144
|
+
# Skip specific emptyCache method
|
145
|
+
next if method == :emptyCache
|
146
|
+
|
147
|
+
self.define_singleton_method(method) { |br, *args|
|
148
|
+
if br.version =~ /([0-9]+)/
|
149
|
+
major=$1.to_i
|
150
|
+
elsif br.version == "master"
|
151
|
+
major=99999
|
152
|
+
else
|
153
|
+
raise("Unable to monitor branch #{br} on a CI")
|
154
|
+
end
|
155
|
+
if major < AZURE_MIN_VERSION
|
156
|
+
@travis.send(method, br, *args)
|
157
|
+
else
|
158
|
+
@azure.send(method, br, *args)
|
159
|
+
end
|
160
|
+
}
|
161
|
+
}
|
162
|
+
end
|
163
|
+
def emptyCache()
|
164
|
+
@travis.emptyCache()
|
165
|
+
@azure.emptyCache()
|
166
|
+
end
|
167
|
+
end
|
112
168
|
GitMaintain::registerCustom(RDMACoreBranch::REPO_NAME,
|
113
|
-
{
|
169
|
+
{
|
170
|
+
GitMaintain::Branch => RDMACoreBranch,
|
171
|
+
GitMaintain::Repo => RDMACoreRepo,
|
172
|
+
GitMaintain::CI => RDMACoreCI,
|
173
|
+
})
|
114
174
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module GitMaintain
|
2
|
+
class GitMaintainBranch < Branch
|
3
|
+
REPO_NAME = "git-maintain"
|
4
|
+
|
5
|
+
def release(opts)
|
6
|
+
prev_ver=@repo.runGit("show HEAD:CHANGELOG | grep -A 1 -- '---------' | head -n 2 | tail -n 1 | awk '{ print $1}'").chomp()
|
7
|
+
ver_nums = prev_ver.split(".")
|
8
|
+
|
9
|
+
if opts[:manual_branch] == nil then
|
10
|
+
new_ver = (ver_nums[0 .. -2] + [ver_nums[-1].to_i() + 1 ]).join(".")
|
11
|
+
git_prev_ver = "v" + (ver_nums[-1] == "0" ? ver_nums[0 .. -2].join(".") : prev_ver)
|
12
|
+
else
|
13
|
+
new_ver = (ver_nums[0 .. -3] + [ver_nums[-2].to_i() + 1 ] + [ "0" ]).join(".")
|
14
|
+
git_prev_ver = "v" + prev_ver
|
15
|
+
end
|
16
|
+
|
17
|
+
changes=@repo.runGit("show HEAD:CHANGELOG | awk ' BEGIN {count=0} {if ($1 == \"------------------\") count++; if (count == 0) print $0}'")
|
18
|
+
|
19
|
+
puts "Preparing release #{prev_ver} => #{new_ver}"
|
20
|
+
rep = GitMaintain::checkLog(opts, @local_branch, git_prev_ver, "release")
|
21
|
+
if rep != "y" then
|
22
|
+
puts "Skipping release"
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
# Prepare tag message
|
27
|
+
tag_path=`mktemp`.chomp()
|
28
|
+
puts tag_path
|
29
|
+
tag_file = File.open(tag_path, "w+")
|
30
|
+
tag_file.puts "git-maintain-#{new_ver}"
|
31
|
+
tag_file.puts ""
|
32
|
+
tag_file.puts changes
|
33
|
+
tag_file.close()
|
34
|
+
|
35
|
+
@repo.run("cat <<EOF > CHANGELOG.new
|
36
|
+
------------------
|
37
|
+
#{new_ver} #{`date '+ (%Y-%m-%d)'`.chomp()}
|
38
|
+
------------------
|
39
|
+
|
40
|
+
$(cat CHANGELOG)
|
41
|
+
EOF
|
42
|
+
mv CHANGELOG.new CHANGELOG")
|
43
|
+
|
44
|
+
# Add and commit
|
45
|
+
@repo.runGit("add CHANGELOG")
|
46
|
+
@repo.runGitInteractive("commit -F #{tag_path} --verbose --edit --signoff")
|
47
|
+
if $? != 0 then
|
48
|
+
raise("Failed to commit on branch #{local_branch}")
|
49
|
+
end
|
50
|
+
@repo.runGitInteractive("tag -a -s v#{new_ver} --edit -F #{tag_path}")
|
51
|
+
if $? != 0 then
|
52
|
+
raise("Failed to tag branch #{local_branch}")
|
53
|
+
end
|
54
|
+
`rm -f #{tag_path}`
|
55
|
+
end
|
56
|
+
end
|
57
|
+
class GitMaintainRepo < Repo
|
58
|
+
def initialize(path)
|
59
|
+
super(path)
|
60
|
+
@NOTIFY_RELEASE = false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
GitMaintain::registerCustom(GitMaintainBranch::REPO_NAME,
|
64
|
+
{ GitMaintain::Branch => GitMaintainBranch,
|
65
|
+
GitMaintain::Repo => GitMaintainRepo})
|
66
|
+
end
|
data/lib/azure.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module GitMaintain
|
2
|
+
class AzureCI < CI
|
3
|
+
AZURE_URL='https://dev.azure.com/'
|
4
|
+
|
5
|
+
def initialize(repo, stable='', valid='')
|
6
|
+
super(repo)
|
7
|
+
@url = AZURE_URL
|
8
|
+
@stable_org=stable
|
9
|
+
@valid_org=valid
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def getState(sha1, resp)
|
14
|
+
br = findBranch(sha1, resp)
|
15
|
+
return "not found" if br == nil
|
16
|
+
return "running" if br["result"] == nil
|
17
|
+
return br["result"].to_s()
|
18
|
+
end
|
19
|
+
def getLog(sha1, resp)
|
20
|
+
str=""
|
21
|
+
# br = findBranch(sha1, resp)
|
22
|
+
# raise("Travis build not found") if br == nil
|
23
|
+
# job_id = br["id"].to_s()
|
24
|
+
# logs= getJson(@url, "azure_log_list" + job_id,
|
25
|
+
# @repo.name + "/_apis/build/builds/#{job_id}/logs?api-version=5.1")
|
26
|
+
# 1.upto(logs["count"]) { |x|
|
27
|
+
# log(:DEBUG_CI, "Downloading log file #{x}/#{logs["count"]}")
|
28
|
+
# nzstr = getJson(@url, "azure_log_" + job_id + '_' + x.to_s(),
|
29
|
+
# @repo.name + "/_apis/build/builds/#{job_id}/logs/#{x}?api-version=5.1", false)
|
30
|
+
# # This is zipped. We need to extract it
|
31
|
+
# }
|
32
|
+
return str
|
33
|
+
end
|
34
|
+
def getTS(sha1, resp)
|
35
|
+
br = findBranch(sha1, resp)
|
36
|
+
raise("Travis build not found") if br == nil
|
37
|
+
return br["started_at"]
|
38
|
+
end
|
39
|
+
def checkState(sha1, resp)
|
40
|
+
st = getState(sha1, resp)
|
41
|
+
return st == "passed" || st == "succeeded"
|
42
|
+
end
|
43
|
+
|
44
|
+
def getBrValidJson()
|
45
|
+
raise("Validation organisation not provided") if @valid_org == ''
|
46
|
+
return getJson(@url + @valid_org + '/',
|
47
|
+
:azure_br_valid, @repo.name + '/_apis/build/builds?api-version=5.1')
|
48
|
+
end
|
49
|
+
def getBrStableJson()
|
50
|
+
raise("Stable organisation not provided") if @stable_org == ''
|
51
|
+
return getJson(@url + @stable_org + '/',
|
52
|
+
:azure_br_stable, @repo.name + '/_apis/build/builds?api-version=5.1')
|
53
|
+
end
|
54
|
+
def findBranch(sha1, resp)
|
55
|
+
log(:DEBUG_CI, "Looking for build for #{sha1}")
|
56
|
+
resp["value"].each(){|br|
|
57
|
+
commit= br["sourceVersion"]
|
58
|
+
raise("Incomplete JSON received from Travis") if commit == nil
|
59
|
+
log(:DEBUG_CI, "Found entry for sha #{commit}")
|
60
|
+
next if commit != sha1
|
61
|
+
return br
|
62
|
+
}
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
public
|
67
|
+
def getValidState(br, sha1)
|
68
|
+
return getState(sha1, getBrValidJson())
|
69
|
+
end
|
70
|
+
def checkValidState(br, sha1)
|
71
|
+
return checkState(sha1, getBrValidJson())
|
72
|
+
end
|
73
|
+
def getValidLog(br, sha1)
|
74
|
+
return getLog(sha1, getBrValidJson())
|
75
|
+
end
|
76
|
+
def getValidTS(br, sha1)
|
77
|
+
return getTS(sha1, getBrValidJson())
|
78
|
+
end
|
79
|
+
|
80
|
+
def getStableState(br, sha1)
|
81
|
+
return getState(sha1, getBrStableJson())
|
82
|
+
end
|
83
|
+
def checkStableState(br, sha1)
|
84
|
+
return checkState(sha1, getBrStableJson())
|
85
|
+
end
|
86
|
+
def getStableLog(br, sha1)
|
87
|
+
return getLog(sha1, getBrStableJson())
|
88
|
+
end
|
89
|
+
def getStableTS(br, sha1)
|
90
|
+
return getTS(sha1, getBrStableJson())
|
91
|
+
end
|
92
|
+
def isErrored(br, status)
|
93
|
+
# https://docs.microsoft.com/en-us/rest/api/azure/devops/build/builds/list?
|
94
|
+
# view=azure-devops-rest-5.1#buildresult
|
95
|
+
return status == "failed"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|