summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Thursfield <sam.thursfield@codethink.co.uk>2014-10-08 13:21:20 +0100
committerSam Thursfield <sam.thursfield@codethink.co.uk>2014-10-08 13:21:20 +0100
commitaa68a0aa5917639dd1b7f67aa21c8d8a2755459a (patch)
tree19d6bb0d013792987c9b6989519cc9d9790abacb
parent12e2cc8f3fdc7ee65cc1e50fe8acbb652d2ca866 (diff)
downloadmorph-aa68a0aa5917639dd1b7f67aa21c8d8a2755459a.tar.gz
import: Move the processing loop into its own class
This allows keeping the state as members instead of having to pass 12+ parameters to functions. Also in this change: you can now specify 'extra_args' for a tool which are passed at the start of the commandline. Some tools (e.g. Omnibus) require more than just the source repo and name to generate a chunk morph or lorry file.
-rw-r--r--import/main.py582
1 files changed, 293 insertions, 289 deletions
diff --git a/import/main.py b/import/main.py
index 684f7060..af7d6979 100644
--- a/import/main.py
+++ b/import/main.py
@@ -29,7 +29,6 @@ import json
import logging
import os
import pipes
-import subprocess
import sys
import time
@@ -337,144 +336,117 @@ def run_extension(filename, args, cwd='.'):
return '\n'.join(output)
-class BaserockImportApplication(cliapp.Application):
- def add_settings(self):
- self.settings.string(['lorries-dir'],
- "location for Lorry files",
- metavar="PATH",
- default=os.path.abspath('./lorries'))
- self.settings.string(['definitions-dir'],
- "location for morphology files",
- metavar="PATH",
- default=os.path.abspath('./definitions'))
- self.settings.string(['checkouts-dir'],
- "location for Git checkouts",
- metavar="PATH",
- default=os.path.abspath('./checkouts'))
+class ImportLoop(object):
+ '''Import a package and all of its dependencies into Baserock.
- self.settings.boolean(['update-existing'],
- "update all the checked-out Git trees and "
- "generated definitions",
- default=False)
- self.settings.boolean(['use-master-if-no-tag'],
- "if the correct tag for a version can't be "
- "found, use 'master' instead of raising an "
- "error",
- default=False)
+ This class holds the state for the processing loop.
- def _stream_has_colours(self, stream):
- # http://blog.mathieu-leplatre.info/colored-output-in-console-with-python.html
- if not hasattr(stream, "isatty"):
- return False
- if not stream.isatty():
- return False # auto color only on TTYs
- try:
- import curses
- curses.setupterm()
- return curses.tigetnum("colors") > 2
- except:
- # guess false in case of error
- return False
+ '''
- def setup(self):
- self.add_subcommand('omnibus', self.import_omnibus,
- arg_synopsis='REPO PROJECT_NAME SOFTWARE_NAME')
- self.add_subcommand('rubygems', self.import_rubygems,
- arg_synopsis='GEM_NAME')
+ def __init__(self, app, kind, goal_name, goal_version, extra_args=[]):
+ self.app = app
+ self.kind = kind
+ self.goal_name = goal_name
+ self.goal_version = goal_version
+ self.extra_args = extra_args
- self.stdout_has_colours = self._stream_has_colours(sys.stdout)
+ self.lorry_set = LorrySet(self.app.settings['lorries-dir'])
+ self.morph_set = MorphologySet(self.app.settings['definitions-dir'])
- def setup_logging_formatter_for_file(self):
- root_logger = logging.getLogger()
- root_logger.name = 'main'
+ self.morphloader = morphlib.morphloader.MorphologyLoader()
- # You need recent cliapp for this to work, with commit "Split logging
- # setup into further overrideable methods".
- return logging.Formatter("%(name)s: %(levelname)s: %(message)s")
+ def run(self):
+ '''Process the goal package and all of its dependencies.'''
+ start_time = time.time()
+ start_displaytime = time.strftime('%x %X %Z', time.localtime())
- def process_args(self, args):
- if len(args) == 0:
- # Cliapp default is to just say "ERROR: must give subcommand" if
- # no args are passed, I prefer this.
- args = ['help']
+ self.app.status(
+ '%s: Import of %s %s started', start_displaytime, self.kind,
+ self.goal_name)
- super(BaserockImportApplication, self).process_args(args)
+ if not self.app.settings['update-existing']:
+ self.app.status(
+ 'Not updating existing Git checkouts or existing definitions')
- def status(self, msg, *args, **kwargs):
- text = msg % args
- if kwargs.get('error') == True:
- logging.error(text)
- if self.stdout_has_colours:
- sys.stdout.write(ansicolor.red(text))
- else:
- sys.stdout.write(text)
- else:
- logging.info(text)
- sys.stdout.write(text)
- sys.stdout.write('\n')
+ chunk_dir = os.path.join(self.morph_set.path, 'strata', self.goal_name)
+ if not os.path.exists(chunk_dir):
+ os.makedirs(chunk_dir)
- def import_omnibus(self, args):
- '''Import a software component from an Omnibus project.
+ to_process = [Package(self.goal_name, self.goal_version)]
+ processed = networkx.DiGraph()
- Omnibus is a tool for generating application bundles for various
- platforms. See <https://github.com/opscode/omnibus> for more
- information.
+ errors = {}
- '''
- if len(args) != 3:
- raise cliapp.AppException(
- 'Please give the location of the Omnibus definitions repo, '
- 'and the name of the project and the top-level software '
- 'component.')
+ while len(to_process) > 0:
+ current_item = to_process.pop()
- def running_inside_bundler():
- return 'BUNDLE_GEMFILE' in os.environ
+ try:
+ build_deps, runtime_deps = self._process_package(current_item)
+ except BaserockImportException as e:
+ self.app.status(str(e), error=True)
+ errors[current_item] = e
+ build_deps = runtime_deps = {}
- def command_to_run_python_in_directory(directory, args):
- # Bundler requires that we run it from the Omnibus project
- # directory. That messes up any relative paths the user may have
- # passed on the commandline, so we do a bit of a hack to change
- # back to the original directory inside the `bundle exec` process.
- subshell_command = "(cd %s; exec python %s)" % \
- (pipes.quote(directory), ' '.join(map(pipes.quote, args)))
- shell_command = "sh -c %s" % pipes.quote(subshell_command)
- return shell_command
+ processed.add_node(current_item)
- def reexecute_self_with_bundler(path):
- script = sys.argv[0]
+ self._process_dependency_list(
+ current_item, build_deps, to_process, processed, True)
+ self._process_dependency_list(
+ current_item, runtime_deps, to_process, processed, False)
- logging.info('Reexecuting %s within Bundler, so that extensions '
- 'use the correct dependencies for Omnibus and the '
- 'Omnibus project definitions.', script)
- command = command_to_run_python_in_directory(os.getcwd(), sys.argv)
+ if len(errors) > 0:
+ self.app.status(
+ '\nErrors encountered, not generating a stratum morphology.')
+ self.app.status(
+ 'See the README files for guidance.')
+ else:
+ self._generate_stratum_morph_if_none_exists(
+ processed, self.goal_name)
- logging.debug('Running: `bundle exec %s` in dir %s', command, path)
- os.chdir(path)
- os.execvp('bundle', [script, 'exec', command])
+ duration = time.time() - start_time
+ end_displaytime = time.strftime('%x %X %Z', time.localtime())
- # Omnibus definitions are spread across multiple repos, and there is
- # no stability guarantee for the definition format. The official advice
- # is to use Bundler to execute Omnibus, so let's do that.
- if not running_inside_bundler():
- reexecute_self_with_bundler(args[0])
+ self.app.status(
+ '%s: Import of %s %s ended (took %i seconds)', end_displaytime,
+ self.kind, self.goal_name, duration)
- # This is a slightly unhappy way of passing both the repo path and
- # the project name in one argument.
- definitions_dir = '%s#%s' % (args[0], args[1])
+ def _get_dependencies_from_morphology(self, morphology, field_name):
+ # We need to validate this field because it doesn't go through the
+ # normal MorphologyFactory validation, being an extension.
+ value = morphology.get(field_name, {})
+ if not hasattr(value, 'iteritems'):
+ value_type = type(value).__name__
+ raise cliapp.AppException(
+ "Morphology for %s has invalid '%s': should be a dict, but "
+ "got a %s." % (morphology['name'], field_name, value_type))
- self.import_package_and_all_dependencies(
- 'omnibus', args[2], definitions_dir=definitions_dir)
+ return value
- def import_rubygems(self, args):
- '''Import one or more RubyGems.'''
- if len(args) != 1:
- raise cliapp.AppException(
- 'Please pass the name of a RubyGem on the commandline.')
+ def _process_package(self, package):
+ name = package.name
+ version = package.version
+
+ lorry = self._find_or_create_lorry_file(name)
+ source_repo, url = self._fetch_or_update_source(lorry)
+
+ checked_out_version, ref = self._checkout_source_version(
+ source_repo, name, version)
+ package.set_version_in_use(checked_out_version)
+
+ chunk_morph = self._find_or_create_chunk_morph(
+ name, checked_out_version, source_repo, url, ref)
+
+ package.set_morphology(chunk_morph)
- self.import_package_and_all_dependencies('rubygems', args[0])
+ build_deps = self._get_dependencies_from_morphology(
+ chunk_morph, 'x-build-dependencies-%s' % self.kind)
+ runtime_deps = self._get_dependencies_from_morphology(
+ chunk_morph, 'x-runtime-dependencies-%s' % self.kind)
- def process_dependency_list(self, current_item, deps, to_process,
- processed, these_are_build_deps):
+ return build_deps, runtime_deps
+
+ def _process_dependency_list(self, current_item, deps, to_process,
+ processed, these_are_build_deps):
# All deps are added as nodes to the 'processed' graph. Runtime
# dependencies only need to appear in the stratum, but build
# dependencies have ordering constraints, so we add edges in
@@ -501,132 +473,15 @@ class BaserockImportApplication(cliapp.Application):
dep_package.set_is_build_dep(True)
processed.add_edge(dep_package, current_item)
- def get_dependencies_from_morphology(self, morphology, field_name):
- # We need to validate this field because it doesn't go through the
- # normal MorphologyFactory validation, being an extension.
- value = morphology.get(field_name, {})
- if not hasattr(value, 'iteritems'):
- value_type = type(value).__name__
- raise cliapp.AppException(
- "Morphology for %s has invalid '%s': should be a dict, but "
- "got a %s." % (morphology['name'], field_name, value_type))
-
- return value
-
- def import_package_and_all_dependencies(self, kind, goal_name,
- goal_version='master',
- definitions_dir=None):
- start_time = time.time()
- start_displaytime = time.strftime('%x %X %Z', time.localtime())
-
- self.status('%s: Import of %s %s started', start_displaytime, kind,
- goal_name)
-
- if not self.settings['update-existing']:
- self.status('Not updating existing Git checkouts or existing definitions')
-
- lorry_set = LorrySet(self.settings['lorries-dir'])
- morph_set = MorphologySet(self.settings['definitions-dir'])
-
- chunk_dir = os.path.join(morph_set.path, 'strata', goal_name)
- if not os.path.exists(chunk_dir):
- os.makedirs(chunk_dir)
-
- to_process = [Package(goal_name, goal_version)]
- processed = networkx.DiGraph()
-
- errors = {}
-
- while len(to_process) > 0:
- current_item = to_process.pop()
- name = current_item.name
- version = current_item.version
-
- try:
- lorry = self.find_or_create_lorry_file(lorry_set, kind, name,
- definitions_dir=definitions_dir)
-
- source_repo, url = self.fetch_or_update_source(lorry)
-
- if definitions_dir is None:
- # Package is defined in the project's actual repo.
- package_definition_repo = source_repo
- checked_out_version, ref = self.checkout_source_version(
- source_repo, name, version)
- current_item.set_version_in_use(checked_out_version)
- else:
- # FIXME: we do actually need to make the package source
- # available too so we can detect which SHA1 to build.
- # For now we lie
- checked_out_version = 'lie'
- ref = 'lie'
- # If 'definitions_dir' was passed, we assume all packages
- # are defined in the same repo.
- package_definition_repo = GitDirectory(definitions_dir)
-
- chunk_morph = self.find_or_create_chunk_morph(
- morph_set, goal_name, kind, name, checked_out_version,
- package_definition_repo, url, ref, definitions_dir)
-
- current_item.set_morphology(chunk_morph)
-
- build_deps = self.get_dependencies_from_morphology(
- chunk_morph, 'x-build-dependencies-%s' % kind)
- runtime_deps = self.get_dependencies_from_morphology(
- chunk_morph, 'x-runtime-dependencies-%s' % kind)
- except BaserockImportException as e:
- self.status(str(e), error=True)
- errors[current_item] = e
- build_deps = runtime_deps = {}
-
- processed.add_node(current_item)
-
- self.process_dependency_list(
- current_item, build_deps, to_process, processed, True)
- self.process_dependency_list(
- current_item, runtime_deps, to_process, processed, False)
-
- if len(errors) > 0:
- self.status(
- '\nErrors encountered, not generating a stratum morphology.')
- self.status(
- 'See the README files for guidance.')
- else:
- self.generate_stratum_morph_if_none_exists(processed, goal_name)
-
- duration = time.time() - start_time
- end_displaytime = time.strftime('%x %X %Z', time.localtime())
-
- self.status('%s: Import of %s %s ended (took %i seconds)',
- end_displaytime, kind, goal_name, duration)
-
- def generate_lorry_for_package(self, kind, name, definitions_dir):
- tool = '%s.to_lorry' % kind
- self.status('Calling %s to generate lorry for %s', tool, name)
- if definitions_dir is None:
- cwd = '.'
- args = [name]
- else:
- cwd = definitions_dir.rsplit('#')[0]
- args = [definitions_dir, name]
- lorry_text = run_extension(tool, args, cwd=cwd)
- try:
- lorry = json.loads(lorry_text)
- except ValueError as e:
- raise cliapp.AppException(
- 'Invalid output from %s: %s' % (tool, lorry_text))
- return lorry
-
- def find_or_create_lorry_file(self, lorry_set, kind, name,
- definitions_dir):
+ def _find_or_create_lorry_file(self, name):
# Note that the lorry file may already exist for 'name', but lorry
# files are named for project name rather than package name. In this
# case we will generate the lorry, and try to add it to the set, at
# which point LorrySet will notice the existing one and merge the two.
- lorry = lorry_set.find_lorry_for_package(kind, name)
+ lorry = self.lorry_set.find_lorry_for_package(self.kind, name)
if lorry is None:
- lorry = self.generate_lorry_for_package(kind, name, definitions_dir)
+ lorry = self._generate_lorry_for_package(name)
if len(lorry) != 1:
raise Exception(
@@ -645,7 +500,7 @@ class BaserockImportApplication(cliapp.Application):
raise cliapp.AppException(
'Invalid lorry data for %s: %s' % (name, lorry))
- lorry_set.add(lorry_filename, lorry)
+ self.lorry_set.add(lorry_filename, lorry)
else:
lorry_filename = lorry.keys()[0]
logging.info(
@@ -653,23 +508,34 @@ class BaserockImportApplication(cliapp.Application):
return lorry
- def fetch_or_update_source(self, lorry):
+ def _generate_lorry_for_package(self, name):
+ tool = '%s.to_lorry' % self.kind
+ self.app.status('Calling %s to generate lorry for %s', tool, name)
+ lorry_text = run_extension(tool, self.extra_args + [name])
+ try:
+ lorry = json.loads(lorry_text)
+ except ValueError as e:
+ raise cliapp.AppException(
+ 'Invalid output from %s: %s' % (tool, lorry_text))
+ return lorry
+
+ def _fetch_or_update_source(self, lorry):
assert len(lorry) == 1
lorry_entry = lorry.values()[0]
url = lorry_entry['url']
reponame = os.path.basename(url.rstrip('/'))
- repopath = os.path.join(self.settings['checkouts-dir'], reponame)
+ repopath = os.path.join(self.app.settings['checkouts-dir'], reponame)
# FIXME: we should use Lorry here, so that we can import other VCSes.
# But for now, this hack is fine!
if os.path.exists(repopath):
- if self.settings['update-existing']:
- self.status('Updating repo %s', url)
+ if self.app.settings['update-existing']:
+ self.app.status('Updating repo %s', url)
cliapp.runcmd(['git', 'remote', 'update', 'origin'],
cwd=repopath)
else:
- self.status('Cloning repo %s', url)
+ self.app.status('Cloning repo %s', url)
try:
cliapp.runcmd(['git', 'clone', '--quiet', url, repopath])
except cliapp.AppException as e:
@@ -687,7 +553,7 @@ class BaserockImportApplication(cliapp.Application):
'%s exists but is not the root of a Git repository' % repopath)
return repo, url
- def checkout_source_version(self, source_repo, name, version):
+ def _checkout_source_version(self, source_repo, name, version):
# FIXME: we need to be a bit smarter than this. Right now we assume
# that 'version' is a valid Git ref.
@@ -703,7 +569,7 @@ class BaserockImportApplication(cliapp.Application):
ref = tag_name
break
else:
- if self.settings['use-master-if-no-tag']:
+ if self.app.settings['use-master-if-no-tag']:
logging.warning(
"Couldn't find tag %s in repo %s. Using 'master'.",
tag_name, source_repo)
@@ -715,45 +581,24 @@ class BaserockImportApplication(cliapp.Application):
return version, ref
- def generate_chunk_morph_for_package(self, kind, source_repo, name,
- version, filename, definitions_dir):
- tool = '%s.to_chunk' % kind
- self.status('Calling %s to generate chunk morph for %s', tool, name)
-
- if definitions_dir is None:
- cwd = '.'
- args = [source_repo.dirname, name]
- else:
- cwd = definitions_dir.rsplit('#')[0]
- args = [definitions_dir, name]
-
- if version != 'master':
- args.append(version)
-
- text = run_extension(tool, args, cwd=cwd)
-
- loader = morphlib.morphloader.MorphologyLoader()
- return loader.load_from_string(text, filename)
-
- def find_or_create_chunk_morph(self, morph_set, goal_name, kind, name,
- version, source_repo, repo_url, named_ref,
- definitions_dir):
+ def _find_or_create_chunk_morph(self, name, version, source_repo, repo_url,
+ named_ref):
morphology_filename = 'strata/%s/%s-%s.morph' % (
- goal_name, name, version)
+ self.goal_name, name, version)
+ # HACK so omnibus works!
#sha1 = source_repo.resolve_ref_to_commit(named_ref)
sha1 = 1
def generate_morphology():
- morphology = self.generate_chunk_morph_for_package(
- kind, source_repo, name, version, morphology_filename,
- definitions_dir)
- morph_set.save_morphology(morphology_filename, morphology)
+ morphology = self._generate_chunk_morph_for_package(
+ source_repo, name, version, morphology_filename)
+ self.morph_set.save_morphology(morphology_filename, morphology)
return morphology
- if self.settings['update-existing']:
+ if self.app.settings['update-existing']:
morphology = generate_morphology()
else:
- morphology = morph_set.get_morphology(
+ morphology = self.morph_set.get_morphology(
repo_url, sha1, morphology_filename)
if morphology is None:
@@ -762,13 +607,13 @@ class BaserockImportApplication(cliapp.Application):
# morph. So the first time we touch a chunk morph we need to
# set this info.
logging.debug("Didn't find morphology for %s|%s|%s", repo_url,
- sha1, morphology_filename)
- morphology = morph_set.get_morphology(
+ sha1, morphology_filename)
+ morphology = self.morph_set.get_morphology(
None, None, morphology_filename)
if morphology is None:
logging.debug("Didn't find morphology for None|None|%s",
- morphology_filename)
+ morphology_filename)
morphology = generate_morphology()
morphology.repo_url = repo_url
@@ -777,7 +622,20 @@ class BaserockImportApplication(cliapp.Application):
return morphology
- def sort_chunks_by_build_order(self, graph):
+ def _generate_chunk_morph_for_package(self, source_repo, name, version,
+ filename):
+ tool = '%s.to_chunk' % self.kind
+ self.app.status(
+ 'Calling %s to generate chunk morph for %s', tool, name)
+
+ args = self.extra_args + [source_repo.dirname, name]
+ if version != 'master':
+ args.append(version)
+ text = run_extension(tool, args)
+
+ return self.morphloader.load_from_string(text, filename)
+
+ def _sort_chunks_by_build_order(self, graph):
order = reversed(sorted(graph.nodes()))
try:
return networkx.topological_sort(graph, nbunch=order)
@@ -794,20 +652,22 @@ class BaserockImportApplication(cliapp.Application):
'One or more cycles detected in build graph: %s' %
(', '.join(all_loops_str)))
- def generate_stratum_morph_if_none_exists(self, graph, goal_name):
+ def _generate_stratum_morph_if_none_exists(self, graph, goal_name):
filename = os.path.join(
- self.settings['definitions-dir'], 'strata', '%s.morph' % goal_name)
+ self.app.settings['definitions-dir'], 'strata', '%s.morph' %
+ goal_name)
if os.path.exists(filename) and not self.settings['update-existing']:
- self.status(msg='Found stratum morph for %s at %s, not overwriting'
- % (goal_name, filename))
+ self.app.status(
+ msg='Found stratum morph for %s at %s, not overwriting' %
+ (goal_name, filename))
return
- self.status(msg='Generating stratum morph for %s' % goal_name)
+ self.app.status(msg='Generating stratum morph for %s' % goal_name)
chunk_entries = []
- for package in self.sort_chunks_by_build_order(graph):
+ for package in self._sort_chunks_by_build_order(graph):
m = package.morphology
if m is None:
raise cliapp.AppException('No morphology for %s' % package)
@@ -842,12 +702,156 @@ class BaserockImportApplication(cliapp.Application):
'chunks': chunk_entries,
}
- loader = morphlib.morphloader.MorphologyLoader()
- morphology = loader.load_from_string(json.dumps(stratum),
- filename=filename)
+ morphology = self.morphloader.load_from_string(
+ json.dumps(stratum), filename=filename)
+ self.morphloader.unset_defaults(morphology)
+ self.morphloader.save_to_file(filename, morphology)
+
+
+class BaserockImportApplication(cliapp.Application):
+ def add_settings(self):
+ self.settings.string(['lorries-dir'],
+ "location for Lorry files",
+ metavar="PATH",
+ default=os.path.abspath('./lorries'))
+ self.settings.string(['definitions-dir'],
+ "location for morphology files",
+ metavar="PATH",
+ default=os.path.abspath('./definitions'))
+ self.settings.string(['checkouts-dir'],
+ "location for Git checkouts",
+ metavar="PATH",
+ default=os.path.abspath('./checkouts'))
+
+ self.settings.boolean(['update-existing'],
+ "update all the checked-out Git trees and "
+ "generated definitions",
+ default=False)
+ self.settings.boolean(['use-master-if-no-tag'],
+ "if the correct tag for a version can't be "
+ "found, use 'master' instead of raising an "
+ "error",
+ default=False)
+
+ def _stream_has_colours(self, stream):
+ # http://blog.mathieu-leplatre.info/colored-output-in-console-with-python.html
+ if not hasattr(stream, "isatty"):
+ return False
+ if not stream.isatty():
+ return False # auto color only on TTYs
+ try:
+ import curses
+ curses.setupterm()
+ return curses.tigetnum("colors") > 2
+ except:
+ # guess false in case of error
+ return False
+
+ def setup(self):
+ self.add_subcommand('omnibus', self.import_omnibus,
+ arg_synopsis='REPO PROJECT_NAME SOFTWARE_NAME')
+ self.add_subcommand('rubygems', self.import_rubygems,
+ arg_synopsis='GEM_NAME')
+
+ self.stdout_has_colours = self._stream_has_colours(sys.stdout)
+
+ def setup_logging_formatter_for_file(self):
+ root_logger = logging.getLogger()
+ root_logger.name = 'main'
+
+ # You need recent cliapp for this to work, with commit "Split logging
+ # setup into further overrideable methods".
+ return logging.Formatter("%(name)s: %(levelname)s: %(message)s")
+
+ def process_args(self, args):
+ if len(args) == 0:
+ # Cliapp default is to just say "ERROR: must give subcommand" if
+ # no args are passed, I prefer this.
+ args = ['help']
+
+ super(BaserockImportApplication, self).process_args(args)
+
+ def status(self, msg, *args, **kwargs):
+ text = msg % args
+ if kwargs.get('error') == True:
+ logging.error(text)
+ if self.stdout_has_colours:
+ sys.stdout.write(ansicolor.red(text))
+ else:
+ sys.stdout.write(text)
+ else:
+ logging.info(text)
+ sys.stdout.write(text)
+ sys.stdout.write('\n')
+
+ def import_omnibus(self, args):
+ '''Import a software component from an Omnibus project.
+
+ Omnibus is a tool for generating application bundles for various
+ platforms. See <https://github.com/opscode/omnibus> for more
+ information.
+
+ '''
+ if len(args) != 3:
+ raise cliapp.AppException(
+ 'Please give the location of the Omnibus definitions repo, '
+ 'and the name of the project and the top-level software '
+ 'component.')
+
+ def running_inside_bundler():
+ return 'BUNDLE_GEMFILE' in os.environ
+
+ def command_to_run_python_in_directory(directory, args):
+ # Bundler requires that we run it from the Omnibus project
+ # directory. That messes up any relative paths the user may have
+ # passed on the commandline, so we do a bit of a hack to change
+ # back to the original directory inside the `bundle exec` process.
+ subshell_command = "(cd %s; exec python %s)" % \
+ (pipes.quote(directory), ' '.join(map(pipes.quote, args)))
+ shell_command = "sh -c %s" % pipes.quote(subshell_command)
+ return shell_command
+
+ def reexecute_self_with_bundler(path):
+ script = sys.argv[0]
+
+ logging.info('Reexecuting %s within Bundler, so that extensions '
+ 'use the correct dependencies for Omnibus and the '
+ 'Omnibus project definitions.', script)
+ command = command_to_run_python_in_directory(os.getcwd(), sys.argv)
+
+ logging.debug('Running: `bundle exec %s` in dir %s', command, path)
+ os.chdir(path)
+ os.execvp('bundle', [script, 'exec', command])
+
+ # Omnibus definitions are spread across multiple repos, and there is
+ # no stability guarantee for the definition format. The official advice
+ # is to use Bundler to execute Omnibus, so let's do that.
+ if not running_inside_bundler():
+ reexecute_self_with_bundler(args[0])
+
+ definitions_dir = args[0]
+ project_name = args[1]
+
+ ImportLoop(
+ app=self,
+ kind='omnibus',
+ goal_name=args[2],
+ goal_version='master',
+ extra_args=[definitions_dir, project_name]
+ ).run()
+
+ def import_rubygems(self, args):
+ '''Import one or more RubyGems.'''
+ if len(args) != 1:
+ raise cliapp.AppException(
+ 'Please pass the name of a RubyGem on the commandline.')
- loader.unset_defaults(morphology)
- loader.save_to_file(filename, morphology)
+ ImportLoop(
+ app=self,
+ kind='rubygems',
+ goal_name=args[0],
+ goal_version='master'
+ ).run()
app = BaserockImportApplication(progname='import')