summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Thursfield <sam.thursfield@codethink.co.uk>2014-09-30 18:22:08 +0100
committerSam Thursfield <sam.thursfield@codethink.co.uk>2014-09-30 18:22:08 +0100
commit9c4095c2c668a71c6246e2e703ad759ec5b1a9ec (patch)
treeaa393cb54943201656912da659a2dcdff9daf095
parent19368162c05a2fb327814c2c7a730ccfc09bfb5e (diff)
downloadmorph-9c4095c2c668a71c6246e2e703ad759ec5b1a9ec.tar.gz
import: Add initial Omnibus importer
-rw-r--r--import/main.py55
-rw-r--r--import/omnibus.to_chunk106
2 files changed, 156 insertions, 5 deletions
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 <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 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