blob: ea6e2b82e14623c8f8895ec740dec8ea5f44a4ee [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
Brad Bishop9cfd66e2020-05-28 15:16:36 -040043def extract_project_from_uris(uris):
44 # 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
50 # remove fetcher arguments
Patrick Williamse310dd92022-12-07 06:55:38 -060051 uri = uri.split(";")[0]
Brad Bishop9cfd66e2020-05-28 15:16:36 -040052 # the project is the right-most path segment
Patrick Williamse310dd92022-12-07 06:55:38 -060053 return uri.split("/")[-1].replace(".git", "")
Brad Bishop9cfd66e2020-05-28 15:16:36 -040054
55 return None
56
57
Brad Bishop35982432018-08-30 17:27:20 -040058def extract_sha_from_recipe(recipe):
59 with open(recipe) as fp:
Patrick Williamse310dd92022-12-07 06:55:38 -060060 uris = ""
Brad Bishop35982432018-08-30 17:27:20 -040061 project = None
62 sha = None
63
64 for line in fp:
Brad Bishop9cfd66e2020-05-28 15:16:36 -040065 line = line.rstrip()
Patrick Williamse310dd92022-12-07 06:55:38 -060066 if "SRCREV" in line:
67 sha = line.split("=")[-1].replace('"', "").strip()
68 elif not project and uris or "_URI" in line:
69 uris += line.split("\\")[0]
70 if "\\" not in line:
Brad Bishop9cfd66e2020-05-28 15:16:36 -040071 # In uris we've gathered a complete (possibly multi-line)
72 # assignment to a bitbake variable that ends with _URI.
73 # Try to pull an OpenBMC project out of it.
74 project = extract_project_from_uris(uris)
75 if project is None:
76 # We didn't find a project. Unset uris and look for
77 # another bitbake variable that ends with _URI.
Patrick Williamse310dd92022-12-07 06:55:38 -060078 uris = ""
Brad Bishop35982432018-08-30 17:27:20 -040079
80 if project and sha:
81 return (project, sha)
82
Patrick Williamse310dd92022-12-07 06:55:38 -060083 raise RuntimeError("No SRCREV or URI found in {}".format(recipe))
Brad Bishop35982432018-08-30 17:27:20 -040084
85
86def find_candidate_recipes(meta, args):
87 remote_fmt_args = (args.ssh_config_host, meta)
Patrick Williamse310dd92022-12-07 06:55:38 -060088 remote = "ssh://{}/openbmc/{}".format(*remote_fmt_args)
Brad Bishop35982432018-08-30 17:27:20 -040089 try:
90 git_clone_or_reset(meta, remote, args)
91 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -060092 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -040093 return []
94
Patrick Williamse310dd92022-12-07 06:55:38 -060095 match_suffixes = ("bb", "bbclass", "inc")
96 pathspecs = ("*.{}".format(x) for x in match_suffixes)
97 grep_args = ("-l", "-e", "_URI", "--and", "-e", "github.com/openbmc")
Brad Bishop2b6a5462021-01-25 16:06:58 -050098 grep_args = (*grep_args, *pathspecs)
Brad Bishop35982432018-08-30 17:27:20 -040099 try:
Patrick Williamse310dd92022-12-07 06:55:38 -0600100 return git.grep(*grep_args, _cwd=meta).stdout.decode("utf-8").split()
Brad Bishop35982432018-08-30 17:27:20 -0400101 except sh.ErrorReturnCode_1:
102 pass
103 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -0600104 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -0400105
106 return []
107
108
109def find_and_process_bumps(meta, args):
110 candidate_recipes = find_candidate_recipes(meta, args)
111
112 for recipe in candidate_recipes:
113 full_recipe_path = os.path.join(meta, recipe)
114 recipe_basename = os.path.basename(full_recipe_path)
115 project_name, recipe_sha = extract_sha_from_recipe(full_recipe_path)
116
117 remote_fmt_args = (args.ssh_config_host, project_name)
Patrick Williamse310dd92022-12-07 06:55:38 -0600118 remote = "ssh://{}/openbmc/{}".format(*remote_fmt_args)
119 ls_remote_args = [remote, "refs/heads/{}".format(args.branch)]
Brad Bishop35982432018-08-30 17:27:20 -0400120 try:
Patrick Williamse310dd92022-12-07 06:55:38 -0600121 project_sha = git("ls-remote", *ls_remote_args)
122 project_sha = project_sha.stdout.decode("utf-8").split()[0]
Brad Bishop35982432018-08-30 17:27:20 -0400123 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -0600124 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -0400125 continue
126
127 if project_sha == recipe_sha:
128 message_args = (recipe_basename, recipe_sha[:10])
Patrick Williamse310dd92022-12-07 06:55:38 -0600129 print("{} is up to date ({})".format(*message_args))
Brad Bishop35982432018-08-30 17:27:20 -0400130 continue
131
Patrick Williamse310dd92022-12-07 06:55:38 -0600132 change_id = "autobump {} {} {}".format(recipe, recipe_sha, project_sha)
133 hash_object_args = ["-t", "blob", "--stdin"]
134 change_id = git(sh.echo(change_id), "hash-object", *hash_object_args)
135 change_id = "I{}".format(change_id.strip())
Brad Bishop35982432018-08-30 17:27:20 -0400136
Patrick Williamse310dd92022-12-07 06:55:38 -0600137 query_args = ["query", "change:{}".format(change_id)]
Brad Bishop35982432018-08-30 17:27:20 -0400138 gerrit_query_result = args.gerrit(*query_args)
Patrick Williamse310dd92022-12-07 06:55:38 -0600139 gerrit_query_result = gerrit_query_result.stdout.decode("utf-8")
Brad Bishop35982432018-08-30 17:27:20 -0400140
Patrick Williamse310dd92022-12-07 06:55:38 -0600141 if change_id in gerrit_query_result:
Brad Bishop35982432018-08-30 17:27:20 -0400142 message_args = (recipe_basename, change_id)
Patrick Williamse310dd92022-12-07 06:55:38 -0600143 print("{} {} already exists".format(*message_args))
Brad Bishop35982432018-08-30 17:27:20 -0400144 continue
145
146 message_args = (recipe_basename, recipe_sha[:10], project_sha[:10])
Patrick Williamse310dd92022-12-07 06:55:38 -0600147 print("{} updating from {} to {}".format(*message_args))
Brad Bishop35982432018-08-30 17:27:20 -0400148
149 remote_args = (args.ssh_config_host, project_name)
Patrick Williamse310dd92022-12-07 06:55:38 -0600150 remote = "ssh://{}/openbmc/{}".format(*remote_args)
Brad Bishop35982432018-08-30 17:27:20 -0400151 git_clone_or_reset(project_name, remote, args)
152
153 try:
Patrick Williamse310dd92022-12-07 06:55:38 -0600154 revlist = "{}..{}".format(recipe_sha, project_sha)
Brad Bishop35982432018-08-30 17:27:20 -0400155 shortlog = git.shortlog(revlist, _cwd=project_name)
Patrick Williamse310dd92022-12-07 06:55:38 -0600156 shortlog = shortlog.stdout.decode("utf-8")
Brad Bishop35982432018-08-30 17:27:20 -0400157 except sh.ErrorReturnCode as e:
Patrick Williamse310dd92022-12-07 06:55:38 -0600158 log("{}".format(e), args)
Brad Bishop35982432018-08-30 17:27:20 -0400159 continue
160
Patrick Williamse310dd92022-12-07 06:55:38 -0600161 reset_args = ["--hard", "origin/{}".format(args.branch)]
Brad Bishop35982432018-08-30 17:27:20 -0400162 git.reset(*reset_args, _cwd=meta)
163
164 recipe_content = None
165 with open(full_recipe_path) as fd:
166 recipe_content = fd.read()
167
168 recipe_content = recipe_content.replace(recipe_sha, project_sha)
Patrick Williamse310dd92022-12-07 06:55:38 -0600169 with open(full_recipe_path, "w") as fd:
Brad Bishop35982432018-08-30 17:27:20 -0400170 fd.write(recipe_content)
171
172 git.add(recipe, _cwd=meta)
173
174 commit_summary_args = (project_name, recipe_sha[:10], project_sha[:10])
Patrick Williamse310dd92022-12-07 06:55:38 -0600175 commit_msg = "{}: srcrev bump {}..{}".format(*commit_summary_args)
176 commit_msg += "\n\n{}".format(shortlog)
177 commit_msg += "\n\nChange-Id: {}".format(change_id)
Brad Bishop35982432018-08-30 17:27:20 -0400178
Patrick Williamse310dd92022-12-07 06:55:38 -0600179 git.commit(sh.echo(commit_msg), "-s", "-F", "-", _cwd=meta)
Brad Bishop35982432018-08-30 17:27:20 -0400180
Brad Bishop199f7952021-01-25 14:20:49 -0500181 push_args = [
Patrick Williamse310dd92022-12-07 06:55:38 -0600182 "origin",
183 "HEAD:refs/for/{}%topic=autobump".format(args.branch),
Brad Bishop199f7952021-01-25 14:20:49 -0500184 ]
Brad Bishop5d7dcfb2020-05-28 14:39:03 -0400185 if not args.dry_run:
186 git.push(*push_args, _cwd=meta)
Brad Bishop35982432018-08-30 17:27:20 -0400187
188
189def main():
Patrick Williamse310dd92022-12-07 06:55:38 -0600190 app_description = """OpenBMC bitbake recipe bumping tool.
Brad Bishop35982432018-08-30 17:27:20 -0400191
192Find bitbake metadata files (recipes) that use the git fetcher
193and check the project repository for newer revisions.
194
195Generate commits that update bitbake metadata files with SRCREV.
196
197Push generated commits to the OpenBMC Gerrit instance for review.
Patrick Williamse310dd92022-12-07 06:55:38 -0600198 """
Brad Bishop35982432018-08-30 17:27:20 -0400199 parser = argparse.ArgumentParser(
200 description=app_description,
Patrick Williamse310dd92022-12-07 06:55:38 -0600201 formatter_class=argparse.RawDescriptionHelpFormatter,
202 )
Brad Bishop35982432018-08-30 17:27:20 -0400203
Patrick Williamse310dd92022-12-07 06:55:38 -0600204 parser.set_defaults(branch="master")
Brad Bishop35982432018-08-30 17:27:20 -0400205 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600206 "-d",
207 "--dry-run",
208 dest="dry_run",
209 action="store_true",
210 help="perform a dry run only",
211 )
Brad Bishop5d7dcfb2020-05-28 14:39:03 -0400212 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600213 "-m",
214 "--meta-repository",
215 dest="meta_repository",
216 action="append",
217 help="meta repository to check for updates",
218 )
Brad Bishop35982432018-08-30 17:27:20 -0400219 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600220 "-v",
221 "--verbose",
222 dest="noisy",
223 action="store_true",
224 help="enable verbose status messages",
225 )
Brad Bishop35982432018-08-30 17:27:20 -0400226 parser.add_argument(
Patrick Williamse310dd92022-12-07 06:55:38 -0600227 "ssh_config_host",
228 metavar="SSH_CONFIG_HOST_ENTRY",
229 help="SSH config host entry for Gerrit connectivity",
230 )
Brad Bishop35982432018-08-30 17:27:20 -0400231
232 args = parser.parse_args()
Patrick Williamse310dd92022-12-07 06:55:38 -0600233 setattr(args, "gerrit", sh.ssh.bake(args.ssh_config_host, "gerrit"))
Brad Bishop35982432018-08-30 17:27:20 -0400234
Patrick Williamse310dd92022-12-07 06:55:38 -0600235 metas = getattr(args, "meta_repository")
Brad Bishop35982432018-08-30 17:27:20 -0400236 if metas is None:
Patrick Williamse310dd92022-12-07 06:55:38 -0600237 metas = args.gerrit("ls-projects", "-m", "meta-")
238 metas = metas.stdout.decode("utf-8").split()
Brad Bishop35982432018-08-30 17:27:20 -0400239 metas = [os.path.split(x)[-1] for x in metas]
240
241 for meta in metas:
242 find_and_process_bumps(meta, args)
243
244
Patrick Williamse310dd92022-12-07 06:55:38 -0600245if __name__ == "__main__":
Brad Bishop35982432018-08-30 17:27:20 -0400246 sys.exit(0 if main() else 1)