diff options
Diffstat (limited to 'morphlib/plugins')
-rw-r--r-- | morphlib/plugins/artifact_inspection_plugin.py | 5 | ||||
-rw-r--r-- | morphlib/plugins/branch_and_merge_new_plugin.py | 199 | ||||
-rw-r--r-- | morphlib/plugins/branch_and_merge_plugin.py | 15 |
3 files changed, 158 insertions, 61 deletions
diff --git a/morphlib/plugins/artifact_inspection_plugin.py b/morphlib/plugins/artifact_inspection_plugin.py index 6c321abb..9181d488 100644 --- a/morphlib/plugins/artifact_inspection_plugin.py +++ b/morphlib/plugins/artifact_inspection_plugin.py @@ -78,7 +78,8 @@ class AutotoolsVersionGuesser(ProjectVersionGuesser): break # Then, try running autoconf against the configure script - version = self._check_autoconf_package_version(filename, data) + version = self._check_autoconf_package_version( + repo, ref, filename, data) if version: self.app.status( msg='%(repo)s: Version of %(ref)s detected ' @@ -107,7 +108,7 @@ class AutotoolsVersionGuesser(ProjectVersionGuesser): return version return None - def _check_autoconf_package_version(self, filename, data): + def _check_autoconf_package_version(self, repo, ref, filename, data): tempdir = morphlib.tempdir.Tempdir(self.app.settings['tempdir']) with open(tempdir.join(filename), 'w') as f: f.write(data) diff --git a/morphlib/plugins/branch_and_merge_new_plugin.py b/morphlib/plugins/branch_and_merge_new_plugin.py index 39552ef0..0a43b918 100644 --- a/morphlib/plugins/branch_and_merge_new_plugin.py +++ b/morphlib/plugins/branch_and_merge_new_plugin.py @@ -15,6 +15,7 @@ import cliapp +import contextlib import glob import logging import os @@ -55,6 +56,15 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): arg_synopsis='-- COMMAND [ARGS...]') self.app.add_subcommand('status', self.status, arg_synopsis='') + self.app.add_subcommand('branch-from-image', self.branch_from_image, + arg_synopsis='BRANCH') + group_branch = 'Branching Options' + self.app.settings.string(['metadata-dir'], + 'Set metadata location for branch-from-image' + ' (default: /baserock)', + metavar='DIR', + default='/baserock', + group=group_branch) def disable(self): pass @@ -97,6 +107,40 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): ws = morphlib.workspace.open('.') self.app.output.write('%s\n' % ws.root) + # TODO: Move this somewhere nicer + @contextlib.contextmanager + def _initializing_system_branch(self, ws, root_url, system_branch, + cached_repo, base_ref): + '''A context manager for system branches under construction. + + The purpose of this context manager is to factor out the branch + cleanup code for if an exception occurs while a branch is being + constructed. + + This could be handled by a higher order function which takes + a function to initialize the branch as a parameter, but with + statements look nicer and are more obviously about resource + cleanup. + + ''' + root_dir = ws.get_default_system_branch_directory_name(system_branch) + try: + sb = morphlib.sysbranchdir.create( + root_dir, root_url, system_branch) + gd = sb.clone_cached_repo(cached_repo, base_ref) + + yield (sb, gd) + + gd.update_submodules(self.app) + gd.update_remotes() + + except BaseException as e: + # Oops. Clean up. + logging.error('Caught exception: %s' % str(e)) + logging.info('Removing half-finished branch %s' % system_branch) + self._remove_branch_dir_safe(ws.root, root_dir) + raise + def checkout(self, args): '''Check out an existing system branch. @@ -124,6 +168,7 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): root_url = args[0] system_branch = args[1] + base_ref = system_branch self._require_git_user_config() @@ -139,27 +184,12 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): # Check the git branch exists. cached_repo.resolve_ref(system_branch) - root_dir = ws.get_default_system_branch_directory_name(system_branch) - - try: - # Create the system branch directory. This doesn't yet clone - # the root repository there. - sb = morphlib.sysbranchdir.create( - root_dir, root_url, system_branch) - - gd = sb.clone_cached_repo(cached_repo, system_branch) + with self._initializing_system_branch( + ws, root_url, system_branch, cached_repo, base_ref) as (sb, gd): if not self._checkout_has_systems(gd): - raise BranchRootHasNoSystemsError(root_url, system_branch) + raise BranchRootHasNoSystemsError(base_ref) - gd.update_submodules(self.app) - gd.update_remotes() - except BaseException as e: - # Oops. Clean up. - logging.error('Caught exception: %s' % str(e)) - logging.info('Removing half-finished branch %s' % system_branch) - self._remove_branch_dir_safe(ws.root, root_dir) - raise def branch(self, args): '''Create a new system branch. @@ -212,29 +242,14 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): # Make sure the base_ref exists. cached_repo.resolve_ref(base_ref) - root_dir = ws.get_default_system_branch_directory_name(system_branch) + with self._initializing_system_branch( + ws, root_url, system_branch, cached_repo, base_ref) as (sb, gd): - try: - # Create the system branch directory. This doesn't yet clone - # the root repository there. - sb = morphlib.sysbranchdir.create( - root_dir, root_url, system_branch) - - gd = sb.clone_cached_repo(cached_repo, base_ref) gd.branch(system_branch, base_ref) gd.checkout(system_branch) if not self._checkout_has_systems(gd): - raise BranchRootHasNoSystemsError(root_url, base_ref) - - gd.update_submodules(self.app) - gd.update_remotes() - except BaseException as e: - # Oops. Clean up. - logging.error('Caught exception: %s' % str(e)) - logging.info('Removing half-finished branch %s' % system_branch) - self._remove_branch_dir_safe(ws.root, root_dir) - raise + raise BranchRootHasNoSystemsError(base_ref) def _save_dirty_morphologies(self, loader, sb, morphs): logging.debug('Saving dirty morphologies: start') @@ -266,7 +281,9 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): # of triplets (repo url, ref, filename). return [ - (spec['repo'], spec['ref'], '%s.morph' % spec['morph']) + (spec['repo'] or morph.repo_url, + spec['ref'] or morph.ref, + '%s.morph' % spec['morph']) for spec in specs ] @@ -417,16 +434,6 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): sb = morphlib.sysbranchdir.open_from_within('.') loader = morphlib.morphloader.MorphologyLoader() - # FIXME: The old "morph edit" code did its own morphology validation, - # which was much laxer than what MorphologyFactory does, or the - # new MorphologyLoader does. This new "morph edit" uses - # MorphologyLoader, and the stricter validation breaks the test - # suite. However, I want to keep the test suite as untouched as - # possible, until all the old code is gone (after which the test - # suite will be refactored). Thus, to work around the test suite - # breaking, we disable morphology validation for now. - loader.validate = lambda *args: None - # Load the system morphology, and all stratum morphologies, including # all the strata that are being build-depended on. @@ -679,6 +686,9 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): #TODO: Stop using app.resolve_ref def resolve_refs(morphs): for repo, ref in morphs.list_refs(): + # You can't resolve null refs, so don't attempt to. + if repo is None or ref is None: + continue # TODO: Handle refs that are only in workspace in general if (repo == sb.root_repository_url and ref == sb.system_branch_name): @@ -784,3 +794,98 @@ class SimpleBranchAndMergePlugin(cliapp.Plugin): if not has_uncommitted_changes: self.app.output.write("\nNo repos have outstanding changes.\n") + + def branch_from_image(self, args): + '''Produce a branch of an existing system image. + + Given the metadata specified by --metadata-dir, create a new + branch then petrify it to the state of the commits at the time + the system was built. + + If --metadata-dir is not specified, it defaults to your currently + running system. + + ''' + if len(args) != 1: + raise cliapp.AppException( + "branch-from-image needs exactly 1 argument " + "of the new system branch's name") + system_branch = args[0] + metadata_path = self.app.settings['metadata-dir'] + alias_resolver = morphlib.repoaliasresolver.RepoAliasResolver( + self.app.settings['repo-alias']) + + self._require_git_user_config() + + ws = morphlib.workspace.open('.') + + system, metadata = self._load_system_metadata(metadata_path) + resolved_refs = dict(self._resolve_refs_from_metadata(alias_resolver, + metadata)) + logging.debug('Resolved refs: %r' % resolved_refs) + base_ref = system['sha1'] + # The previous version would fall back to deducing this from the repo + # url and the repo alias resolver, but this does not always work, and + # new systems always have repo-alias in the metadata + root_url = system['repo-alias'] + + lrc, rrc = morphlib.util.new_repo_caches(self.app) + cached_repo = lrc.get_updated_repo(root_url) + + + with self._initializing_system_branch( + ws, root_url, system_branch, cached_repo, base_ref) as (sb, gd): + + # TODO: It's nasty to clone to a sha1 then create a branch + # of that sha1 then check it out, a nicer API may be the + # initial clone not checking out a branch at all, then + # the user creates and checks out their own branches + gd.branch(system_branch, base_ref) + gd.checkout(system_branch) + + loader = morphlib.morphloader.MorphologyLoader() + morphs = self._load_all_sysbranch_morphologies(sb, loader) + + morphs.repoint_refs(sb.root_repository_url, + sb.system_branch_name) + + morphs.petrify_chunks(resolved_refs) + + self._save_dirty_morphologies(loader, sb, morphs.morphologies) + + @staticmethod + def _load_system_metadata(path): + '''Load all metadata in `path` corresponding to a single System. + ''' + + smd = morphlib.systemmetadatadir.SystemMetadataDir(path) + metadata = smd.values() + systems = [md for md in metadata + if 'kind' in md and md['kind'] == 'system'] + + if not systems: + raise cliapp.AppException( + 'Metadata directory does not contain any systems.') + if len(systems) > 1: + raise cliapp.AppException( + 'Metadata directory contains multiple systems.') + system_metadatum = systems[0] + + metadata_cache_id_lookup = dict((md['cache-key'], md) + for md in metadata) + + return system_metadatum, metadata_cache_id_lookup + + @staticmethod + def _resolve_refs_from_metadata(alias_resolver, metadata): + '''Pre-resolve a set of refs from existing metadata. + + Given the metadata, generate a mapping of all the (repo, ref) + pairs defined in the metadata and the commit id they resolved to. + + ''' + for md in metadata.itervalues(): + repourls = set((md['repo-alias'], md['repo'])) + repourls.update(alias_resolver.aliases_from_url(md['repo'])) + for repourl in repourls: + yield ((repourl, md['original_ref']), md['sha1']) diff --git a/morphlib/plugins/branch_and_merge_plugin.py b/morphlib/plugins/branch_and_merge_plugin.py index 37d5e40c..fec16415 100644 --- a/morphlib/plugins/branch_and_merge_plugin.py +++ b/morphlib/plugins/branch_and_merge_plugin.py @@ -68,15 +68,9 @@ class BranchAndMergePlugin(cliapp.Plugin): self.app.add_subcommand('build', self.build, arg_synopsis='SYSTEM') self.app.add_subcommand('old-status', self.status) - self.app.add_subcommand('branch-from-image', self.branch_from_image, - arg_synopsis='REPO BRANCH') - group_branch = 'Branching Options' - self.app.settings.string(['metadata-dir'], - 'Set metadata location for branch-from-image' - ' (default: /baserock)', - metavar='DIR', - default='/baserock', - group=group_branch) + self.app.add_subcommand('old-branch-from-image', + self.branch_from_image, + arg_synopsis='REPO BRANCH') # Advanced commands self.app.add_subcommand('old-foreach', self.foreach, @@ -339,7 +333,6 @@ class BranchAndMergePlugin(cliapp.Plugin): required = { 'system': [ 'name', - 'system-kind', 'arch', 'strata', ], @@ -360,8 +353,6 @@ class BranchAndMergePlugin(cliapp.Plugin): 'system': [ 'kind', 'description', - 'disk-size', - '_disk-size', 'configuration-extensions', ], 'stratum': [ |