blob: 57e18ac38f977b5eff67447f071b884db0cb7406 [file] [log] [blame]
Brad Bishop35982432018-08-30 17:27:20 -04001#!/usr/bin/env python3
2
3# Contributors Listed Below - COPYRIGHT 2018
4# [+] International Business Machines Corp.
5#
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16# implied. See the License for the specific language governing
17# permissions and limitations under the License.
18
19import argparse
20import os
Brad Bishop35982432018-08-30 17:27:20 -040021import sys
22
Patrick Williamse310dd92022-12-07 06:55:38 -060023import sh
24
25git = sh.git.bake("--no-pager")
Brad Bishop35982432018-08-30 17:27:20 -040026
27
28def log(msg, args):
29 if args.noisy:
Patrick Williamse310dd92022-12-07 06:55:38 -060030 sys.stderr.write("{}\n".format(msg))
Brad Bishop35982432018-08-30 17:27:20 -040031
32
33def git_clone_or_reset(local_name, remote, args):
34 if not os.path.exists(local_name):
Patrick Williamse310dd92022-12-07 06:55:38 -060035 log("cloning into {}...".format(local_name), args)
Brad Bishop35982432018-08-30 17:27:20 -040036 git.clone(remote, local_name)
37 else:
Patrick Williamse310dd92022-12-07 06:55:38 -060038 log("{} exists, updating...".format(local_name), args)
Brad Bishop35982432018-08-30 17:27:20 -040039 git.fetch(_cwd=local_name)
Patrick Williamse310dd92022-12-07 06:55:38 -060040 git.reset("--hard", "FETCH_HEAD", _cwd=local_name)
Brad Bishop35982432018-08-30 17:27:20 -040041
42
Patrick Williamsffb4d522023-01-25 15:08:49 -060043def extract_project_from_uris(uris, args):
Brad Bishop9cfd66e2020-05-28 15:16:36 -040044 # remove SRC_URI = and quotes (does not handle escaped quotes)
45 uris = uris.split('"')[1]
46 for uri in uris.split():
Patrick Williamse310dd92022-12-07 06:55:38 -060047 if "github.com/openbmc" not in uri:
Brad Bishop9cfd66e2020-05-28 15:16:36 -040048 continue
49
Patrick Williamsffb4d522023-01-25 15:08:49 -060050 segments = uri.split(";")
51 branch = args.branch
52
53 for s in segments[1:]:
54 if "branch=" not in s:
55 continue
56 if "nobranch=" in s:
57 continue
58 branch = s.split("=")[1]
59
Brad Bishop9cfd66e2020-05-28 15:16:36 -040060 # remove fetcher arguments
Patrick Williamsffb4d522023-01-25 15:08:49 -060061 uri = segments[0]
Brad Bishop9cfd66e2020-05-28 15:16:36 -040062 # the project is the right-most path segment
Patrick Williamsffb4d522023-01-25 15:08:49 -060063 return (uri.split("/")[-1].replace(".git", ""), branch)
Brad Bishop9cfd66e2020-05-28 15:16:36 -040064
65 return None
66
67
Patrick Williamsffb4d522023-01-25 15:08:49 -060068def extract_sha_from_recipe(recipe, args):
Brad Bishop35982432018-08-30 17:27:20 -040069 with open(recipe) as fp:
Patrick Williamse310dd92022-12-07 06:55:38 -060070 uris = ""
Brad Bishop35982432018-08-30 17:27:20 -040071 project = None
Patrick Williamsffb4d522023-01-25 15:08:49 -060072 branch = None
Brad Bishop35982432018-08-30 17:27:20 -040073 sha = None
74
75 for line in fp:
Brad Bishop9cfd66e2020-05-28 15:16:36 -040076 line = line.rstrip()
Patrick Williamse310dd92022-12-07 06:55:38 -060077 if "SRCREV" in line:
78 sha = line.split("=")[-1].replace('"', "").strip()
79 elif not project and uris or "_URI" in line:
80 uris += line.split("\\")[0]
81 if "\\" not in line:
Brad Bishop9cfd66e2020-05-28 15:16:36 -040082 # In uris we've gathered a complete (possibly multi-line)
83 # assignment to a bitbake variable that ends with _URI.
84 # Try to pull an OpenBMC project out of it.
Patrick Williamsffb4d522023-01-25 15:08:49 -060085 uri = extract_project_from_uris(uris, args)
86 if uri is None:
Brad Bishop9cfd66e2020-05-28 15:16:36 -040087 # We didn't find a project. Unset uris and look for
88 # another bitbake variable that ends with _URI.
Patrick Williamse310dd92022-12-07 06:55:38 -060089 uris = ""
Patrick Williamsffb4d522023-01-25 15:08:49 -060090 else:
91 (project, branch) = uri
Brad Bishop35982432018-08-30 17:27:20 -040092
Patrick Williamsffb4d522023-01-25 15:08:49 -060093 if project and branch and sha:
94 return (project, branch, sha)
Brad Bishop35982432018-08-30 17:27:20 -040095
Patrick Williamse310dd92022-12-07 06:55:38 -060096 raise RuntimeError("No SRCREV or URI found in {}".format(recipe))
Brad Bishop35982432018-08-30 17:27:20 -040097
98
99def find_candidate_recipes(meta, args):
100 remote_fmt_args = (args.ssh_config_host, meta)
Patrick Williamse310dd92022-12-07 06:55:38 -0600101 remote = "ssh://{}/openbmc/{}".format(*remote_fmt_args)
Brad Bishop35982432018-08-30 17:27:20 -0400102 try:
103 git_clone_or_reset(meta, remote, args)
104 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -0600105 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -0400106 return []
107
Patrick Williamse310dd92022-12-07 06:55:38 -0600108 match_suffixes = ("bb", "bbclass", "inc")
109 pathspecs = ("*.{}".format(x) for x in match_suffixes)
Patrick Williamsffb4d522023-01-25 15:08:49 -0600110 grep_args = (
111 "--no-color",
112 "-l",
113 "-e",
114 "_URI",
115 "--and",
116 "-e",
117 "github.com/openbmc",
118 )
Brad Bishop2b6a5462021-01-25 16:06:58 -0500119 grep_args = (*grep_args, *pathspecs)
Brad Bishop35982432018-08-30 17:27:20 -0400120 try:
Patrick Williamse310dd92022-12-07 06:55:38 -0600121 return git.grep(*grep_args, _cwd=meta).stdout.decode("utf-8").split()
Brad Bishop35982432018-08-30 17:27:20 -0400122 except sh.ErrorReturnCode_1:
123 pass
124 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -0600125 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -0400126
127 return []
128
129
130def find_and_process_bumps(meta, args):
131 candidate_recipes = find_candidate_recipes(meta, args)
132
133 for recipe in candidate_recipes:
134 full_recipe_path = os.path.join(meta, recipe)
135 recipe_basename = os.path.basename(full_recipe_path)
Patrick Williamsffb4d522023-01-25 15:08:49 -0600136 project_name, recipe_branch, recipe_sha = extract_sha_from_recipe(
137 full_recipe_path, args
138 )
Brad Bishop35982432018-08-30 17:27:20 -0400139
140 remote_fmt_args = (args.ssh_config_host, project_name)
Patrick Williamse310dd92022-12-07 06:55:38 -0600141 remote = "ssh://{}/openbmc/{}".format(*remote_fmt_args)
Patrick Williamsffb4d522023-01-25 15:08:49 -0600142 ls_remote_args = [remote, "refs/heads/{}".format(recipe_branch)]
Brad Bishop35982432018-08-30 17:27:20 -0400143 try:
Patrick Williamse310dd92022-12-07 06:55:38 -0600144 project_sha = git("ls-remote", *ls_remote_args)
145 project_sha = project_sha.stdout.decode("utf-8").split()[0]
Brad Bishop35982432018-08-30 17:27:20 -0400146 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -0600147 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -0400148 continue
149
150 if project_sha == recipe_sha:
151 message_args = (recipe_basename, recipe_sha[:10])
Patrick Williamse310dd92022-12-07 06:55:38 -0600152 print("{} is up to date ({})".format(*message_args))
Brad Bishop35982432018-08-30 17:27:20 -0400153 continue
154
Patrick Williamse310dd92022-12-07 06:55:38 -0600155 change_id = "autobump {} {} {}".format(recipe, recipe_sha, project_sha)
156 hash_object_args = ["-t", "blob", "--stdin"]
157 change_id = git(sh.echo(change_id), "hash-object", *hash_object_args)
158 change_id = "I{}".format(change_id.strip())
Brad Bishop35982432018-08-30 17:27:20 -0400159
Patrick Williamse310dd92022-12-07 06:55:38 -0600160 query_args = ["query", "change:{}".format(change_id)]
Brad Bishop35982432018-08-30 17:27:20 -0400161 gerrit_query_result = args.gerrit(*query_args)
Patrick Williamse310dd92022-12-07 06:55:38 -0600162 gerrit_query_result = gerrit_query_result.stdout.decode("utf-8")
Brad Bishop35982432018-08-30 17:27:20 -0400163
Patrick Williamse310dd92022-12-07 06:55:38 -0600164 if change_id in gerrit_query_result:
Brad Bishop35982432018-08-30 17:27:20 -0400165 message_args = (recipe_basename, change_id)
Patrick Williamse310dd92022-12-07 06:55:38 -0600166 print("{} {} already exists".format(*message_args))
Brad Bishop35982432018-08-30 17:27:20 -0400167 continue
168
169 message_args = (recipe_basename, recipe_sha[:10], project_sha[:10])
Patrick Williamse310dd92022-12-07 06:55:38 -0600170 print("{} updating from {} to {}".format(*message_args))
Brad Bishop35982432018-08-30 17:27:20 -0400171
172 remote_args = (args.ssh_config_host, project_name)
Patrick Williamse310dd92022-12-07 06:55:38 -0600173 remote = "ssh://{}/openbmc/{}".format(*remote_args)
Brad Bishop35982432018-08-30 17:27:20 -0400174 git_clone_or_reset(project_name, remote, args)
175
176 try:
Patrick Williamse310dd92022-12-07 06:55:38 -0600177 revlist = "{}..{}".format(recipe_sha, project_sha)
Brad Bishop35982432018-08-30 17:27:20 -0400178 shortlog = git.shortlog(revlist, _cwd=project_name)
Patrick Williamse310dd92022-12-07 06:55:38 -0600179 shortlog = shortlog.stdout.decode("utf-8")
Brad Bishop35982432018-08-30 17:27:20 -0400180 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -0600181 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -0400182 continue
183
Patrick Williamse310dd92022-12-07 06:55:38 -0600184 reset_args = ["--hard", "origin/{}".format(args.branch)]
Brad Bishop35982432018-08-30 17:27:20 -0400185 git.reset(*reset_args, _cwd=meta)
186
187 recipe_content = None
188 with open(full_recipe_path) as fd:
189 recipe_content = fd.read()
190
191 recipe_content = recipe_content.replace(recipe_sha, project_sha)
Patrick Williamse310dd92022-12-07 06:55:38 -0600192 with open(full_recipe_path, "w") as fd:
Brad Bishop35982432018-08-30 17:27:20 -0400193 fd.write(recipe_content)
194
195 git.add(recipe, _cwd=meta)
196
197 commit_summary_args = (project_name, recipe_sha[:10], project_sha[:10])
Patrick Williamse310dd92022-12-07 06:55:38 -0600198 commit_msg = "{}: srcrev bump {}..{}".format(*commit_summary_args)
199 commit_msg += "\n\n{}".format(shortlog)
200 commit_msg += "\n\nChange-Id: {}".format(change_id)
Brad Bishop35982432018-08-30 17:27:20 -0400201
Patrick Williamse310dd92022-12-07 06:55:38 -0600202 git.commit(sh.echo(commit_msg), "-s", "-F", "-", _cwd=meta)
Brad Bishop35982432018-08-30 17:27:20 -0400203
Brad Bishop199f7952021-01-25 14:20:49 -0500204 push_args = [
Patrick Williamse310dd92022-12-07 06:55:38 -0600205 "origin",
206 "HEAD:refs/for/{}%topic=autobump".format(args.branch),
Brad Bishop199f7952021-01-25 14:20:49 -0500207 ]
Brad Bishop5d7dcfb2020-05-28 14:39:03 -0400208 if not args.dry_run:
209 git.push(*push_args, _cwd=meta)
Brad Bishop35982432018-08-30 17:27:20 -0400210
211
212def main():
Patrick Williamse310dd92022-12-07 06:55:38 -0600213 app_description = """OpenBMC bitbake recipe bumping tool.
Brad Bishop35982432018-08-30 17:27:20 -0400214
215Find bitbake metadata files (recipes) that use the git fetcher
216and check the project repository for newer revisions.
217
218Generate commits that update bitbake metadata files with SRCREV.
219
220Push generated commits to the OpenBMC Gerrit instance for review.
Patrick Williamse310dd92022-12-07 06:55:38 -0600221 """
Brad Bishop35982432018-08-30 17:27:20 -0400222 parser = argparse.ArgumentParser(
223 description=app_description,
Patrick Williamse310dd92022-12-07 06:55:38 -0600224 formatter_class=argparse.RawDescriptionHelpFormatter,
225 )
Brad Bishop35982432018-08-30 17:27:20 -0400226
Patrick Williamse310dd92022-12-07 06:55:38 -0600227 parser.set_defaults(branch="master")
Brad Bishop35982432018-08-30 17:27:20 -0400228 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600229 "-d",
230 "--dry-run",
231 dest="dry_run",
232 action="store_true",
233 help="perform a dry run only",
234 )
Brad Bishop5d7dcfb2020-05-28 14:39:03 -0400235 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600236 "-m",
237 "--meta-repository",
238 dest="meta_repository",
239 action="append",
240 help="meta repository to check for updates",
241 )
Brad Bishop35982432018-08-30 17:27:20 -0400242 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600243 "-v",
244 "--verbose",
245 dest="noisy",
246 action="store_true",
247 help="enable verbose status messages",
248 )
Brad Bishop35982432018-08-30 17:27:20 -0400249 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600250 "ssh_config_host",
251 metavar="SSH_CONFIG_HOST_ENTRY",
252 help="SSH config host entry for Gerrit connectivity",
253 )
Brad Bishop35982432018-08-30 17:27:20 -0400254
255 args = parser.parse_args()
Patrick Williamse310dd92022-12-07 06:55:38 -0600256 setattr(args, "gerrit", sh.ssh.bake(args.ssh_config_host, "gerrit"))
Brad Bishop35982432018-08-30 17:27:20 -0400257
Patrick Williamse310dd92022-12-07 06:55:38 -0600258 metas = getattr(args, "meta_repository")
Brad Bishop35982432018-08-30 17:27:20 -0400259 if metas is None:
Patrick Williamse310dd92022-12-07 06:55:38 -0600260 metas = args.gerrit("ls-projects", "-m", "meta-")
261 metas = metas.stdout.decode("utf-8").split()
Brad Bishop35982432018-08-30 17:27:20 -0400262 metas = [os.path.split(x)[-1] for x in metas]
263
264 for meta in metas:
265 find_and_process_bumps(meta, args)
266
267
Patrick Williamse310dd92022-12-07 06:55:38 -0600268if __name__ == "__main__":
Brad Bishop35982432018-08-30 17:27:20 -0400269 sys.exit(0 if main() else 1)