[ostree/wip/ostbuild-v2] ostbuild: Major rework



commit feb09cdf76fcd4c60d6b7c1d68556f35bf9f701d
Author: Colin Walters <walters verbum org>
Date:   Fri Apr 6 15:12:57 2012 -0400

    ostbuild: Major rework

 Makefile-ostbuild.am                               |    1 +
 gnomeos/README-build.md                            |   42 +++++++++-
 src/ostbuild/pyostbuild/builtin_build.py           |   39 ++++++---
 src/ostbuild/pyostbuild/builtin_checkout.py        |   16 +++-
 .../pyostbuild/builtin_chroot_compile_one.py       |   28 ++++--
 src/ostbuild/pyostbuild/builtin_compile_one.py     |   25 +++---
 src/ostbuild/pyostbuild/builtin_query_content.py   |   31 +++----
 .../pyostbuild/builtin_replace_component.py        |   67 +++++++++++++++
 src/ostbuild/pyostbuild/builtin_resolve.py         |    5 +-
 src/ostbuild/pyostbuild/builtins.py                |   88 ++++++++++++++++++--
 src/ostbuild/pyostbuild/main.py                    |    1 +
 src/ostbuild/pyostbuild/subprocess_helpers.py      |    6 +-
 12 files changed, 277 insertions(+), 72 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index 08eb8c0..effe6cf 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -29,6 +29,7 @@ pyostbuild_PYTHON =					\
 	src/ostbuild/pyostbuild/builtin_compile_one.py	\
 	src/ostbuild/pyostbuild/builtin_query_content.py	\
 	src/ostbuild/pyostbuild/builtin_resolve.py	\
+	src/ostbuild/pyostbuild/builtin_replace_component.py	\
 	src/ostbuild/pyostbuild/builtin_status.py	\
 	src/ostbuild/pyostbuild/builtins.py		\
 	src/ostbuild/pyostbuild/filemonitor.py		\
diff --git a/gnomeos/README-build.md b/gnomeos/README-build.md
index b432276..0414576 100644
--- a/gnomeos/README-build.md
+++ b/gnomeos/README-build.md
@@ -16,6 +16,9 @@ base "runtime", and one "devel" with all of the development tools like
 gcc.  We then import that into an OSTree branch
 e.g. "bases/yocto/gnomeos-3.4-i686-devel".
 
+At present, it's still (mostly) possible to put this data on an ext4
+filesystem and boot into it.
+
 We also have a Yocto recipe "ostree-native" which generates (as you
 might guess) a native binary of ostree.  That binary is used to import
 into an "archive mode" OSTree repository.  You can see it in
@@ -26,26 +29,59 @@ can use "ostbuild" which uses "linux-user-chroot" to chroot inside,
 run a build on a source tree, and outputs binaries, which we then add
 to the build tree for the next module, and so on.
 
+The final result of all of this is that the OSTree repository gains
+new commits (which can be downloaded by clients), while still
+retaining old build history.
+
+Yocto details
+-------------
+
+I have a branch of Yocto here:
+
+https://github.com/cgwalters/poky
+
+It has a collection of patches on top of the "Edison" release of
+Yocto, some of which are hacky, others upstreamable.  The most
+important part though are the modifications to commit the generated
+root filesystem into OSTree.
+
 ostbuild details
 ----------------
 
 The simple goal of ostbuild is that it only takes as input a
-"manifest" which is basically just a list of components to build.  A
-component is a pure metadata file which includes the git repository
+"manifest" which is basically just a list of components to build.  You
+can see this here:
+
+http://git.gnome.org/browse/ostree/tree/gnomeos/3.4/gnomeos-3.4-src.json
+
+A component is a pure metadata file which includes the git repository
 URL and branch name, as well as ./configure flags (--enable-foo).
 
 There is no support for building from "tarballs" - I want the ability
 to review all of the code that goes in, and to efficiently store
-source code updates.
+source code updates.  It's also just significantly easier from an
+implementation perspective, versus having to maintain a version
+control abstraction layer.
 
 The result of a build of a component is an OSTree branch like
 "artifacts/gnomeos-3.4-i686-devel/libxslt/master".  Then, a "compose"
 process merges together the individual filesystem trees into the final
 branches (e.g. gnomeos-3.4-i686-devel).
 
+Doing local builds
+------------------
+
+This is where you want to modify one (or a few) components on top of
+what comes from the ostree.gnome.org server, and test the result
+locally.  I'm working on this.
+
 Doing a full build on your system
 ---------------------------------
 
+Following this process is equivalent to what we have set up on the
+ostree.gnome.org build server.  It will generate a completely new
+repository.
+
 srcdir=/src
 builddir=/src/build
 
diff --git a/src/ostbuild/pyostbuild/builtin_build.py b/src/ostbuild/pyostbuild/builtin_build.py
index 772aaca..2482bc4 100755
--- a/src/ostbuild/pyostbuild/builtin_build.py
+++ b/src/ostbuild/pyostbuild/builtin_build.py
@@ -38,7 +38,7 @@ class BuildOptions(object):
 
 class OstbuildBuild(builtins.Builtin):
     name = "build"
-    short_description = "Rebuild all artifacts from the given manifest"
+    short_description = "Build artifacts and compose trees from snapshot"
 
     def __init__(self):
         builtins.Builtin.__init__(self)
@@ -49,7 +49,8 @@ class OstbuildBuild(builtins.Builtin):
             args = ['setarch', architecture]
         else:
             args = []
-        args.extend(['ostbuild', 'chroot-compile-one'])
+        args.extend(['ostbuild', 'chroot-compile-one',
+                     '--snapshot=' + self.snapshot_path])
         return args
 
     def _launch_debug_shell(self, architecture, buildroot, cwd=None):
@@ -96,15 +97,11 @@ class OstbuildBuild(builtins.Builtin):
 
         checkoutdir = os.path.join(self.workdir, 'src')
         component_src = os.path.join(checkoutdir, name)
-        run_sync(['ostbuild', 'checkout', '--clean', '--overwrite', name], cwd=checkoutdir)
+        run_sync(['ostbuild', 'checkout', '--snapshot=' + self.snapshot_path,
+                  '--clean', '--overwrite', name], cwd=checkoutdir)
 
         artifact_meta = dict(component)
 
-        metadata_path = os.path.join(component_src, '_ostbuild-meta.json')
-        f = open(metadata_path, 'w')
-        json.dump(artifact_meta, f, indent=4, sort_keys=True)
-        f.close()
-
         logdir = os.path.join(self.workdir, 'logs', name)
         fileutil.ensure_dir(logdir)
         log_path = os.path.join(logdir, 'compile.log')
@@ -185,14 +182,27 @@ class OstbuildBuild(builtins.Builtin):
                     'base': resolved_base, 
                     'contents': resolved_contents}
 
+        compose_contents = []
         for branch in contents:
             branch_rev = branch_to_rev[branch]
             subtrees = branch_to_subtrees[branch]
-            for subtree in subtrees:
-                run_sync(['ostree', '--repo=' + self.repo,
-                          'checkout', '--user-mode',
-                          '--union', '--subpath=' + subtree,
-                          branch_rev, compose_rootdir])
+            for subpath in subtrees:
+                compose_contents.append((branch_rev, subpath))
+
+        (fd, tmppath) = tempfile.mkstemp(suffix='.txt', prefix='ostbuild-compose-')
+        f = os.fdopen(fd, 'w')
+        for (branch, subpath) in compose_contents:
+            f.write(branch)
+            f.write('\0')
+            f.write(subpath)
+            f.write('\0')
+        f.close()
+
+        run_sync(['ostree', '--repo=' + self.repo,
+                  'checkout', '--user-mode', '--no-triggers',
+                  '--union', '--from-stdin', compose_rootdir],
+                 stdin=open(tmppath))
+        os.unlink(tmppath)
 
         contents_path = os.path.join(compose_rootdir, 'contents.json')
         f = open(contents_path, 'w')
@@ -207,6 +217,7 @@ class OstbuildBuild(builtins.Builtin):
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
         parser.add_argument('--skip-built', action='store_true')
+        parser.add_argument('--snapshot', required=True)
         parser.add_argument('--recompose', action='store_true')
         parser.add_argument('--skip-compose', action='store_true')
         parser.add_argument('--start-at')
@@ -218,7 +229,7 @@ class OstbuildBuild(builtins.Builtin):
         self.args = args
         
         self.parse_config()
-        self.parse_snapshot()
+        self.parse_snapshot(args.snapshot)
 
         self.buildopts = BuildOptions()
         self.buildopts.shell_on_failure = args.shell_on_failure
diff --git a/src/ostbuild/pyostbuild/builtin_checkout.py b/src/ostbuild/pyostbuild/builtin_checkout.py
index 553e505..ae94163 100755
--- a/src/ostbuild/pyostbuild/builtin_checkout.py
+++ b/src/ostbuild/pyostbuild/builtin_checkout.py
@@ -40,6 +40,7 @@ class OstbuildCheckout(builtins.Builtin):
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
         parser.add_argument('--overwrite', action='store_true')
+        parser.add_argument('--snapshot')
         parser.add_argument('--clean', action='store_true')
         parser.add_argument('components', nargs='*')
 
@@ -47,18 +48,20 @@ class OstbuildCheckout(builtins.Builtin):
         self.args = args
         
         self.parse_config()
-        self.parse_snapshot()
 
         if len(args.components) > 0:
             checkout_components = args.components
         else:
             checkout_components = [os.path.basename(os.getcwd())]
 
+        if args.snapshot:
+            self.parse_snapshot(args.snapshot)
+        else:
+            self.parse_active_branch()
+
         for component_name in checkout_components:
             found = False
-            component = self.snapshot['components'].get(component_name)
-            if component is None:
-                fatal("Unknown component %r" % (component_name, ))
+            component = self.get_component_meta(component_name)
             (keytype, uri) = buildutil.parse_src_key(component['src'])
             checkoutdir = os.path.join(os.getcwd(), component_name)
             fileutil.ensure_parent_dir(checkoutdir)
@@ -86,6 +89,11 @@ class OstbuildCheckout(builtins.Builtin):
                 for patch in patches['files']:
                     patch_path = os.path.join(patchdir, patch)
                     run_sync(['git', 'am', '--ignore-date', '-3', patch_path], cwd=checkoutdir)
+
+            metadata_path = os.path.join(checkoutdir, '_ostbuild-meta.json')
+            f = open(metadata_path, 'w')
+            json.dump(component, f, indent=4, sort_keys=True)
+            f.close()
         
             print "Checked out: %r" % (component_src, )
         
diff --git a/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py b/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py
index fa29be9..6c8432e 100755
--- a/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py
+++ b/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py
@@ -45,22 +45,34 @@ class OstbuildChrootCompileOne(builtins.Builtin):
             checkout_trees.append((buildname, '/runtime'))
             checkout_trees.append((buildname, '/devel'))
 
-        for (branch, rootpath) in checkout_trees:
-            run_sync(['ostree', '--repo=' + self.repo,
-                      'checkout', '--user-mode',
-                      '--union', '--subpath=' + rootpath,
-                      branch, dirpath])
+        (fd, tmppath) = tempfile.mkstemp(suffix='.txt', prefix='ostbuild-buildroot-')
+        f = os.fdopen(fd, 'w')
+        for (branch, subpath) in checkout_trees:
+            f.write(branch)
+            f.write('\0')
+            f.write(subpath)
+            f.write('\0')
+        f.close()
+
+        log("roots: %r" % (checkout_trees, ))
+
+        run_sync(['ostree', '--repo=' + self.repo,
+                  'checkout', '--user-mode', '--union', '--from-stdin', dirpath],
+                 stdin=open(tmppath))
+
+        os.unlink(tmppath);
 
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
         parser.add_argument('--pristine', action='store_true')
+        parser.add_argument('--snapshot', required=True)
         parser.add_argument('--name')
         parser.add_argument('--debug-shell', action='store_true')
         
         args = parser.parse_args(argv)
 
         self.parse_config()
-        self.parse_snapshot()
+        self.parse_snapshot(args.snapshot)
 
         if args.name:
             component_name = args.name
@@ -110,9 +122,6 @@ class OstbuildChrootCompileOne(builtins.Builtin):
             
         self._compose_buildroot(component_name, rootdir_tmp)
 
-        child_args = ['ostbuild', 'chroot-run-triggers', rootdir_tmp]
-        run_sync(child_args)
-
         builddir_tmp = os.path.join(rootdir_tmp, 'ostbuild')
         os.mkdir(builddir_tmp)
         os.mkdir(os.path.join(builddir_tmp, 'source'))
@@ -145,7 +154,6 @@ class OstbuildChrootCompileOne(builtins.Builtin):
                                'compile-one',
                                '--ostbuild-resultdir=/ostbuild/results',
                                '--ostbuild-meta=_ostbuild-meta.json'])
-            child_args.extend(self.metadata.get('config-opts', []))
         env_copy = dict(buildutil.BUILD_ENV)
         env_copy['PWD'] = chroot_sourcedir
         run_sync(child_args, env=env_copy, keep_stdin=args.debug_shell)
diff --git a/src/ostbuild/pyostbuild/builtin_compile_one.py b/src/ostbuild/pyostbuild/builtin_compile_one.py
index f703d0d..eef08df 100755
--- a/src/ostbuild/pyostbuild/builtin_compile_one.py
+++ b/src/ostbuild/pyostbuild/builtin_compile_one.py
@@ -57,6 +57,8 @@ class OstbuildCompileOne(builtins.Builtin):
 
     def execute(self, args):
         self.default_buildapi_jobs = ['-j', '%d' % (cpu_count() * 2, )]
+
+        starttime = time.time()
         
         uname=os.uname()
         kernel=uname[0].lower()
@@ -77,32 +79,28 @@ class OstbuildCompileOne(builtins.Builtin):
                       '--infodir=' + os.path.join(PREFIX, 'share', 'info')]
         self.makeargs = ['make']
 
-        self.ostbuild_resultdir=None
-        self.ostbuild_meta=None
+        self.ostbuild_resultdir='_ostbuild-results'
+        self.ostbuild_meta_path='_ostbuild-meta.json'
 
         chdir = None
+        opt_install = False
 
         for arg in args:
             if arg.startswith('--ostbuild-resultdir='):
                 self.ostbuild_resultdir=arg[len('--ostbuild-resultdir='):]
             elif arg.startswith('--ostbuild-meta='):
-                self.ostbuild_meta=arg[len('--ostbuild-meta='):]
+                self.ostbuild_meta_path=arg[len('--ostbuild-meta='):]
             elif arg.startswith('--chdir='):
                 os.chdir(arg[len('--chdir='):])
-            elif arg.startswith('--'):
-                self.configargs.append(arg)
             else:
                 self.makeargs.append(arg)
         
-        if self.ostbuild_resultdir is None:
-            fatal("Must specify --ostbuild-resultdir=")
-        if self.ostbuild_meta is None:
-            fatal("Must specify --ostbuild-meta=")
-
-        f = open(self.ostbuild_meta)
+        f = open(self.ostbuild_meta_path)
         self.metadata = json.load(f)
         f.close()
 
+        self.configargs.extend(self.metadata.get('config-opts', []))
+
         if self.metadata.get('rm-configure', False):
             configure_path = 'configure'
             if os.path.exists(configure_path):
@@ -235,6 +233,11 @@ class OstbuildCompileOne(builtins.Builtin):
                 except OSError, e:
                     pass
 
+        endtime = time.time()
+
+        log("Compliation succeeded; %d seconds elapsed" % (int(endtime - starttime),))
+        log("Results placed in %s" % (self.ostbuild_resultdir, ))
+
     def _install_and_unlink(self, src, dest):
         statsrc = os.lstat(src)
         dirname = os.path.dirname(dest)
diff --git a/src/ostbuild/pyostbuild/builtin_query_content.py b/src/ostbuild/pyostbuild/builtin_query_content.py
index 89f5839..b769562 100755
--- a/src/ostbuild/pyostbuild/builtin_query_content.py
+++ b/src/ostbuild/pyostbuild/builtin_query_content.py
@@ -37,31 +37,26 @@ class OstbuildQueryContent(builtins.Builtin):
 
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
-        parser.add_argument('--branch', required=True)
+        parser.add_argument('--branch')
         parser.add_argument('--component')
 
         args = parser.parse_args(argv)
         self.args = args
         self.parse_config()
 
-        contents_json_text = run_sync_get_output(['ostree', '--repo=' + self.repo,
-                                                  'cat', args.branch, 'contents.json'])
-        
+        if self.args.branch is None:
+            self.args.branch = self.active_branch
+        if self.args.branch is None:
+            fatal("No active branch, and --branch not specified")
+            
+        self.parse_active_branch()    
+
         if args.component is None:
-            sys.stdout.write(contents_json_text)
+            json.dump(self.active_branch_contents, sys.stdout,
+                      indent=4, sort_keys=True)
         else:
-            contents = json.loads(contents_json_text)
-            contents_list = contents['contents']
-            found = False
-            for content in contents_list:
-                if content['name'] != args.component:
-                    found = True
-                    break
-            if not found:
-                fatal("Unknown component '%s'" % (args.component, ))
-            ostbuildmeta_json = run_sync_get_output(['ostree', '--repo=' + self.repo,
-                                                     'cat', content['ostree-revision'],
-                                                     '/_ostbuild-meta.json'])
-            sys.stdout.write(ostbuildmeta_json)
+            meta = self.get_component_meta(args.component)
+            json.dump(meta, sys.stdout,
+                      indent=4, sort_keys=True)
 
 builtins.register(OstbuildQueryContent)
diff --git a/src/ostbuild/pyostbuild/builtin_replace_component.py b/src/ostbuild/pyostbuild/builtin_replace_component.py
new file mode 100755
index 0000000..d23c9dc
--- /dev/null
+++ b/src/ostbuild/pyostbuild/builtin_replace_component.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2011,2012 Colin Walters <walters verbum org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# ostbuild-compile-one-make wraps systems that implement the GNOME build API:
+# http://people.gnome.org/~walters/docs/build-api.txt
+
+import os,sys,stat,subprocess,tempfile,re,shutil
+from StringIO import StringIO
+import json
+import select,time
+import argparse
+
+from . import builtins
+from .ostbuildlog import log, fatal
+from .subprocess_helpers import run_sync, run_sync_get_output
+
+class OstbuildReplaceComponent(builtins.Builtin):
+    name = "replace-component"
+    short_description = "Replace contents of component in current filesystem"
+
+    def __init__(self):
+        builtins.Builtin.__init__(self)
+
+    def execute(self, argv):
+        parser = argparse.ArgumentParser(description=self.short_description)
+        parser.add_argument('component')
+        parser.add_argument('new-contents')
+
+        args = parser.parse_args(argv)
+        
+        self.parse_config()
+        self.parse_active_branch()
+
+        snapshot = self.get_component_snapshot(args.component)
+
+        replacing_trees = snapshot['trees']
+        previous_contents = set()
+        new_contents = set()
+        for tree in replacing_trees:
+            previous_contents_string = run_sync_get_output(['ostree', '--repo=' + self.repo,
+                                                            'ls', '--nul-filenames-only',
+                                                            '-R', snapshot['ostree-revision'],
+                                                            '/'])
+            for filename in previous_contents.split('\0'):
+                previous_contents.add(filename)
+        new_contents = run_sync_get_output(['find', '-print0'],
+                                           cwd=parser.new_contents)
+        for filename in new_contents.split('\0'):
+            new_contents.add(filename)
+
+        print "%r %r" % (previous_contents, new_contents)
+    
+builtins.register(OstbuildReplaceComponent)
diff --git a/src/ostbuild/pyostbuild/builtin_resolve.py b/src/ostbuild/pyostbuild/builtin_resolve.py
index 3486396..88e6af8 100755
--- a/src/ostbuild/pyostbuild/builtin_resolve.py
+++ b/src/ostbuild/pyostbuild/builtin_resolve.py
@@ -19,6 +19,7 @@ import os,sys,subprocess,tempfile,re,shutil
 import copy
 import argparse
 import json
+import time
 import urlparse
 from StringIO import StringIO
 
@@ -278,7 +279,9 @@ class OstbuildResolve(builtins.Builtin):
             del component['name']
         snapshot['components'] = components_by_name
 
-        out_snapshot = os.path.join(self.workdir, '%s-snapshot.json' % (name_prefix, ))
+        current_time = time.time()
+
+        out_snapshot = os.path.join(self.workdir, '%s-snapshot-%d.json' % (name_prefix, int(current_time)))
         f = open(out_snapshot, 'w')
         json.dump(snapshot, f, indent=4, sort_keys=True)
         f.close()
diff --git a/src/ostbuild/pyostbuild/builtins.py b/src/ostbuild/pyostbuild/builtins.py
index 64f7be4..41f2a4d 100755
--- a/src/ostbuild/pyostbuild/builtins.py
+++ b/src/ostbuild/pyostbuild/builtins.py
@@ -19,11 +19,13 @@
 
 import os
 import sys
+import stat
 import argparse
 import json
 
 from . import ostbuildrc
 from .ostbuildlog import log, fatal
+from .subprocess_helpers import run_sync, run_sync_get_output
 
 _all_builtins = {}
 
@@ -31,9 +33,43 @@ class Builtin(object):
     name = None
     short_description = None
 
+    def __init__(self):
+        self._meta_cache = {}
+        self.manifest = None
+        self.snapshot = None
+        self.repo = None
+        self.ostree_dir = self._find_ostree_dir()
+        (self.active_branch, self.active_branch_checksum) = self._find_active_branch()
+
+    def _find_ostree_dir(self):
+        for path in ['/ostree', '/sysroot/ostree']:
+            if os.path.isdir(path):
+                return path
+        return None
+        
+    def _find_active_branch(self):
+        if self.ostree_dir is None:
+            return (None, None)
+        current_path = os.path.join(self.ostree_dir, 'current')
+        while True:
+            try:
+                target = os.path.join(self.ostree_dir, current_path)
+                stbuf = os.lstat(target)
+            except OSError, e:
+                current_path = None
+                break
+            if not stat.S_ISLNK(stbuf.st_mode):
+                break
+            current_path = os.readlink(target)
+        if current_path is not None:
+            basename = os.path.basename(current_path)
+            return basename.rsplit('-', 1)
+        else:
+            return (None, None)
+
     def parse_config(self):
         self.ostbuildrc = ostbuildrc
-        self.repo = ostbuildrc.get_key('repo')
+
         self.mirrordir = ostbuildrc.get_key('mirrordir')
         if not os.path.isdir(self.mirrordir):
             fatal("Specified mirrordir '%s' is not a directory" % (self.mirrordir, ))
@@ -42,15 +78,49 @@ class Builtin(object):
             fatal("Specified workdir '%s' is not a directory" % (self.workdir, ))
         self.patchdir = os.path.join(self.workdir, 'patches')
 
-    def parse_manifest(self):
-        self.manifest_path = ostbuildrc.get_key('manifest')
-        self.manifest = json.load(open(self.manifest_path))
-        self.name_prefix = self.manifest['name-prefix']
+    def parse_active_branch(self):
+        if self.ostree_dir is None:
+            fatal("/ostree directory not found")
+        repo_path = os.path.join(self.ostree_dir, 'repo')
+        if not os.path.isdir(repo_path):
+            fatal("Repository '%s' doesn't exist" % (repo_path, ))
+        self.repo = repo_path
+        branch_path = os.path.join(self.ostree_dir, self.active_branch)
+        contents_path = os.path.join(branch_path, 'contents.json')
+        f = open(contents_path)
+        self.active_branch_contents = json.load(f)
+        f.close()
+
+    def get_component_snapshot(self, name):
+        found = False
+        for content in self.active_branch_contents['contents']:
+            if content['name'] == name:
+                found = True
+                break
+        if not found:
+            fatal("Unknown component '%s'" % (name, ))
+        return content
 
-    def parse_snapshot(self):
-        self.parse_manifest()
-        snapshot_path = os.path.join(self.workdir, '%s-snapshot.json' % (self.name_prefix, ))
-        self.snapshot = json.load(open(snapshot_path))
+    def get_component_meta(self, name):
+        assert self.repo is not None
+
+        if self.snapshot is not None:
+            return self.snapshot['components'][name]
+
+        meta = self._meta_cache.get(name)
+        if meta is None:
+            content = self.get_component_snapshot(name)
+            meta_text = run_sync_get_output(['ostree', '--repo=' + self.repo,
+                                             'cat', content['ostree-revision'],
+                                             '/_ostbuild-meta.json'])
+            meta = json.loads(meta_text)
+            self._meta_cache[name] = meta
+        return meta
+
+    def parse_snapshot(self, path):
+        self.repo = ostbuildrc.get_key('repo')
+        self.snapshot_path = path
+        self.snapshot = json.load(open(self.snapshot_path))
 
     def execute(self, args):
         raise NotImplementedError()
diff --git a/src/ostbuild/pyostbuild/main.py b/src/ostbuild/pyostbuild/main.py
index 842cb5b..ccbc644 100755
--- a/src/ostbuild/pyostbuild/main.py
+++ b/src/ostbuild/pyostbuild/main.py
@@ -29,6 +29,7 @@ from . import builtin_chroot_run_triggers
 from . import builtin_compile_one
 from . import builtin_query_content
 from . import builtin_resolve
+from . import builtin_replace_component
 from . import builtin_status
 
 def usage(ecode):
diff --git a/src/ostbuild/pyostbuild/subprocess_helpers.py b/src/ostbuild/pyostbuild/subprocess_helpers.py
index 116d0fb..3754900 100755
--- a/src/ostbuild/pyostbuild/subprocess_helpers.py
+++ b/src/ostbuild/pyostbuild/subprocess_helpers.py
@@ -70,14 +70,16 @@ def run_sync_get_output(args, cwd=None, env=None, stdout=None, stderr=None, none
     return None
 
 def run_sync(args, cwd=None, env=None, fatal_on_error=True, keep_stdin=False,
-             log_success=True, log_initiation=True, stdout=None,
+             log_success=True, log_initiation=True, stdin=None, stdout=None,
              stderr=None):
     if log_initiation:
         log("running: %s" % (subprocess.list2cmdline(args),))
 
     env_copy = _get_env_for_cwd(cwd, env)
 
-    if keep_stdin:
+    if stdin is not None:
+        stdin_target = stdin
+    elif keep_stdin:
         stdin_target = sys.stdin
     else:
         stdin_target = open('/dev/null', 'r')



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]