| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 1 | #!/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 |  | 
|  | 19 | import argparse | 
|  | 20 | import os | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 21 | import sys | 
|  | 22 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 23 | import sh | 
|  | 24 |  | 
|  | 25 | git = sh.git.bake("--no-pager") | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 26 |  | 
|  | 27 |  | 
|  | 28 | def log(msg, args): | 
|  | 29 | if args.noisy: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 30 | sys.stderr.write("{}\n".format(msg)) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 31 |  | 
|  | 32 |  | 
|  | 33 | def git_clone_or_reset(local_name, remote, args): | 
|  | 34 | if not os.path.exists(local_name): | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 35 | log("cloning into {}...".format(local_name), args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 36 | git.clone(remote, local_name) | 
|  | 37 | else: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 38 | log("{} exists, updating...".format(local_name), args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 39 | git.fetch(_cwd=local_name) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 40 | git.reset("--hard", "FETCH_HEAD", _cwd=local_name) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 41 |  | 
|  | 42 |  | 
| Brad Bishop | 9cfd66e | 2020-05-28 15:16:36 -0400 | [diff] [blame] | 43 | def 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 Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 47 | if "github.com/openbmc" not in uri: | 
| Brad Bishop | 9cfd66e | 2020-05-28 15:16:36 -0400 | [diff] [blame] | 48 | continue | 
|  | 49 |  | 
|  | 50 | # remove fetcher arguments | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 51 | uri = uri.split(";")[0] | 
| Brad Bishop | 9cfd66e | 2020-05-28 15:16:36 -0400 | [diff] [blame] | 52 | # the project is the right-most path segment | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 53 | return uri.split("/")[-1].replace(".git", "") | 
| Brad Bishop | 9cfd66e | 2020-05-28 15:16:36 -0400 | [diff] [blame] | 54 |  | 
|  | 55 | return None | 
|  | 56 |  | 
|  | 57 |  | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 58 | def extract_sha_from_recipe(recipe): | 
|  | 59 | with open(recipe) as fp: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 60 | uris = "" | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 61 | project = None | 
|  | 62 | sha = None | 
|  | 63 |  | 
|  | 64 | for line in fp: | 
| Brad Bishop | 9cfd66e | 2020-05-28 15:16:36 -0400 | [diff] [blame] | 65 | line = line.rstrip() | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 66 | 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 Bishop | 9cfd66e | 2020-05-28 15:16:36 -0400 | [diff] [blame] | 71 | # 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 Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 78 | uris = "" | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 79 |  | 
|  | 80 | if project and sha: | 
|  | 81 | return (project, sha) | 
|  | 82 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 83 | raise RuntimeError("No SRCREV or URI found in {}".format(recipe)) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 84 |  | 
|  | 85 |  | 
|  | 86 | def find_candidate_recipes(meta, args): | 
|  | 87 | remote_fmt_args = (args.ssh_config_host, meta) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 88 | remote = "ssh://{}/openbmc/{}".format(*remote_fmt_args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 89 | try: | 
|  | 90 | git_clone_or_reset(meta, remote, args) | 
|  | 91 | except sh.ErrorReturnCode as e: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 92 | log("{}".format(e), args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 93 | return [] | 
|  | 94 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 95 | 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 Bishop | 2b6a546 | 2021-01-25 16:06:58 -0500 | [diff] [blame] | 98 | grep_args = (*grep_args, *pathspecs) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 99 | try: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 100 | return git.grep(*grep_args, _cwd=meta).stdout.decode("utf-8").split() | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 101 | except sh.ErrorReturnCode_1: | 
|  | 102 | pass | 
|  | 103 | except sh.ErrorReturnCode as e: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 104 | log("{}".format(e), args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 105 |  | 
|  | 106 | return [] | 
|  | 107 |  | 
|  | 108 |  | 
|  | 109 | def 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 Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 118 | remote = "ssh://{}/openbmc/{}".format(*remote_fmt_args) | 
|  | 119 | ls_remote_args = [remote, "refs/heads/{}".format(args.branch)] | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 120 | try: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 121 | project_sha = git("ls-remote", *ls_remote_args) | 
|  | 122 | project_sha = project_sha.stdout.decode("utf-8").split()[0] | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 123 | except sh.ErrorReturnCode as e: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 124 | log("{}".format(e), args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 125 | continue | 
|  | 126 |  | 
|  | 127 | if project_sha == recipe_sha: | 
|  | 128 | message_args = (recipe_basename, recipe_sha[:10]) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 129 | print("{} is up to date ({})".format(*message_args)) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 130 | continue | 
|  | 131 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 132 | 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 Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 136 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 137 | query_args = ["query", "change:{}".format(change_id)] | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 138 | gerrit_query_result = args.gerrit(*query_args) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 139 | gerrit_query_result = gerrit_query_result.stdout.decode("utf-8") | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 140 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 141 | if change_id in gerrit_query_result: | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 142 | message_args = (recipe_basename, change_id) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 143 | print("{} {} already exists".format(*message_args)) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 144 | continue | 
|  | 145 |  | 
|  | 146 | message_args = (recipe_basename, recipe_sha[:10], project_sha[:10]) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 147 | print("{} updating from {} to {}".format(*message_args)) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 148 |  | 
|  | 149 | remote_args = (args.ssh_config_host, project_name) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 150 | remote = "ssh://{}/openbmc/{}".format(*remote_args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 151 | git_clone_or_reset(project_name, remote, args) | 
|  | 152 |  | 
|  | 153 | try: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 154 | revlist = "{}..{}".format(recipe_sha, project_sha) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 155 | shortlog = git.shortlog(revlist, _cwd=project_name) | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 156 | shortlog = shortlog.stdout.decode("utf-8") | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 157 | except sh.ErrorReturnCode as e: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 158 | log("{}".format(e), args) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 159 | continue | 
|  | 160 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 161 | reset_args = ["--hard", "origin/{}".format(args.branch)] | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 162 | 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 Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 169 | with open(full_recipe_path, "w") as fd: | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 170 | 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 Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 175 | 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 Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 178 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 179 | git.commit(sh.echo(commit_msg), "-s", "-F", "-", _cwd=meta) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 180 |  | 
| Brad Bishop | 199f795 | 2021-01-25 14:20:49 -0500 | [diff] [blame] | 181 | push_args = [ | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 182 | "origin", | 
|  | 183 | "HEAD:refs/for/{}%topic=autobump".format(args.branch), | 
| Brad Bishop | 199f795 | 2021-01-25 14:20:49 -0500 | [diff] [blame] | 184 | ] | 
| Brad Bishop | 5d7dcfb | 2020-05-28 14:39:03 -0400 | [diff] [blame] | 185 | if not args.dry_run: | 
|  | 186 | git.push(*push_args, _cwd=meta) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 187 |  | 
|  | 188 |  | 
|  | 189 | def main(): | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 190 | app_description = """OpenBMC bitbake recipe bumping tool. | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 191 |  | 
|  | 192 | Find bitbake metadata files (recipes) that use the git fetcher | 
|  | 193 | and check the project repository for newer revisions. | 
|  | 194 |  | 
|  | 195 | Generate commits that update bitbake metadata files with SRCREV. | 
|  | 196 |  | 
|  | 197 | Push generated commits to the OpenBMC Gerrit instance for review. | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 198 | """ | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 199 | parser = argparse.ArgumentParser( | 
|  | 200 | description=app_description, | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 201 | formatter_class=argparse.RawDescriptionHelpFormatter, | 
|  | 202 | ) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 203 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 204 | parser.set_defaults(branch="master") | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 205 | parser.add_argument( | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 206 | "-d", | 
|  | 207 | "--dry-run", | 
|  | 208 | dest="dry_run", | 
|  | 209 | action="store_true", | 
|  | 210 | help="perform a dry run only", | 
|  | 211 | ) | 
| Brad Bishop | 5d7dcfb | 2020-05-28 14:39:03 -0400 | [diff] [blame] | 212 | parser.add_argument( | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 213 | "-m", | 
|  | 214 | "--meta-repository", | 
|  | 215 | dest="meta_repository", | 
|  | 216 | action="append", | 
|  | 217 | help="meta repository to check for updates", | 
|  | 218 | ) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 219 | parser.add_argument( | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 220 | "-v", | 
|  | 221 | "--verbose", | 
|  | 222 | dest="noisy", | 
|  | 223 | action="store_true", | 
|  | 224 | help="enable verbose status messages", | 
|  | 225 | ) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 226 | parser.add_argument( | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 227 | "ssh_config_host", | 
|  | 228 | metavar="SSH_CONFIG_HOST_ENTRY", | 
|  | 229 | help="SSH config host entry for Gerrit connectivity", | 
|  | 230 | ) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 231 |  | 
|  | 232 | args = parser.parse_args() | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 233 | setattr(args, "gerrit", sh.ssh.bake(args.ssh_config_host, "gerrit")) | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 234 |  | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 235 | metas = getattr(args, "meta_repository") | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 236 | if metas is None: | 
| Patrick Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 237 | metas = args.gerrit("ls-projects", "-m", "meta-") | 
|  | 238 | metas = metas.stdout.decode("utf-8").split() | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 239 | 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 Williams | e310dd9 | 2022-12-07 06:55:38 -0600 | [diff] [blame^] | 245 | if __name__ == "__main__": | 
| Brad Bishop | 3598243 | 2018-08-30 17:27:20 -0400 | [diff] [blame] | 246 | sys.exit(0 if main() else 1) |