[gnome-ostree/wip/gjs-round2: 1/10] Rewrite checkout in gjs



commit 66f405a39f9d91d1146c6c932e011061e8402545
Author: Colin Walters <walters verbum org>
Date:   Thu Dec 6 22:24:46 2012 -0500

    Rewrite checkout in gjs
    
    And add lots more infrastructure for that.

 Makefile-ostbuild.am                        |   10 ++-
 Makefile.am                                 |    1 +
 src/libgsystem                              |    2 +-
 src/ostbuild/js/argparse.js                 |  142 +++++++++++++++++++++++++++
 src/ostbuild/js/autobuilder.js              |    6 +-
 src/ostbuild/js/buildutil.js                |   39 ++++++++
 src/ostbuild/js/checkout.js                 |  124 +++++++++++++++++++++++
 src/ostbuild/js/params.js                   |   35 +++++++
 src/ostbuild/js/procutil.js                 |   63 +++++++++++--
 src/ostbuild/js/snapshot.js                 |   45 +++++++++
 src/ostbuild/js/vcs.js                      |  135 +++++++++++++++++++++++++
 src/ostbuild/pyostbuild/builtin_checkout.py |  120 ----------------------
 src/ostbuild/pyostbuild/main.py             |    6 +-
 13 files changed, 592 insertions(+), 136 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index eff0a1a..3ee047c 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -45,7 +45,6 @@ pyostbuilddir=$(libdir)/ostbuild/pyostbuild
 pyostbuild_PYTHON =					\
 	src/ostbuild/pyostbuild/buildutil.py		\
 	src/ostbuild/pyostbuild/builtin_build.py	\
-	src/ostbuild/pyostbuild/builtin_checkout.py	\
 	src/ostbuild/pyostbuild/builtin_deploy_qemu.py	\
 	src/ostbuild/pyostbuild/builtin_deploy_root.py	\
 	src/ostbuild/pyostbuild/builtin_import_tree.py	\
@@ -75,13 +74,18 @@ pyostbuild_PYTHON =					\
 
 jsostbuilddir=$(pkgdatadir)/js
 jsostbuild_DATA= \
+	src/ostbuild/js/argparse.js \
 	src/ostbuild/js/autobuilder.js \
+	src/ostbuild/js/buildutil.js \
+	src/ostbuild/js/checkout.js \
 	src/ostbuild/js/config.js \
 	src/ostbuild/js/jsondb.js \
-	src/ostbuild/js/task.js \
+	src/ostbuild/js/jsonutil.js \
+	src/ostbuild/js/params.js \
 	src/ostbuild/js/procutil.js \
 	src/ostbuild/js/snapshot.js \
-	src/ostbuild/js/jsonutil.js \
+	src/ostbuild/js/task.js \
+	src/ostbuild/js/vcs.js \
 	$(NULL)
 
 endif
diff --git a/Makefile.am b/Makefile.am
index f9b7fb7..4386e8b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,6 +51,7 @@ GSystem_1_0_gir_LIBS = libgsystem.la
 GSystem_1_0_gir_CFLAGS = $(libgsystem_cflags)
 GSystem_1_0_gir_SCANNERFLAGS =			\
        --warn-all				\
+       --warn-error				\
        --symbol-prefix=gs_			\
        --identifier-prefix=GS			\
         --c-include="libgsystem.h"	        \
diff --git a/src/libgsystem b/src/libgsystem
index dd1b303..aac3668 160000
--- a/src/libgsystem
+++ b/src/libgsystem
@@ -1 +1 @@
-Subproject commit dd1b3032b4cd175c2a9b2c611a62525454bad772
+Subproject commit aac3668399f8058877cf21ba35617ec4cfecd500
diff --git a/src/ostbuild/js/argparse.js b/src/ostbuild/js/argparse.js
new file mode 100644
index 0000000..92ef802
--- /dev/null
+++ b/src/ostbuild/js/argparse.js
@@ -0,0 +1,142 @@
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+
+const ArgumentParser = new Lang.Class({
+    Name: 'ArgumentParser',
+
+    _init: function(description) {
+	this.description = description;
+	this._opts = [];
+	this._namedArgs = [];
+	this._optNames = {};
+	this._argNames = {};
+    },
+
+    usage: function() {
+	let buf = 'Usage: ' + this.description + '\n';
+	for (let i = 0; i < this._opts.length; i++) {
+	    let opt = this._opts[i];
+	    let names = this._opts._names;
+	    for (let j = 0; j < names.length; j++) {
+		let name = names[j];
+		buf += name;
+		if (j < names.length - 1)
+		    buf += ", ";
+	    }
+	    if (opt.description)
+		buf += '        ' + opt.description;
+	    buf += '\n';
+	}
+	for (let i = 0; i < this._namedArgs.length; i++) {
+	    let arg = this._namedArgs[i];
+	    buf += arg + "\n";
+	}
+	return buf;
+    },
+
+    addArgument: function(nameOrNames, opts) {
+	if (!opts)
+	    opts = {};
+	let names;
+	if (nameOrNames instanceof Array)
+	    names = nameOrNames;
+	else
+	    names = [nameOrNames];
+
+	if (opts.action == undefined)
+	    opts.action = 'store';
+
+	if (names.length == 0) {
+	    throw new Error("Must specify at least one argument");
+	} else if (names.length == 1 && names[0][0] != '-') {
+	    let name = names[0];
+	    this._namedArgs.push(opts);
+	    this._argNames[name] = opts;
+	    opts._varName = name;
+	} else {
+	    opts._names = names;
+	    this._opts.push(opts);
+
+	    opts._varName = null;
+
+	    let shortOpt = null;
+	    
+	    for (let i = 0; i < names.length; i++) {
+		let name = names[i];
+		if (this._optNames[name]) {
+		    throw new Error("Argument " + name + " already added");
+		} else if (names.length != 1 && name[0] != '-') {
+		    throw new Error("Argument " + name + " does not start with -");
+		}
+		
+		this._optNames[name] = opts;
+		if (opts._varName == null) {
+		    if (name.indexOf('--') == 0)
+			opts._varName = name.substr(2);
+		    else if (shortOpt == null && name[0] == '-' && name[1] != '-')
+			shortOpt = name.substr(1);
+		}
+	    }
+	    if (opts._varName == null)
+		opts._varName = shortOpt;
+	}
+    },
+
+    _failed: function() {
+	print(this.usage());
+	throw new Error("Argument parsing failed");
+    },
+
+    parse: function(argv) {
+	let result = {};
+	let rest = [];
+
+	for (let name in this._optNames) {
+	    let opts = this._optNames[name];
+	    if (opts.action == 'store') {
+		result[opts._varName] = null;
+	    } else if (opts.action == 'storeTrue') {
+		result[opts._varName] = false;
+	    }
+	}
+	for (let name in this._argNames) {
+	    result[name] = null;
+	}
+
+	let rest = [];
+	
+	for (let i = 0; i < argv.length; i++) {
+	    let arg = argv[i];
+	    if (arg[0] == '-') {
+		let equalsIdx = arg.indexOf('=');
+		let opts;
+		if (equalsIdx != -1)
+		    opts = this._optNames[arg.substr(0, equalsIdx)];
+		else
+		    opts = this._optNames[arg];
+		
+		if (!opts) this._failed();
+
+		if (opts.action == 'store') {
+		    if (i == argv.length - 1) this._failed();
+		    result[opts._varName] = argv[i+1];
+		    i++;
+		} else if (opts.action == 'storeTrue') {
+		    result[opts._varName] = true;
+		    i++;
+		}
+	    } else {
+		rest.push(arg);
+	    }
+	}
+	
+	for (let i = 0; i < this._namedArgs.length; i++) {
+	    let a = this._namedArgs[i];
+	    if (rest.length == 0) this._failed();
+	    let value = rest.shift();
+	    result[a._varName] = value;
+	}
+
+	return result;
+    }
+});
diff --git a/src/ostbuild/js/autobuilder.js b/src/ostbuild/js/autobuilder.js
index c6e9d63..f261856 100644
--- a/src/ostbuild/js/autobuilder.js
+++ b/src/ostbuild/js/autobuilder.js
@@ -12,6 +12,7 @@ const JsonDB = imports.jsondb;
 const ProcUtil = imports.procutil;
 const JsonUtil = imports.jsonutil;
 const Snapshot = imports.snapshot;
+const Config = imports.config;
 
 const loop = GLib.MainLoop.new(null, true);
 
@@ -32,7 +33,7 @@ const AutoBuilder = new Lang.Class({
 	this._resolve_proc = null;
 	this._build_proc = null;
 
-	this.config = imports.config.get();
+	this.config = Config.get();
 	this.workdir = Gio.File.new_for_path(this.config.getGlobal('workdir'));
 	this.prefix = this.config.getPrefix();
 	this._snapshot_dir = this.workdir.get_child('snapshots');
@@ -109,6 +110,7 @@ const AutoBuilder = new Lang.Class({
     },
 
     _fetch: function() {
+	let cancellable = null;
 	if (this._resolve_proc != null) {
 	    this._full_resolve_needed = true;
 	    return false;
@@ -117,7 +119,7 @@ const AutoBuilder = new Lang.Class({
 	let taskWorkdir = t.path;
 
 	if (this._autoupdate_self)
-	    ProcUtil.runSync(['git', 'pull', '-r'])
+	    ProcUtil.runSync(['git', 'pull', '-r'], cancellable)
 
 	let args = ['ostbuild', 'resolve', '--manifest=manifest.json',
 		    '--fetch', '--fetch-keep-going'];
diff --git a/src/ostbuild/js/buildutil.js b/src/ostbuild/js/buildutil.js
new file mode 100644
index 0000000..4a26373
--- /dev/null
+++ b/src/ostbuild/js/buildutil.js
@@ -0,0 +1,39 @@
+// Copyright (C) 2011 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.
+
+const BUILD_ENV = {
+    'HOME' : '/', 
+    'HOSTNAME' : 'ostbuild',
+    'LANG': 'C',
+    'PATH' : '/usr/bin:/bin:/usr/sbin:/sbin',
+    'SHELL' : '/bin/bash',
+    'TERM' : 'vt100',
+    'TMPDIR' : '/tmp',
+    'TZ': 'EST5EDT'
+    };
+
+function parseSrcKey(srckey) {
+    let idx = srckey.indexOf(':');
+    if (idx < 0) {
+        throw new Error("Invalid SRC uri=" + srckey);
+    }
+    let keytype = srckey.substr(0, idx);
+    if (!(keytype == 'git' || keytype == 'local')) 
+        throw new Error("Unsupported SRC uri=" + srckey);
+    let uri = srckey.substr(idx+1);
+    return [keytype, uri];
+}
diff --git a/src/ostbuild/js/checkout.js b/src/ostbuild/js/checkout.js
new file mode 100644
index 0000000..e24072b
--- /dev/null
+++ b/src/ostbuild/js/checkout.js
@@ -0,0 +1,124 @@
+#!/usr/bin/env gjs
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const Task = imports.task;
+const JsonDB = imports.jsondb;
+const ProcUtil = imports.procutil;
+const JsonUtil = imports.jsonutil;
+const Snapshot = imports.snapshot;
+const Config = imports.config;
+const BuildUtil = imports.buildutil;
+const Vcs = imports.vcs;
+const ArgParse = imports.argparse;
+
+const loop = GLib.MainLoop.new(null, true);
+
+const Checkout = new Lang.Class({
+    Name: 'Checkout',
+
+    _init: function() {
+    },
+
+    execute: function(argv) {
+	let cancellable = null;
+	let parser = new ArgParse.ArgumentParser('Check out specified modules');
+	parser.addArgument('--overwrite', {action:'storeTrue'});
+	parser.addArgument('--prefix');
+	parser.addArgument('--patches-path');
+	parser.addArgument('--metadata-path');
+	parser.addArgument('--snapshot');
+	parser.addArgument('--checkoutdir');
+	parser.addArgument('--clean', {action: 'storeTrue'});
+	parser.addArgument('component');
+
+	let args = parser.parse(argv);
+        
+	this.config = Config.get();
+	this.workdir = Gio.File.new_for_path(this.config.getGlobal('workdir'));
+	this.mirrordir = Gio.File.new_for_path(this.config.getGlobal('mirrordir'));
+	if (!this.mirrordir.query_exists(cancellable))
+	    throw new Error("Need mirrordir: "+ this.mirrordir.get_path());
+	this.prefix = args.prefix || this.config.getPrefix();
+	this._snapshotDir = this.workdir.get_child('snapshots');
+
+	this._srcDb = new JsonDB.JsonDB(this._snapshotDir, this.prefix + '-src-snapshot');
+	this._snapshot = Snapshot.load(this._srcDb, this.prefix, args.snapshot, cancellable);
+
+        let componentName = args.component;
+
+	let component;
+        if (args.metadata_path != null) {
+	    component = JsonUtil.load(Gio.File.new_for_path(args.metadata_path));
+        } else {
+            component = Snapshot.getExpanded(this._snapshot, componentName);
+	}
+        let [keytype, uri] = BuildUtil.parseSrcKey(component['src']);
+
+        let isLocal = (keytype == 'local');
+
+	let checkoutdir;
+        if (isLocal) {
+            if (args.checkoutdir != null) {
+                checkoutdir = Gio.File.new_for_path(args.checkoutdir);
+		let ftype = checkoutdir.query_file_type(Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
+                // Kind of a hack, but...
+                if (ftype == Gio.FileType.SYMBOLIC_LINK)
+                    GSystem.file_unlink(checkoutdir, cancellable);
+                if (args.overwrite && ftype == Gio.FileType.DIRECTORY)
+                    GSystem.shutil_rm_rf(checkoutdir, cancellable);
+		
+		checkoutdir.make_symbolic_link(uri, cancellable);
+            } else {
+                checkoutdir = Gio.File.new_for_path(uri);
+	    }
+        } else {
+            if (args.checkoutdir) {
+                checkoutdir = Gio.File.new_for_path(args.checkoutdir);
+            } else {
+                checkoutdir = Gio.File.new_for_path(componentName);
+		GSystem.file_ensure_directory(checkoutdir.get_parent(), true, cancellable);
+	    }
+            Vcs.getVcsCheckout(this.mirrordir, keytype, uri, checkoutdir,
+                               component['revision'], cancellable,
+                               {overwrite: args.overwrite});
+	}
+
+        if (args.clean) {
+            if (isLocal) {
+                log("note: ignoring --clean argument due to \"local:\" specification");
+            } else {
+                Vcs.clean(keytype, checkoutdir, cancellable);
+	    }
+	}
+
+        if (component['patches']) {
+            if (args.patches_path == null) {
+                patchdir = Vcs.checkoutPatches(this.mirrordir, this.patchdir, component);
+            } else {
+                patchdir = args.patches_path
+	    }
+            patches = BuildUtil.getPatchPathsForComponent(patchdir, component)
+            for (let i = 0; i < patches.length; i++) {
+		let patch = patches[i];
+                ProcUtil.runSync(['git', 'am', '--ignore-date', '-3', patch], cancellable,
+				 {cwd:checkoutdir.get_path()});
+	    }
+	}
+
+        let metadataPath = checkoutdir.get_child('_ostbuild-meta.json');
+	JsonUtil.writeJsonFileAtomic(metadataPath, component, cancellable);
+        
+        log("Checked out: " + checkoutdir.get_path());
+    }
+});
+
+var checkout = new Checkout();
+GLib.idle_add(GLib.PRIORITY_DEFAULT,
+	      function() { try { checkout.execute(ARGV); } finally { loop.quit(); }; return false; });
+loop.run();
diff --git a/src/ostbuild/js/params.js b/src/ostbuild/js/params.js
new file mode 100644
index 0000000..3b3798b
--- /dev/null
+++ b/src/ostbuild/js/params.js
@@ -0,0 +1,35 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+// Taken from gnome-shell/js/misc/params.js under the GNU General Public License
+// parse:
+// @params: caller-provided parameter object, or %null
+// @defaults: function-provided defaults object
+// @allowExtras: whether or not to allow properties not in @default
+//
+// Examines @params and fills in default values from @defaults for
+// any properties in @defaults that don't appear in @params. If
+// @allowExtras is not %true, it will throw an error if @params
+// contains any properties that aren't in @defaults.
+//
+// If @params is %null, this returns the values from @defaults.
+//
+// Return value: a new object, containing the merged parameters from
+// @params and @defaults
+function parse(params, defaults, allowExtras) {
+    let ret = {}, prop;
+
+    if (!params)
+        params = {};
+
+    for (prop in params) {
+        if (!(prop in defaults) && !allowExtras)
+            throw new Error('Unrecognized parameter "' + prop + '"');
+        ret[prop] = params[prop];
+    }
+
+    for (prop in defaults) {
+        if (!(prop in params))
+            ret[prop] = defaults[prop];
+    }
+
+    return ret;
+}
diff --git a/src/ostbuild/js/procutil.js b/src/ostbuild/js/procutil.js
index 69c65b6..fe387a8 100644
--- a/src/ostbuild/js/procutil.js
+++ b/src/ostbuild/js/procutil.js
@@ -2,16 +2,65 @@ const GLib = imports.gi.GLib;
 const Gio = imports.gi.Gio;
 
 const GSystem = imports.gi.GSystem;
+const Params = imports.params;
 
-function runSync(args, stdoutDisposition, stderrDisposition) {
-    if (stdoutDisposition == undefined)
-	stdoutDisposition = GSystem.SubprocessStreamDisposition.INHERIT;
-    if (stderrDisposition == undefined)
-	stderrDisposition = GSystem.SubprocessStreamDisposition.INHERIT;
-    var proc = GSystem.Subprocess.new_simple_argv(args, stdoutDisposition, stderrDisposition);
-    proc.wait_sync_check(null);
+function _setContextFromParams(context, params) {
+    params = Params.parse(params, {cwd: null});
+    if (params.cwd)
+	context.set_cwd(params.cwd);
 }
 
+function _wait_sync_check_internal(proc, cancellable) {
+    try {
+	proc.wait_sync_check(cancellable);
+    } catch (e) {
+	if (e.domain == GLib.spawn_exit_error_quark() ||
+	    e.matches(GLib.SpawnError, GLib.SpawnError.FAILED))
+	    throw new Error(Format.vprintf("Child process %s: %s", [JSON.stringify(proc.context.argv), e.message]));
+	else
+	    throw e;
+    }
+}
+
+function runSync(args, cancellable, params) {
+    let context = new GSystem.SubprocessContext({argv: args});
+    _setContextFromParams(context, params);
+    let proc = new GSystem.Subprocess({context: context});
+    proc.init(cancellable);
+    _wait_sync_check_internal(proc, cancellable);
+}
+
+function _runSyncGetOutputInternal(args, cancellable, params, splitLines) {
+    params = Params.parse(params, {cwd: null});
+    let context = new GSystem.SubprocessContext({argv: args});
+    context.set_stdout_disposition(GSystem.SubprocessStreamDisposition.PIPE);
+    context.set_stderr_disposition(GSystem.SubprocessStreamDisposition.INHERIT);
+    let proc = new GSystem.Subprocess({context: context});
+    proc.init(cancellable);
+    let input = proc.get_stdout_pipe();
+    let dataIn = Gio.DataInputStream.new(input);
+    let resultLines = [];
+    let resultBuf = '';
+    while (true) {
+	let [line, len] = dataIn.read_line_utf8(cancellable);
+	if (line == null)
+	    break;
+	if (splitLines)
+	    resultLines.push(line);
+	else
+	    resultBuf += line;
+    }
+    _wait_sync_check_internal(proc, cancellable);
+    return splitLines ? resultLines : resultBuf;
+}
+
+function runSyncGetOutputLines(args, cancellable, params) {
+    return _runSyncGetOutputInternal(args, cancellable, params, true);
+}
+
+function runSyncGetOutputUTF8(args, cancellable, params) {
+    return _runSyncGetOutputInternal(args, cancellable, params, false);
+}
 
 function asyncWaitCheckFinish(process, result) {
     let [waitSuccess, estatus] = process.wait_finish(result);
diff --git a/src/ostbuild/js/snapshot.js b/src/ostbuild/js/snapshot.js
index f24369c..0038c8a 100644
--- a/src/ostbuild/js/snapshot.js
+++ b/src/ostbuild/js/snapshot.js
@@ -16,6 +16,8 @@
 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 // Boston, MA 02111-1307, USA.
 
+const JsonDB = imports.jsondb;
+
 function _componentDict(snapshot) {
     let r = {};
     let components = snapshot['components'];
@@ -55,3 +57,46 @@ function snapshotDiff(a, b) {
     }
     return [added, modified, removed];
 }
+
+function load(db, prefix, pathName, cancellable) {
+    if (pathName) {
+	return db.loadFromPath(Gio.File.new_for_path(pathName), cancellable);
+    } else if (prefix) {
+	return db.loadFromPath(db.getLatestPath(), cancellable);
+    } else {
+	throw new Error("No prefix or snapshot specified");
+    }
+}
+
+function getComponent(snapshot, name, allowNone) {
+    let d = _componentDict(snapshot);
+    let r = d[name] || null;
+    if (!r && !allowNone)
+	throw new Error("No component " + name + " in snapshot");
+    return r;
+}
+
+function expandComponent(snapshot, component) {
+    let r = {};
+    Lang.copyProperties(component, r);
+    let patchMeta = getComponent(snapshot, 'patches', true);
+    if (patchMeta != null) {
+	let componentPatchFiles = component['patches'] || [];
+	if (componentPatchFiles.length > 0) {
+	    let patches = {};
+	    Lang.copyProperties(patchMeta, patches);
+	    patches['files'] = componentPatchFiles;
+	    r['patches'] = patches;
+	}
+    }
+    let configOpts = new Array(snapshot['config-opts'] || []);
+    configOpts.push.apply(configOpts, component['config-opts'] || []);
+    r['config-opts'] = configOpts;
+    return r;
+}
+
+function getExpanded(snapshot, name) {
+    return expandComponent(snapshot, getComponent(snapshot, name));
+}
+
+
diff --git a/src/ostbuild/js/vcs.js b/src/ostbuild/js/vcs.js
new file mode 100644
index 0000000..168b474
--- /dev/null
+++ b/src/ostbuild/js/vcs.js
@@ -0,0 +1,135 @@
+// 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.
+
+const Params = imports.params;
+const Gio = imports.gi.Gio;
+const GSystem = imports.gi.GSystem;
+
+const ProcUtil = imports.procutil;
+
+function getMirrordir(mirrordir, keytype, uri, params) {
+    params = Params.parse(params, {prefix: ''});
+    let colon = uri.indexOf('://');
+    let scheme = uri.substr(0, colon);
+    let rest = uri.substr(colon+3);
+    let slash = rest.indexOf('/');
+    let netloc = rest.substr(0, slash);
+    let path = rest.substr(slash+1);
+    let prefix = params.prefix ? params.prefix + '/' : '';
+    return mirrordir.resolve_relative_path(prefix + keytype + '/' + 
+					   scheme + '/' + netloc + '/' +
+					   path);
+}
+
+function _fixupSubmoduleReferences(mirrordir, cwd, cancellable) {
+    let lines = ProcUtil.runSyncGetOutputLines(['git', 'submodule', 'status'],
+					       cancellable, {cwd: cwd.get_path()}); 
+    let haveSubmodules = false;
+    for (let i = 0; i < lines.length; i++) {
+	let line = lines[i];
+        if (line == '') continue;
+        haveSubmodules = true;
+        line = line.substr(1);
+        let [subChecksum, subName, rest] = line.split(' ');
+	let configKey = Format.vprintf('submodule.%s.url', [subName]);
+        let subUrl = ProcUtil.runSyncGetOutputUTF8(['git', 'config', '-f', '.gitmodules', configKey],
+						   cancellable, {cwd: cwd.get_path()});
+        let localMirror = getMirrordir(mirrordir, 'git', subUrl);
+	ProcUtil.runSync(['git', 'config', configKey, 'file://' + localMirror.get_path()],
+			 cancellable, {cwd:cwd.get_path()});
+    }
+    return haveSubmodules;
+}
+
+function getVcsCheckout(mirrordir, keytype, uri, dest, branch, cancellable, params) {
+    params = Params.parse(params, {overwrite: true,
+				   quiet: false});
+    let moduleMirror = getMirrordir(mirrordir, keytype, uri);
+    let checkoutdirParent = dest.get_parent();
+    GSystem.file_ensure_directory(checkoutdirParent, true, cancellable);
+    let tmpDest = checkoutdirParent.get_child(dest.get_basename() + '.tmp');
+    GSystem.shutil_rm_rf(tmpDest, cancellable);
+    let ftype = dest.query_file_type(Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable);
+    if (ftype == Gio.FileType.SYMBOLIC_LINK) {
+        GSystem.file_unlink(dest, cancellable);
+    } else if (ftype == Gio.FileType.DIRECTORY) {
+        if (params.overwrite) {
+	    GSystem.shutil_rm_rf(dest);
+        } else {
+            tmpDest = dest;
+	}
+    }
+    ftype = tmpDest.query_file_type(Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable);
+    if (ftype != Gio.FileType.DIRECTORY) {
+        ProcUtil.runSync(['git', 'clone', '-q', '--origin', 'localmirror',
+			  '--no-checkout', moduleMirror.get_path(), tmpDest.get_path()], cancellable);
+        ProcUtil.runSync(['git', 'remote', 'add', 'upstream', uri], cancellable, {cwd: tmpDest.get_path()});
+    } else {
+        ProcUtil.runSync(['git', 'fetch', 'localmirror'], cancellable, {cwd: tmpDest.get_path()});
+    }
+    ProcUtil.runSync(['git', 'checkout', '-q', branch], cancellable, {cwd: tmpDest.get_path()});
+    ProcUtil.runSync(['git', 'submodule', 'init'], cancellable, {cwd: tmpDest.get_path()});
+    let haveSubmodules = _fixupSubmoduleReferences(mirrordir, tmpDest, cancellable);
+    if (haveSubmodules) {
+        ProcUtil.runSync(['git', 'submodule', 'update'], cancellable, {cwd: tmpDest.get_path()});
+    }
+    if (!tmpDest.equal(dest)) {
+        GSystem.file_rename(tmpDest, dest, cancellable);
+    }
+    return dest;
+}
+
+function clean(keytype, checkoutdir, cancellable) {
+    ProcUtil.runSync(['git', 'clean', '-d', '-f', '-x'], cancellable,
+		     {cwd: checkoutdir.get_path()});
+}
+
+function parseSrcKey(srckey) {
+    let idx = srckey.indexOf(':');
+    if (idx < 0) {
+        throw new Error("Invalid SRC uri=" + srckey);
+    }
+    let keytype = srckey.substr(0, idx);
+    if (!(keytype == 'git' || keytype == 'local')) {
+        throw new Error("Unsupported SRC uri=" + srckey);
+    }
+    let uri = srckey.substr(idx+1);
+    return [keytype, uri];
+}
+    
+function checkoutPatches(mirrordir, patchdir, component, cancellable, params) {
+    params = Params.parse(params, { patches_path: null });
+    let patches = component['patches'];
+    let patches_keytype = null;
+    let patches_uri = null;
+    if (params.patches_path != null) {
+        patches_keytype = local;
+	patches_uri = patches_path;
+        patchdir = patches_uri;
+    } else {
+        [patches_keytype, patches_uri] = parseSrcKey(patches['src']);
+        let patchesMirror = getMirrordir(mirrordir, patches_keytype, patches_uri);
+        getVcsCheckout(mirrordir, patches_keytype, patches_uri,
+                       patchdir, patches['revision'], cancellable,
+                       {overwrite: true,
+                        quiet: true});
+    }
+
+    return patchdir;
+}
+
+    
diff --git a/src/ostbuild/pyostbuild/main.py b/src/ostbuild/pyostbuild/main.py
index 2c4d4a3..0c7939c 100755
--- a/src/ostbuild/pyostbuild/main.py
+++ b/src/ostbuild/pyostbuild/main.py
@@ -23,7 +23,6 @@ import argparse
 
 from . import builtins
 from . import builtin_build
-from . import builtin_checkout
 from . import builtin_deploy_root
 from . import builtin_deploy_qemu
 from . import builtin_git_mirror
@@ -33,7 +32,8 @@ from . import builtin_privhelper_deploy_qemu
 from . import builtin_resolve
 from . import builtin_source_diff
 
-JS_BUILTINS = {'autobuilder': 'Run resolve and build'}
+JS_BUILTINS = {'autobuilder': 'Run resolve and build',
+               'checkout': 'Check out source tree'}
 
 def usage(ecode):
     print "Builtins:"
@@ -60,7 +60,7 @@ def main(args):
                 return usage(1)
             else:
                 child_args = ['ostbuild-js', name]
-                child_args.extend(args[:1])
+                child_args.extend(args[1:])
                 os.execvp('ostbuild-js', child_args)
         return builtin.execute(args[1:])
     



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