Mike Frysinger | 110750a | 2012-03-26 14:19:20 -0400 | [diff] [blame] | 1 | # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | """This module uprevs a given package's ebuild to the next revision.""" |
| 6 | |
Chris McDonald | 59650c3 | 2021-07-20 15:29:28 -0600 | [diff] [blame] | 7 | import logging |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 8 | import os |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 9 | |
Ram Chandrasekar | 60f69f3 | 2022-06-03 22:49:30 +0000 | [diff] [blame] | 10 | from chromite.lib import chromeos_version |
Mike Frysinger | e8dcbfd | 2015-03-08 21:45:52 -0400 | [diff] [blame] | 11 | from chromite.lib import commandline |
Chris McDonald | 59650c3 | 2021-07-20 15:29:28 -0600 | [diff] [blame] | 12 | from chromite.lib import constants |
Chris Sosa | c13bba5 | 2011-05-24 15:14:09 -0700 | [diff] [blame] | 13 | from chromite.lib import cros_build_lib |
David James | 97d9587 | 2012-11-16 15:09:56 -0800 | [diff] [blame] | 14 | from chromite.lib import git |
Mike Frysinger | fddaeb5 | 2012-11-20 11:17:31 -0500 | [diff] [blame] | 15 | from chromite.lib import osutils |
David James | 6450a0a | 2012-12-04 07:59:53 -0800 | [diff] [blame] | 16 | from chromite.lib import parallel |
Alex Deymo | 075c229 | 2014-09-04 18:31:50 -0700 | [diff] [blame] | 17 | from chromite.lib import portage_util |
Lann Martin | b26e129 | 2018-08-09 13:59:19 -0600 | [diff] [blame] | 18 | from chromite.lib import repo_util |
Sergey Frolov | 0161f8b | 2021-07-07 17:41:10 -0600 | [diff] [blame] | 19 | from chromite.lib import retry_util |
Chris Sosa | c13bba5 | 2011-05-24 15:14:09 -0700 | [diff] [blame] | 20 | |
Mike Frysinger | 3278140 | 2020-04-19 06:34:17 -0400 | [diff] [blame] | 21 | |
Gabe Black | 71e963e | 2014-10-28 20:19:59 -0700 | [diff] [blame] | 22 | # Commit message subject for uprevving Portage packages. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 23 | GIT_COMMIT_SUBJECT = "Marking set of ebuilds as stable" |
David James | 15ed130 | 2013-04-25 09:21:19 -0700 | [diff] [blame] | 24 | |
David James | 29e86d5 | 2013-04-19 09:41:29 -0700 | [diff] [blame] | 25 | # Commit message for uprevving Portage packages. |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 26 | _GIT_COMMIT_MESSAGE = "Marking 9999 ebuild for %s as stable." |
David James | 29e86d5 | 2013-04-19 09:41:29 -0700 | [diff] [blame] | 27 | |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 28 | # Dictionary of valid commands with usage information. |
| 29 | COMMAND_DICTIONARY = { |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 30 | "commit": "Marks given ebuilds as stable locally", |
| 31 | "push": "Pushes previous marking of ebuilds to remote repo", |
Mike Frysinger | 5cd8c74 | 2013-10-11 14:43:01 -0400 | [diff] [blame] | 32 | } |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 33 | |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 34 | |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 35 | # ======================= Global Helper Functions ======================== |
| 36 | |
| 37 | |
David James | 41124af | 2015-06-04 21:13:25 -0700 | [diff] [blame] | 38 | def CleanStalePackages(srcroot, boards, package_atoms): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 39 | """Cleans up stale package info from a previous build. |
Mike Frysinger | 5cd8c74 | 2013-10-11 14:43:01 -0400 | [diff] [blame] | 40 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 41 | Args: |
| 42 | srcroot: Root directory of the source tree. |
| 43 | boards: Boards to clean the packages from. |
| 44 | package_atoms: A list of package atoms to unmerge. |
| 45 | """ |
| 46 | if package_atoms: |
| 47 | logging.info("Cleaning up stale packages %s.", package_atoms) |
David James | 15ed130 | 2013-04-25 09:21:19 -0700 | [diff] [blame] | 48 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 49 | # First unmerge all the packages for a board, then eclean it. |
| 50 | # We need these two steps to run in order (unmerge/eclean), |
| 51 | # but we can let all the boards run in parallel. |
| 52 | def _CleanStalePackages(board): |
| 53 | if board: |
| 54 | suffix = "-" + board |
| 55 | runcmd = cros_build_lib.run |
| 56 | else: |
| 57 | suffix = "" |
| 58 | runcmd = cros_build_lib.sudo_run |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 59 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 60 | emerge, eclean = "emerge" + suffix, "eclean" + suffix |
| 61 | if not osutils.FindMissingBinaries([emerge, eclean]): |
| 62 | if package_atoms: |
| 63 | # If nothing was found to be unmerged, emerge will exit(1). |
| 64 | result = runcmd( |
| 65 | [emerge, "-q", "--unmerge"] + list(package_atoms), |
| 66 | enter_chroot=True, |
| 67 | extra_env={"CLEAN_DELAY": "0"}, |
| 68 | check=False, |
| 69 | cwd=srcroot, |
| 70 | ) |
| 71 | if result.returncode not in (0, 1): |
| 72 | raise cros_build_lib.RunCommandError( |
| 73 | "unexpected error", result |
| 74 | ) |
| 75 | runcmd( |
| 76 | [eclean, "-d", "packages"], |
| 77 | cwd=srcroot, |
| 78 | enter_chroot=True, |
| 79 | stdout=True, |
| 80 | stderr=True, |
| 81 | ) |
Mike Frysinger | b7ab9b8 | 2012-04-04 16:22:43 -0400 | [diff] [blame] | 82 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 83 | tasks = [] |
| 84 | for board in boards: |
| 85 | tasks.append([board]) |
| 86 | tasks.append([None]) |
Mike Frysinger | b7ab9b8 | 2012-04-04 16:22:43 -0400 | [diff] [blame] | 87 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 88 | parallel.RunTasksInProcessPool(_CleanStalePackages, tasks) |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 89 | |
| 90 | |
Mike Frysinger | 2ebe373 | 2012-05-08 17:04:12 -0400 | [diff] [blame] | 91 | # TODO(build): This code needs to be gutted and rebased to cros_build_lib. |
| 92 | def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 93 | """Returns true if there are local commits.""" |
| 94 | output = git.RunGit( |
| 95 | cwd, ["rev-parse", stable_branch, tracking_branch] |
| 96 | ).stdout.split() |
| 97 | return output[0] != output[1] |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 98 | |
| 99 | |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 100 | # ======================= End Global Helper Functions ======================== |
| 101 | |
| 102 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 103 | def PushChange( |
| 104 | stable_branch, tracking_branch, dryrun, cwd, staging_branch=None |
| 105 | ): |
| 106 | """Pushes commits in the stable_branch to the remote git repository. |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 107 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 108 | Pushes local commits from calls to CommitChange to the remote git |
| 109 | repository specified by current working directory. If changes are |
| 110 | found to commit, they will be merged to the merge branch and pushed. |
| 111 | In that case, the local repository will be left on the merge branch. |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 112 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 113 | Args: |
| 114 | stable_branch: The local branch with commits we want to push. |
| 115 | tracking_branch: The tracking branch of the local branch. |
| 116 | dryrun: Use git push --dryrun to emulate a push. |
| 117 | cwd: The directory to run commands in. |
| 118 | staging_branch: The staging branch to push for a failed PFQ run |
Mike Frysinger | 1a736a8 | 2013-12-12 01:50:59 -0500 | [diff] [blame] | 119 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 120 | Raises: |
| 121 | OSError: Error occurred while pushing. |
| 122 | """ |
| 123 | if not git.DoesCommitExistInRepo(cwd, stable_branch): |
| 124 | logging.debug("No branch created for %s. Exiting", cwd) |
| 125 | return |
David James | 4795963 | 2015-10-23 07:56:01 -0700 | [diff] [blame] | 126 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 127 | if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd): |
| 128 | logging.debug("No work found to push in %s. Exiting", cwd) |
| 129 | return |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 130 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 131 | # For the commit queue, our local branch may contain commits that were |
| 132 | # just tested and pushed during the CommitQueueCompletion stage. Sync |
| 133 | # and rebase our local branch on top of the remote commits. |
| 134 | remote_ref = git.GetTrackingBranch(cwd, branch=stable_branch, for_push=True) |
| 135 | # SyncPushBranch rebases HEAD onto the updated remote. We need to checkout |
| 136 | # stable_branch here in order to update it. |
| 137 | git.RunGit(cwd, ["checkout", stable_branch]) |
| 138 | git.SyncPushBranch(cwd, remote_ref.remote, remote_ref.ref) |
David James | 6600946 | 2012-03-25 10:08:38 -0700 | [diff] [blame] | 139 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 140 | # Check whether any local changes remain after the sync. |
| 141 | if not _DoWeHaveLocalCommits(stable_branch, remote_ref.ref, cwd): |
| 142 | logging.info("All changes already pushed for %s. Exiting", cwd) |
| 143 | return |
David James | 6600946 | 2012-03-25 10:08:38 -0700 | [diff] [blame] | 144 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 145 | # Add a failsafe check here. Only CLs from these users should be here. |
| 146 | # - 'chrome-bot', |
| 147 | # - 'chromeos-ci-prod' |
| 148 | # - 'chromeos-ci-release' |
| 149 | # If any other CLs are found then complain. In dryruns extra CLs are normal, |
| 150 | # though, and can be ignored. |
| 151 | bad_cl_cmd = [ |
| 152 | "log", |
| 153 | "--format=short", |
| 154 | "--perl-regexp", |
| 155 | "--author", |
| 156 | "^(?!chrome-bot|chromeos-ci-prod|chromeos-ci-release)", |
| 157 | "%s..%s" % (remote_ref.ref, stable_branch), |
| 158 | ] |
| 159 | bad_cls = git.RunGit(cwd, bad_cl_cmd).stdout |
| 160 | if bad_cls.strip() and not dryrun: |
| 161 | logging.error( |
| 162 | "The Uprev stage found changes from users other than " |
| 163 | "chrome-bot or chromeos-ci-prod or chromeos-ci-release:\n\n%s", |
| 164 | bad_cls, |
| 165 | ) |
| 166 | raise AssertionError("Unexpected CLs found during uprev stage.") |
Matt Tennant | cb52205 | 2013-11-25 14:23:43 -0800 | [diff] [blame] | 167 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 168 | if staging_branch is not None: |
| 169 | logging.info( |
| 170 | "PFQ FAILED. Pushing uprev change to staging branch %s", |
| 171 | staging_branch, |
| 172 | ) |
Lev Rumyantsev | 25352ba | 2016-09-07 15:53:58 -0700 | [diff] [blame] | 173 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 174 | description = git.RunGit( |
| 175 | cwd, |
| 176 | [ |
| 177 | "log", |
| 178 | "--format=format:%s%n%n%b", |
| 179 | "%s..%s" % (remote_ref.ref, stable_branch), |
| 180 | ], |
| 181 | ).stdout |
| 182 | description = "%s\n\n%s" % (GIT_COMMIT_SUBJECT, description) |
| 183 | logging.info("For %s, using description %s", cwd, description) |
| 184 | git.CreatePushBranch( |
| 185 | constants.MERGE_BRANCH, cwd, remote_push_branch=remote_ref |
| 186 | ) |
| 187 | git.RunGit(cwd, ["merge", "--squash", stable_branch]) |
| 188 | git.RunGit(cwd, ["commit", "-m", description]) |
| 189 | git.RunGit(cwd, ["config", "push.default", "tracking"]) |
Sergey Frolov | 0161f8b | 2021-07-07 17:41:10 -0600 | [diff] [blame] | 190 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 191 | # Run git.PushBranch and retry up to 5 times. |
| 192 | # retry_util.RetryCommand will only retry on RunCommandErrors, |
| 193 | # which would be thrown by git.PushBranch when it gets to |
| 194 | # cros_build_lib.run()'ning the actual git command, |
| 195 | # which may fail with a transient HTTP 429 Too Many Requests error, |
| 196 | # and we need to retry on that. |
| 197 | # Do not retry if it fails to setup before cros_build_lib.run |
| 198 | # or upon other errors, like getting SIGINT. |
| 199 | max_retries = 5 |
| 200 | retry_util.RetryCommand( |
| 201 | git.PushBranch, |
| 202 | max_retries, |
| 203 | constants.MERGE_BRANCH, |
| 204 | cwd, |
| 205 | dryrun=dryrun, |
| 206 | staging_branch=staging_branch, |
| 207 | sleep=15, |
| 208 | log_retries=True, |
| 209 | ) |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 210 | |
| 211 | |
| 212 | class GitBranch(object): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 213 | """Wrapper class for a git branch.""" |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 214 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 215 | def __init__(self, branch_name, tracking_branch, cwd): |
| 216 | """Sets up variables but does not create the branch. |
David James | c7c4ff5 | 2013-09-18 17:57:13 -0700 | [diff] [blame] | 217 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 218 | Args: |
| 219 | branch_name: The name of the branch. |
| 220 | tracking_branch: The associated tracking branch. |
| 221 | cwd: The git repository to work in. |
| 222 | """ |
| 223 | self.branch_name = branch_name |
| 224 | self.tracking_branch = tracking_branch |
| 225 | self.cwd = cwd |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 226 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 227 | def CreateBranch(self): |
| 228 | self.Checkout() |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 229 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 230 | def Checkout(self, branch=None): |
| 231 | """Function used to check out to another GitBranch.""" |
| 232 | if not branch: |
| 233 | branch = self.branch_name |
| 234 | if branch == self.tracking_branch or self.Exists(branch): |
| 235 | git.RunGit(self.cwd, ["checkout", "-f", branch]) |
| 236 | else: |
| 237 | repo = repo_util.Repository.MustFind(self.cwd) |
| 238 | repo.StartBranch(branch, projects=["."], cwd=self.cwd) |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 239 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 240 | def Exists(self, branch=None): |
| 241 | """Returns True if the branch exists.""" |
| 242 | if not branch: |
| 243 | branch = self.branch_name |
| 244 | branches = git.RunGit(self.cwd, ["branch"]).stdout |
| 245 | return branch in branches.split() |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 246 | |
| 247 | |
Mike Frysinger | e8dcbfd | 2015-03-08 21:45:52 -0400 | [diff] [blame] | 248 | def GetParser(): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 249 | """Creates the argparse parser.""" |
| 250 | parser = commandline.ArgumentParser() |
| 251 | parser.add_argument( |
| 252 | "--all", action="store_true", help="Mark all packages as stable." |
| 253 | ) |
| 254 | parser.add_argument( |
| 255 | "-b", "--boards", default="", help="Colon-separated list of boards." |
| 256 | ) |
| 257 | parser.add_argument( |
| 258 | "--drop_file", help="File to list packages that were revved." |
| 259 | ) |
| 260 | parser.add_argument( |
| 261 | "--dryrun", |
| 262 | action="store_true", |
| 263 | help="Passes dry-run to git push if pushing a change.", |
| 264 | ) |
| 265 | parser.add_argument( |
| 266 | "--force", |
| 267 | action="store_true", |
| 268 | help="Force the stabilization of packages marked for " |
| 269 | "manual uprev. " |
| 270 | "(only compatible with -p)", |
| 271 | ) |
| 272 | parser.add_argument( |
| 273 | "--list_revisions", |
| 274 | action="store_true", |
| 275 | help="List all revisions included in the commit message.", |
| 276 | ) |
| 277 | parser.add_argument( |
| 278 | "-o", "--overlays", help="Colon-separated list of overlays to modify." |
| 279 | ) |
| 280 | parser.add_argument( |
| 281 | "--overlay-type", |
| 282 | help='Populates --overlays based on "public", "private"' ', or "both".', |
| 283 | ) |
| 284 | parser.add_argument( |
| 285 | "-p", "--packages", help="Colon separated list of packages to rev." |
| 286 | ) |
| 287 | parser.add_argument("--buildroot", type="path", help="Path to buildroot.") |
| 288 | parser.add_argument( |
| 289 | "-r", |
| 290 | "--srcroot", |
| 291 | type="path", |
| 292 | help="Path to root src. Deprecated in favor of " "--buildroot", |
| 293 | ) |
| 294 | parser.add_argument( |
| 295 | "--staging_branch", help="The staging branch to push changes" |
| 296 | ) |
| 297 | parser.add_argument( |
| 298 | "command", choices=sorted(COMMAND_DICTIONARY), help="Command to run." |
| 299 | ) |
| 300 | return parser |
Mike Frysinger | e8dcbfd | 2015-03-08 21:45:52 -0400 | [diff] [blame] | 301 | |
| 302 | |
| 303 | def main(argv): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 304 | parser = GetParser() |
| 305 | options = parser.parse_args(argv) |
Don Garrett | 4fef8c3 | 2018-08-10 18:04:01 -0700 | [diff] [blame] | 306 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 307 | # TODO: Remove this code in favor of a simple default on buildroot when |
| 308 | # srcroot is removed. |
| 309 | if options.srcroot and not options.buildroot: |
| 310 | # Convert /<repo>/src -> <repo> |
| 311 | options.buildroot = os.path.dirname(options.srcroot) |
| 312 | if not options.buildroot: |
| 313 | options.buildroot = constants.SOURCE_ROOT |
| 314 | options.srcroot = None |
Don Garrett | 4fef8c3 | 2018-08-10 18:04:01 -0700 | [diff] [blame] | 315 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 316 | options.Freeze() |
Mike Frysinger | e8dcbfd | 2015-03-08 21:45:52 -0400 | [diff] [blame] | 317 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 318 | if options.command == "commit": |
| 319 | if not options.packages and not options.all: |
| 320 | parser.error("Please specify at least one package (--packages)") |
| 321 | if options.force and options.all: |
| 322 | parser.error( |
| 323 | "Cannot use --force with --all. You must specify a list of " |
| 324 | "packages you want to force uprev." |
| 325 | ) |
Bertrand SIMONNET | 6af5430 | 2015-08-05 10:12:49 -0700 | [diff] [blame] | 326 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 327 | if not os.path.isdir(options.buildroot): |
| 328 | parser.error("buildroot is not a valid path: %s" % options.buildroot) |
Mike Frysinger | e8dcbfd | 2015-03-08 21:45:52 -0400 | [diff] [blame] | 329 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 330 | if options.overlay_type and options.overlays: |
| 331 | parser.error("Cannot use --overlay-type with --overlays.") |
Don Garrett | f9eff95 | 2018-08-10 16:50:04 -0700 | [diff] [blame] | 332 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 333 | portage_util.EBuild.VERBOSE = options.verbose |
Chris Sosa | 62ad852 | 2011-03-08 17:46:17 -0800 | [diff] [blame] | 334 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 335 | package_list = None |
| 336 | if options.packages: |
| 337 | package_list = options.packages.split(":") |
Chris Sosa | 62ad852 | 2011-03-08 17:46:17 -0800 | [diff] [blame] | 338 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 339 | overlays = [] |
| 340 | if options.overlays: |
| 341 | for path in options.overlays.split(":"): |
| 342 | if not os.path.isdir(path): |
| 343 | cros_build_lib.Die("Cannot find overlay: %s" % path) |
| 344 | overlays.append(os.path.realpath(path)) |
| 345 | elif options.overlay_type: |
| 346 | overlays = portage_util.FindOverlays( |
| 347 | options.overlay_type, buildroot=options.buildroot |
| 348 | ) |
| 349 | else: |
| 350 | logging.warning("Missing --overlays argument") |
| 351 | overlays.extend( |
| 352 | [ |
| 353 | "%s/src/private-overlays/chromeos-overlay" % options.buildroot, |
| 354 | "%s/src/third_party/chromiumos-overlay" % options.buildroot, |
| 355 | ] |
| 356 | ) |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 357 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 358 | manifest = git.ManifestCheckout.Cached(options.buildroot) |
Ryan Cui | 4656a3c | 2011-05-24 12:30:30 -0700 | [diff] [blame] | 359 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 360 | # Dict mapping from each overlay to its tracking branch. |
| 361 | overlay_tracking_branch = {} |
| 362 | # Dict mapping from each git repository (project) to a list of its overlays. |
| 363 | git_project_overlays = {} |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 364 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 365 | for overlay in overlays: |
| 366 | remote_ref = git.GetTrackingBranchViaManifest( |
| 367 | overlay, manifest=manifest |
| 368 | ) |
| 369 | overlay_tracking_branch[overlay] = remote_ref.ref |
| 370 | git_project_overlays.setdefault(remote_ref.project_name, []).append( |
| 371 | overlay |
| 372 | ) |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 373 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 374 | if options.command == "push": |
| 375 | _WorkOnPush(options, overlay_tracking_branch, git_project_overlays) |
| 376 | elif options.command == "commit": |
| 377 | _WorkOnCommit( |
| 378 | options, |
| 379 | overlays, |
| 380 | overlay_tracking_branch, |
| 381 | git_project_overlays, |
| 382 | manifest, |
| 383 | package_list, |
| 384 | ) |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 385 | |
| 386 | |
| 387 | def _WorkOnPush(options, overlay_tracking_branch, git_project_overlays): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 388 | """Push uprevs of overlays belonging to differet git projects in parallel. |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 389 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 390 | Args: |
| 391 | options: The options object returned by the argument parser. |
| 392 | overlay_tracking_branch: A dict mapping from each overlay to its tracking |
| 393 | branch. |
| 394 | git_project_overlays: A dict mapping from each git repository to a list of |
| 395 | its overlays. |
| 396 | """ |
| 397 | inputs = [ |
| 398 | [options, overlays_per_project, overlay_tracking_branch] |
| 399 | for overlays_per_project in git_project_overlays.values() |
| 400 | ] |
| 401 | parallel.RunTasksInProcessPool(_PushOverlays, inputs) |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 402 | |
| 403 | |
| 404 | def _PushOverlays(options, overlays, overlay_tracking_branch): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 405 | """Push uprevs for overlays in sequence. |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 406 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 407 | Args: |
| 408 | options: The options object returned by the argument parser. |
| 409 | overlays: A list of overlays to push uprevs in sequence. |
| 410 | overlay_tracking_branch: A dict mapping from each overlay to its tracking |
| 411 | branch. |
| 412 | """ |
| 413 | for overlay in overlays: |
| 414 | if not os.path.isdir(overlay): |
| 415 | logging.warning("Skipping %s, which is not a directory.", overlay) |
| 416 | continue |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 417 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 418 | tracking_branch = overlay_tracking_branch[overlay] |
| 419 | PushChange( |
| 420 | constants.STABLE_EBUILD_BRANCH, |
| 421 | tracking_branch, |
| 422 | options.dryrun, |
| 423 | cwd=overlay, |
| 424 | staging_branch=options.staging_branch, |
| 425 | ) |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 426 | |
| 427 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 428 | def _WorkOnCommit( |
| 429 | options, |
| 430 | overlays, |
| 431 | overlay_tracking_branch, |
| 432 | git_project_overlays, |
| 433 | manifest, |
| 434 | package_list, |
| 435 | ): |
| 436 | """Commit uprevs of overlays belonging to different git projects in parallel. |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 437 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 438 | Args: |
| 439 | options: The options object returned by the argument parser. |
| 440 | overlays: A list of overlays to work on. |
| 441 | overlay_tracking_branch: A dict mapping from each overlay to its tracking |
| 442 | branch. |
| 443 | git_project_overlays: A dict mapping from each git repository to a list of |
| 444 | its overlays. |
| 445 | manifest: The manifest of the given source root. |
| 446 | package_list: A list of packages passed from commandline to work on. |
| 447 | """ |
| 448 | # We cleaned up self referential ebuilds by this version, but don't enforce |
| 449 | # the check on older ones to avoid breaking factory/firmware branches. |
| 450 | root_version = chromeos_version.VersionInfo.from_repo(options.buildroot) |
| 451 | no_self_repos_version = chromeos_version.VersionInfo("13099.0.0") |
| 452 | reject_self_repo = root_version >= no_self_repos_version |
Mike Frysinger | 62ff8d7 | 2020-05-19 03:06:51 -0400 | [diff] [blame] | 453 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 454 | overlay_ebuilds = _GetOverlayToEbuildsMap(options, overlays, package_list) |
David James | 84e953c | 2013-04-23 18:44:06 -0700 | [diff] [blame] | 455 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 456 | with parallel.Manager() as manager: |
| 457 | # Contains the array of packages we actually revved. |
| 458 | revved_packages = manager.list() |
| 459 | new_package_atoms = manager.list() |
David James | a8457b5 | 2011-05-28 00:03:20 -0700 | [diff] [blame] | 460 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 461 | inputs = [ |
| 462 | [ |
| 463 | options, |
| 464 | manifest, |
| 465 | overlays_per_project, |
| 466 | overlay_tracking_branch, |
| 467 | overlay_ebuilds, |
| 468 | revved_packages, |
| 469 | new_package_atoms, |
| 470 | reject_self_repo, |
| 471 | ] |
| 472 | for overlays_per_project in git_project_overlays.values() |
| 473 | ] |
| 474 | parallel.RunTasksInProcessPool(_CommitOverlays, inputs) |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 475 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 476 | chroot_path = os.path.join( |
| 477 | options.buildroot, constants.DEFAULT_CHROOT_DIR |
| 478 | ) |
| 479 | if os.path.exists(chroot_path): |
| 480 | CleanStalePackages( |
| 481 | options.buildroot, options.boards.split(":"), new_package_atoms |
| 482 | ) |
| 483 | if options.drop_file: |
| 484 | osutils.WriteFile(options.drop_file, " ".join(revved_packages)) |
Chris Sosa | dad0d32 | 2011-01-31 16:37:33 -0800 | [diff] [blame] | 485 | |
Brian Harring | 609dc4e | 2012-05-07 02:17:44 -0700 | [diff] [blame] | 486 | |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 487 | def _GetOverlayToEbuildsMap(options, overlays, package_list): |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 488 | """Get ebuilds for overlays. |
David James | 15ed130 | 2013-04-25 09:21:19 -0700 | [diff] [blame] | 489 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 490 | Args: |
| 491 | options: The options object returned by the argument parser. |
| 492 | overlays: A list of overlays to work on. |
| 493 | package_list: A list of packages passed from commandline to work on. |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 494 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 495 | Returns: |
| 496 | A dict mapping each overlay to a list of ebuilds belonging to it. |
| 497 | """ |
| 498 | root_version = chromeos_version.VersionInfo.from_repo(options.buildroot) |
| 499 | subdir_removal = chromeos_version.VersionInfo("10363.0.0") |
| 500 | require_subdir_support = root_version < subdir_removal |
Don Garrett | 3dbb293 | 2018-10-02 14:41:26 -0700 | [diff] [blame] | 501 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 502 | overlay_ebuilds = {} |
| 503 | inputs = [ |
| 504 | [ |
| 505 | overlay, |
| 506 | options.all, |
| 507 | package_list, |
| 508 | options.force, |
| 509 | require_subdir_support, |
| 510 | ] |
| 511 | for overlay in overlays |
| 512 | ] |
| 513 | result = parallel.RunTasksInProcessPool( |
| 514 | portage_util.GetOverlayEBuilds, inputs |
| 515 | ) |
| 516 | for idx, ebuilds in enumerate(result): |
| 517 | overlay_ebuilds[overlays[idx]] = ebuilds |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 518 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 519 | return overlay_ebuilds |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 520 | |
| 521 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 522 | def _CommitOverlays( |
| 523 | options, |
| 524 | manifest, |
| 525 | overlays, |
| 526 | overlay_tracking_branch, |
| 527 | overlay_ebuilds, |
| 528 | revved_packages, |
| 529 | new_package_atoms, |
| 530 | reject_self_repo=True, |
| 531 | ): |
| 532 | """Commit uprevs for overlays in sequence. |
Ningning Xia | 419e4eb | 2018-02-05 10:30:36 -0800 | [diff] [blame] | 533 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 534 | Args: |
| 535 | options: The options object returned by the argument parser. |
| 536 | manifest: The manifest of the given source root. |
| 537 | overlays: A list over overlays to commit. |
| 538 | overlay_tracking_branch: A dict mapping from each overlay to its tracking |
| 539 | branch. |
| 540 | overlay_ebuilds: A dict mapping overlays to their ebuilds. |
| 541 | revved_packages: A shared list of revved packages. |
| 542 | new_package_atoms: A shared list of new package atoms. |
| 543 | reject_self_repo: Whether to abort if the ebuild lives in the same git |
| 544 | repo as it is tracking for uprevs. |
| 545 | """ |
| 546 | for overlay in overlays: |
| 547 | if not os.path.isdir(overlay): |
| 548 | logging.warning("Skipping %s, which is not a directory.", overlay) |
Sam McNally | eb5e205 | 2018-09-05 16:34:55 +1000 | [diff] [blame] | 549 | continue |
| 550 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 551 | # Note we intentionally work from the non push tracking branch; |
| 552 | # everything built thus far has been against it (meaning, http mirrors), |
| 553 | # thus we should honor that. During the actual push, the code switches |
| 554 | # to the correct urls, and does an appropriate rebasing. |
| 555 | tracking_branch = overlay_tracking_branch[overlay] |
Sam McNally | eb5e205 | 2018-09-05 16:34:55 +1000 | [diff] [blame] | 556 | |
Alex Klein | 1699fab | 2022-09-08 08:46:06 -0600 | [diff] [blame] | 557 | existing_commit = git.GetGitRepoRevision(overlay) |
| 558 | |
| 559 | # Make sure we run in the top-level git directory in case we are |
| 560 | # adding/removing an overlay in existing_commit. |
| 561 | git_root = git.FindGitTopLevel(overlay) |
| 562 | if git_root is None: |
| 563 | cros_build_lib.Die("No git repo at overlay directory %s.", overlay) |
| 564 | |
| 565 | work_branch = GitBranch( |
| 566 | constants.STABLE_EBUILD_BRANCH, tracking_branch, cwd=git_root |
| 567 | ) |
| 568 | work_branch.CreateBranch() |
| 569 | if not work_branch.Exists(): |
| 570 | cros_build_lib.Die( |
| 571 | "Unable to create stabilizing branch in %s" % overlay |
| 572 | ) |
| 573 | |
| 574 | # In the case of uprevving overlays that have patches applied to them, |
| 575 | # include the patched changes in the stabilizing branch. |
| 576 | git.RunGit(git_root, ["rebase", existing_commit]) |
| 577 | |
| 578 | ebuilds = overlay_ebuilds.get(overlay, []) |
| 579 | if ebuilds: |
| 580 | with parallel.Manager() as manager: |
| 581 | # Contains the array of packages we actually revved. |
| 582 | messages = manager.list() |
| 583 | ebuild_paths_to_add = manager.list() |
| 584 | ebuild_paths_to_remove = manager.list() |
| 585 | |
| 586 | inputs = [ |
| 587 | [ |
| 588 | overlay, |
| 589 | ebuild, |
| 590 | manifest, |
| 591 | options, |
| 592 | ebuild_paths_to_add, |
| 593 | ebuild_paths_to_remove, |
| 594 | messages, |
| 595 | revved_packages, |
| 596 | new_package_atoms, |
| 597 | reject_self_repo, |
| 598 | ] |
| 599 | for ebuild in ebuilds |
| 600 | ] |
| 601 | parallel.RunTasksInProcessPool(_WorkOnEbuild, inputs) |
| 602 | |
| 603 | if ebuild_paths_to_add: |
| 604 | logging.info( |
| 605 | "Adding new stable ebuild paths %s in overlay %s.", |
| 606 | ebuild_paths_to_add, |
| 607 | overlay, |
| 608 | ) |
| 609 | git.RunGit(overlay, ["add"] + list(ebuild_paths_to_add)) |
| 610 | |
| 611 | if ebuild_paths_to_remove: |
| 612 | logging.info( |
| 613 | "Removing old ebuild paths %s in overlay %s.", |
| 614 | ebuild_paths_to_remove, |
| 615 | overlay, |
| 616 | ) |
| 617 | git.RunGit( |
| 618 | overlay, ["rm", "-f"] + list(ebuild_paths_to_remove) |
| 619 | ) |
| 620 | |
| 621 | if messages: |
| 622 | portage_util.EBuild.CommitChange( |
| 623 | "\n\n".join(messages), overlay |
| 624 | ) |
| 625 | |
| 626 | |
| 627 | def _WorkOnEbuild( |
| 628 | overlay, |
| 629 | ebuild, |
| 630 | manifest, |
| 631 | options, |
| 632 | ebuild_paths_to_add, |
| 633 | ebuild_paths_to_remove, |
| 634 | messages, |
| 635 | revved_packages, |
| 636 | new_package_atoms, |
| 637 | reject_self_repo=True, |
| 638 | ): |
| 639 | """Work on a single ebuild. |
| 640 | |
| 641 | Args: |
| 642 | overlay: The overlay where the ebuild belongs to. |
| 643 | ebuild: The ebuild to work on. |
| 644 | manifest: The manifest of the given source root. |
| 645 | options: The options object returned by the argument parser. |
| 646 | ebuild_paths_to_add: New stable ebuild paths to add to git. |
| 647 | ebuild_paths_to_remove: Old ebuild paths to remove from git. |
| 648 | messages: A share list of commit messages. |
| 649 | revved_packages: A shared list of revved packages. |
| 650 | new_package_atoms: A shared list of new package atoms. |
| 651 | reject_self_repo: Whether to abort if the ebuild lives in the same git |
| 652 | repo as it is tracking for uprevs. |
| 653 | """ |
| 654 | if options.verbose: |
| 655 | logging.info( |
| 656 | "Working on %s, info %s", ebuild.package, ebuild.cros_workon_vars |
| 657 | ) |
| 658 | if not ebuild.cros_workon_vars: |
| 659 | logging.warning( |
| 660 | "%s: unable to parse workon settings", ebuild.ebuild_path |
| 661 | ) |
| 662 | |
| 663 | try: |
| 664 | result = ebuild.RevWorkOnEBuild( |
| 665 | os.path.join(options.buildroot, "src"), |
| 666 | manifest, |
| 667 | reject_self_repo=reject_self_repo, |
| 668 | ) |
| 669 | if result: |
| 670 | new_package, ebuild_path_to_add, ebuild_path_to_remove = result |
| 671 | |
| 672 | if ebuild_path_to_add: |
| 673 | ebuild_paths_to_add.append(ebuild_path_to_add) |
| 674 | if ebuild_path_to_remove: |
| 675 | ebuild_paths_to_remove.append(ebuild_path_to_remove) |
| 676 | |
| 677 | messages.append(_GIT_COMMIT_MESSAGE % ebuild.package) |
| 678 | |
| 679 | if options.list_revisions: |
| 680 | info = ebuild.GetSourceInfo( |
| 681 | os.path.join(options.buildroot, "src"), |
| 682 | manifest, |
| 683 | reject_self_repo=reject_self_repo, |
| 684 | ) |
| 685 | srcdirs = [ |
| 686 | os.path.join(options.buildroot, "src", srcdir) |
| 687 | for srcdir in ebuild.cros_workon_vars.localname |
| 688 | ] |
| 689 | old_commit_ids = dict( |
| 690 | zip(srcdirs, ebuild.cros_workon_vars.commit.split(",")) |
| 691 | ) |
| 692 | git_log = [] |
| 693 | for srcdir in info.srcdirs: |
| 694 | old_commit_id = old_commit_ids.get(srcdir) |
| 695 | new_commit_id = ebuild.GetCommitId(srcdir) |
| 696 | if not old_commit_id or old_commit_id == new_commit_id: |
| 697 | continue |
| 698 | |
| 699 | logs = git.RunGit( |
| 700 | srcdir, |
| 701 | [ |
| 702 | "log", |
| 703 | "%s..%s" % (old_commit_id[:8], new_commit_id[:8]), |
| 704 | "--pretty=format:%h %<(63,trunc)%s", |
| 705 | ], |
| 706 | ) |
| 707 | git_log.append("$ " + logs.cmdstr) |
| 708 | git_log.extend( |
| 709 | line.strip() for line in logs.stdout.splitlines() |
| 710 | ) |
| 711 | if git_log: |
| 712 | messages.append("\n".join(git_log)) |
| 713 | |
| 714 | revved_packages.append(ebuild.package) |
| 715 | new_package_atoms.append("=%s" % new_package) |
| 716 | except portage_util.InvalidUprevSourceError as e: |
| 717 | logging.error( |
| 718 | "An error occurred while uprevving %s: %s", ebuild.package, e |
| 719 | ) |
| 720 | raise |
| 721 | except portage_util.EbuildVersionError as e: |
| 722 | logging.warning("Unable to rev %s: %s", ebuild.package, e) |
| 723 | raise |
| 724 | except OSError: |
| 725 | logging.warning( |
| 726 | "Cannot rev %s\n" |
| 727 | "Note you will have to go into %s " |
| 728 | "and reset the git repo yourself.", |
| 729 | ebuild.package, |
| 730 | overlay, |
| 731 | ) |
| 732 | raise |