Index: scripts/tools/cros/cros_builder_convert.py |
diff --git a/scripts/tools/cros/cros_builder_convert.py b/scripts/tools/cros/cros_builder_convert.py |
deleted file mode 100755 |
index d16a6f42341426791c3bcec44fe7eadff7c795e8..0000000000000000000000000000000000000000 |
--- a/scripts/tools/cros/cros_builder_convert.py |
+++ /dev/null |
@@ -1,273 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright 2015 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-"""Updates BuildBot builder directories to the new 'cbuildbot'-driven naming |
-scheme. |
- |
-Classic BuildBot CrOS waterfalls define build directories by composing the |
-directory name from component parts resembling the target and a final branch |
-name. Oftentimes, these component parts (and, therefore, the composition) don't |
-actually match the name of the underlying 'cbuildbot' target. |
- |
-This presents problems because the build target are fundamentally driven by |
-their underlying 'cbuildbot' target, but the composition scheme is extremely |
-arbitrary. |
- |
-Consequently, BuildBot masters are being migrated to a new, deterministic, |
-'cbuildbot'-driven naming scheme. A builder building 'cbuildbot' target |
-<target> and checking Chromite/'cbuildbot' from branch <branch> will use the |
-builder name: <target>-<branch>. This is universally sustainable across all |
-waterfalls and ensures that 'cbuildbot' builds are tracked and numbered based |
-on their underlying 'cbuildbot' target. |
- |
-This script is intended to be run on a stopped BuildBot master during build |
-directory migration. It will iterate through each build directory in the current |
-master naming scheme and rename the classic directories into their new |
-'cbuildbot'-driven namespace. |
-""" |
- |
-import argparse |
-import collections |
-import logging |
-import os |
-import re |
-import shutil |
-import sys |
- |
-from common import cros_chromite |
- |
- |
-class UpdateInfo(collections.namedtuple( |
- 'UpdateInfo', |
- ('src', 'cbb_name', 'branch'))): |
- """Information about a single directory update action.""" |
- |
- _STATIC_PERMUTATIONS = { |
- 'Canary master': 'master-canary', |
- } |
- |
- _TRANSFORMATIONS = ( |
- (r'-canary-', r'-release-'), |
- (r'-full', r'-release'), |
- (r'-pre-flight', r'-pre-flight-branch'), |
- (r'(x86|amd64)$', r'\1-generic'), |
- (r'^chromium-tot-chromeos-(.+)-asan', r'\1-tot-asan-informational'), |
- (r'^chromium-tot-chromeos-(.+)', r'\1-tot-chrome-pfq-informational'), |
- (r'^chromium-(.+)-telemetry$', r'\1-telemetry'), |
- (r'(.+)-bin$', r'\1'), |
- ) |
- |
- @property |
- def dst(self): |
- """Constructs the <cbuildbot>-<branch> form.""" |
- return '%s-%s' % (self.cbb_name, self.branch) |
- |
- @classmethod |
- def permutations(cls, name): |
- """Attempts to permute a legacy BuildBot name into a Chromite target. |
- |
- Args: |
- name (str): The source name to process and map. |
- Yields (str): Various permutations of 'name'. |
- """ |
- # No cbuildbot targets use upper-case letters. |
- name = name.lower() |
- |
- # If 'name' is already a 'cbuildbot' target, return it unmodified. |
- yield name |
- |
- # Apply static permutations. |
- p = cls._STATIC_PERMUTATIONS.get(name) |
- if p: |
- yield p |
- |
- # Replace 'canary' with 'release'. |
- for find, replace in cls._TRANSFORMATIONS: |
- name = re.sub(find, replace, name) |
- yield name |
- |
- # Is 'name' valid if it was a release group? |
- if not name.endswith('-group'): |
- # We never build 'full' group variants. |
- name_group = ('%s-group' % (name,)).replace('-full-', '-release-') |
- yield name_group |
- |
- @classmethod |
- def process(cls, config, name, branches=None, blacklist=None): |
- """Construct an UpdateInfo to map a source name. |
- |
- This function works by attempting to transform a source name into a known |
- 'cbuildbot' target name. If successful, it will use that successful |
- transformation as validation of the correctness and return an UpdateInfo |
- describing the transformation. |
- |
- Args: |
- config (cros_chromite.ChromiteConfig) The Chromite config instance. |
- name (str): The source name to process and map. |
- branches (list): A list of valid branches, extracted from 'cros_chromite'. |
- Returns (UpdateInfo/None): The constructed UpdateInfo, or None if there was |
- no identified mapping. |
- """ |
- def sliding_split_gen(): |
- parts = name.split('-') |
- for i in xrange(len(parts), 0, -1): |
- yield '-'.join(parts[:i]), '-'.join(parts[i:]) |
- |
- logging.debug("Processing candidate name: %s", name) |
- candidates = set() |
- branch = None |
- for orig_name, branch in sliding_split_gen(): |
- logging.debug("Trying construction: Name(%s), Branch(%s)", |
- orig_name, branch) |
- if branches and not branch in branches: |
- logging.debug("Ignoring branch value '%s'.", branch) |
- continue |
- |
- # See if we can properly permute the original name. |
- for permuted_name in cls.permutations(orig_name): |
- if blacklist and any(b in permuted_name for b in blacklist): |
- logging.debug("Ignoring blacklisted config name: %s", permuted_name) |
- continue |
- if permuted_name in config: |
- candidates.add(permuted_name) |
- if not candidates: |
- logging.debug("No 'cbuildbot' config for attempts [%s] branch [%s].", |
- orig_name, branch) |
- continue |
- |
- # We've found a permutation that matches a 'cbuildbot' target. |
- break |
- else: |
- logging.info("No 'cbuildbot' permutations for [%s].", name) |
- return None |
- |
- if not branch: |
- # We need to do an update to add the branch. Default to 'master'. |
- branch = 'master' |
- |
- candidates = sorted(candidates) |
- for candidate in candidates: |
- logging.debug("Identified 'cbuildbot' name [%s] => [%s] branch [%s].", |
- name, candidate, branch) |
- return [cls(name, p, branch) for p in candidates] |
- |
- |
-def main(args): |
- """Main execution function. |
- |
- Args: |
- args (list): Command-line argument array. |
- """ |
- parser = argparse.ArgumentParser() |
- parser.add_argument('path', nargs='+', metavar='PATH', |
- help='The path to the master directory to process.') |
- parser.add_argument('-v', '--verbose', action='count', default=0, |
- help='Increase verbosity. Can be specified multiple times.') |
- parser.add_argument('-d', '--dry-run', action='store_true', |
- help="Print what actions will be taken, but don't modify anything.") |
- parser.add_argument('-n', '--names', action='store_true', |
- help="If specified, then regard 'path' as directory names to test.") |
- parser.add_argument('-B', '--blacklist', action='append', default=[], |
- help="Blacklist configs that contain this text.") |
- args = parser.parse_args() |
- |
- # Select verbosity. |
- if args.verbose == 0: |
- loglevel = logging.WARNING |
- elif args.verbose == 1: |
- loglevel = logging.INFO |
- else: |
- loglevel = logging.DEBUG |
- logging.getLogger().setLevel(loglevel) |
- |
- # Load all availables Chromite configs. We're going to load ToT. |
- config_names = set() |
- branches = set() |
- for branch in cros_chromite.PINS.iterkeys(): |
- branches.add(branch) |
- config_names.update(cros_chromite.Get(branch=branch).iterkeys()) |
- |
- # If we're just testing against names, do that. |
- if args.names: |
- errors = 0 |
- for n in args.path: |
- update_info_list = UpdateInfo.process(config_names, n, branches=branches, |
- blacklist=args.blacklist) |
- if update_info_list: |
- for update_info in update_info_list: |
- logging.warning("[%s] => [%s]", update_info.src, update_info.dst) |
- else: |
- logging.warning("No transformation for name [%s].", n) |
- errors += 1 |
- return errors |
- |
- # Construct the set of actions to take. |
- cbb_already = set() |
- unmatched = set() |
- multiples = {} |
- updates = [] |
- for path in args.path: |
- if not os.path.isdir(path): |
- raise ValueError("Supplied master directory is not valid: %s" % (path,)) |
- |
- seen = set() |
- for f in os.listdir(path): |
- f_path = os.path.join(path, f) |
- if not os.path.isdir(f_path): |
- continue |
- |
- update_info_list = UpdateInfo.process(config_names, f, branches=branches, |
- blacklist=args.blacklist) |
- if not update_info_list: |
- logging.info("No update information for directory [%s]", f) |
- unmatched.add(f) |
- continue |
- elif len(update_info_list) != 1: |
- multiples[f] = update_info_list |
- continue |
- update_info = update_info_list[0] |
- |
- # Make sure that we don't stomp on directory names. This shouldn't happen, |
- # since the mapping to 'cbuildbot' names is inherently deconflicting, but |
- # it's good to assert it just in case. |
- update_info_names = set((update_info.src, update_info.dst)) |
- if update_info_names.intersection(seen): |
- logging.error("Updated names intersect with existing names: %s", |
- ", ".join(update_info_names.intersection(seen))) |
- return 1 |
- seen.update(update_info_names) |
- |
- # We are already in <cbuildbot>-<branch> format, so do nothing. |
- if update_info.src == update_info.dst: |
- cbb_already.add(update_info.src) |
- else: |
- updates.append((path, update_info)) |
- |
- # Execute the updates. |
- logging.info("Executing %d updates.", len(updates)) |
- for master_dir, update_info in updates: |
- logging.info("Updating [%s]: [%s] => [%s]", master_dir, update_info.src, |
- update_info.dst) |
- if not args.dry_run: |
- shutil.move(os.path.join(master_dir, update_info.src), |
- os.path.join(master_dir, update_info.dst)) |
- logging.info("Updated %d directories.", len(updates)) |
- if logging.getLogger().isEnabledFor(logging.DEBUG): |
- logging.debug("%d directories already matching: %s", |
- len(cbb_already), ', '.join(sorted(cbb_already))) |
- if unmatched: |
- logging.warning("%d unmatched directories: %s", |
- len(unmatched), ', '.join(sorted(unmatched))) |
- if multiples: |
- for f in sorted(multiples.iterkeys()): |
- logging.warning("Multiple permutations of [%s]: %s\n%s", |
- f, ", ".join(m.dst for m in multiples[f]), |
- '\n'.join('mv %s %s' % (f, m.dst) for m in multiples[f])) |
- return 0 |
- |
- |
-if __name__ == '__main__': |
- logging.basicConfig() |
- sys.exit(main(sys.argv[1:])) |