From 9c4095c2c668a71c6246e2e703ad759ec5b1a9ec Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Tue, 30 Sep 2014 18:22:08 +0100 Subject: import: Add initial Omnibus importer --- import/main.py | 55 ++++++++++++++++++++++--- import/omnibus.to_chunk | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 import/omnibus.to_chunk diff --git a/import/main.py b/import/main.py index 968c1dcf..5dfcbffb 100644 --- a/import/main.py +++ b/import/main.py @@ -26,6 +26,7 @@ import copy import json import logging import os +import subprocess import sys import time @@ -351,6 +352,8 @@ class BaserockImportApplication(cliapp.Application): default=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') @@ -374,6 +377,40 @@ class BaserockImportApplication(cliapp.Application): print msg % args logging.info(msg % args) + 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 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 reexecute_self_with_bundler(path): + subprocess.call(['bundle', 'exec', 'python'] + sys.argv, + cwd=path) + + # 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]) + + # 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]) + + self.import_package_and_all_dependencies( + 'omnibus', args[2], definitions_dir=definitions_dir) + def import_rubygems(self, args): '''Import one or more RubyGems.''' if len(args) != 1: @@ -423,7 +460,8 @@ class BaserockImportApplication(cliapp.Application): return value def import_package_and_all_dependencies(self, kind, goal_name, - goal_version='master'): + goal_version='master', + definitions_dir=None): start_time = time.time() start_displaytime = time.strftime('%x %X %Z', time.localtime()) @@ -455,12 +493,19 @@ class BaserockImportApplication(cliapp.Application): source_repo, url = self.fetch_or_update_source(lorry) - checked_out_version, ref = self.checkout_source_version( - source_repo, name, version) - current_item.set_version_in_use(checked_out_version) + if definitions_dir is None: + # Package is defined in the project's actual repo. + package_definition_dir = source_repo + checked_out_version, ref = self.checkout_source_version( + source_repo, name, version) + current_item.set_version_in_use(checked_out_version) + else: + # All packages are defined in the same repo. + package_definition_dir = definitions_dir + chunk_morph = self.find_or_create_chunk_morph( morph_set, goal_name, kind, name, checked_out_version, - source_repo, url, ref) + package_definition_dir, url, ref) current_item.set_morphology(chunk_morph) diff --git a/import/omnibus.to_chunk b/import/omnibus.to_chunk new file mode 100644 index 00000000..67ff1175 --- /dev/null +++ b/import/omnibus.to_chunk @@ -0,0 +1,106 @@ +#!/usr/bin/env ruby +# +# Create a chunk morphology to integrate Omnibus software in Baserock +# +# Copyright (C) 2014 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +require 'bundler' +require 'omnibus' + +require 'optparse' +require 'rubygems/commands/install_command' +require 'shellwords' +require 'yaml' + +TARGET_PROJECT = 'chefdk' # should come from args + +TARGET_SOFTWARE = 'chefdk' # should come from args + +class Omnibus::Builder + # It's possible to use `gem install` in build commands, which is a great + # way of subverting the dependency tracking Omnibus provides. It's done + # in `omnibus-chef/config/software/chefdk.rb`, for example. + # + # To handle this, here we extend the class that executes the build commands + # to detect when `gem install` is run. It uses the Gem library to turn the + # commandline back into a Bundler::Dependency object that we can use. + + class GemInstallCommandParser < Gem::Commands::InstallCommand + def dependency_list_from_commandline(args) + handle_options args + options[:args].collect do |gem_name| + Bundler::Dependency.new(gem_name, options[:version]) + end + end + end + + def gem(command, options = {}) + # This function implements the 'gem' function in the build-commands DSL. + if command.match /^install/ + parser = GemInstallCommandParser.new + args = Shellwords.split(command).drop(1) + gems = parser.dependency_list_from_commandline(args) + manually_installed_rubygems.concat gems + end + end + + def manually_installed_rubygems + @manually_installed_rubygems ||= [] + end +end + +class OmnibusChunkMorphologyGenerator + def deps_for_software(software) + deps = Hash.new + software.dependencies.each do |dep| + deps[dep] = 0 + end + software.builder.manually_installed_rubygems.each do |dep| + deps[dep.name] = dep.requirement.to_s + end + deps + end + + def generate_chunk_morph_for_software(software) + { + "name" => software.name, + "kind" => "chunk", + # Possibly this tool should look at software.build and + # generate suitable configure, build and install-commands. + # For now: don't bother! + + # FIXME: are these build or runtime dependencies? We'll assume both. + "x-build-dependencies-omnibus" => deps_for_software(software), + "x-runtime-dependencies-omnibus" => deps_for_software(software), + } + end + + def write_morph(file, morph) + file.write(YAML.dump(morph)) + end + + def run + project = Omnibus::Project.load(TARGET_PROJECT) + + software = Omnibus::Software.load(project, TARGET_SOFTWARE) + + morph = generate_chunk_morph_for_software(software) + + write_morph(STDOUT, morph) + end +end + +OmnibusChunkMorphologyGenerator.new.run -- cgit v1.2.1