[gnome-ostree/wip/tasks-m1] Rewrite tasks system
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-ostree/wip/tasks-m1] Rewrite tasks system
- Date: Mon, 4 Feb 2013 23:22:43 +0000 (UTC)
commit 3963e371f69b6db11ab47335a93325df3a1b080b
Author: Colin Walters <walters verbum org>
Date: Thu Jan 24 09:13:51 2013 -0500
Rewrite tasks system
Merge SubTask and DynTask for now - drop the dependencies, clean
things up a bit. Move resolve, build, builddisks to be tasks.
The autobuilder now is updated to use this.
Add a new bdiff task.
There's a new "ostbuild make" builtin that runs tasks. The benefit of
this when you do a build directly, like:
$ ostbuild make build/gnomeos-3.8
It's consistently run in a tempdir, logged etc., in the same way that
the autobuilder does it.
Makefile-ostbuild.am | 13 +-
src/ostbuild/js/buildutil.js | 70 ++---
src/ostbuild/js/builtins/autobuilder.js | 310 +++++-------------
src/ostbuild/js/builtins/git_mirror.js | 28 +-
src/ostbuild/js/builtins/make.js | 108 ++++++
src/ostbuild/js/builtins/resolve.js | 140 --------
src/ostbuild/js/builtins/run_task.js | 55 +++
src/ostbuild/js/dyntask.js | 244 --------------
src/ostbuild/js/jsonutil.js | 38 +++
src/ostbuild/js/main.js | 2 +
src/ostbuild/js/procutil.js | 2 +-
src/ostbuild/js/snapshot.js | 75 ++++-
src/ostbuild/js/subtask.js | 181 ----------
src/ostbuild/js/task.js | 345 ++++++++++++++++++++
src/ostbuild/js/tasks/task-bdiff.js | 153 +++++++++
.../js/{builtins/build.js => tasks/task-build.js} | 107 +++----
.../build_disks.js => tasks/task-builddisks.js} | 71 +++--
src/ostbuild/js/tasks/task-checksum.js | 123 -------
src/ostbuild/js/tasks/task-resolve.js | 84 +++++
src/ostbuild/js/vcs.js | 39 ++-
src/ostbuild/ostbuild.in | 2 +-
21 files changed, 1114 insertions(+), 1076 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index ef0ef1f..940aac4 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -43,7 +43,7 @@ jsostbuild_DATA= \
src/ostbuild/js/buildutil.js \
src/ostbuild/js/builtin.js \
src/ostbuild/js/config.js \
- src/ostbuild/js/dyntask.js \
+ src/ostbuild/js/task.js \
src/ostbuild/js/jsondb.js \
src/ostbuild/js/jsonutil.js \
src/ostbuild/js/main.js \
@@ -53,28 +53,29 @@ jsostbuild_DATA= \
src/ostbuild/js/procutil.js \
src/ostbuild/js/snapshot.js \
src/ostbuild/js/streamutil.js \
- src/ostbuild/js/subtask.js \
src/ostbuild/js/vcs.js \
$(NULL)
jsostbuiltinsdir=$(jsostbuilddir)/builtins
jsostbuiltins_DATA= \
src/ostbuild/js/builtins/autobuilder.js \
- src/ostbuild/js/builtins/build.js \
- src/ostbuild/js/builtins/build_disks.js \
src/ostbuild/js/builtins/checkout.js \
src/ostbuild/js/builtins/git_mirror.js \
+ src/ostbuild/js/builtins/make.js \
src/ostbuild/js/builtins/qa_make_disk.js \
src/ostbuild/js/builtins/qa_pull_deploy.js \
src/ostbuild/js/builtins/qa_smoketest.js \
src/ostbuild/js/builtins/prefix.js \
- src/ostbuild/js/builtins/resolve.js \
+ src/ostbuild/js/builtins/run_task.js \
src/ostbuild/js/builtins/shell.js \
$(NULL)
jsosttasksdir=$(jsostbuilddir)/tasks
jsosttasks_DATA= \
- src/ostbuild/js/tasks/task-checksum.js \
+ src/ostbuild/js/tasks/task-build.js \
+ src/ostbuild/js/tasks/task-resolve.js \
+ src/ostbuild/js/tasks/task-bdiff.js \
+ src/ostbuild/js/tasks/task-builddisks.js \
$(NULL)
endif
diff --git a/src/ostbuild/js/buildutil.js b/src/ostbuild/js/buildutil.js
index 0f3d1c7..a085992 100644
--- a/src/ostbuild/js/buildutil.js
+++ b/src/ostbuild/js/buildutil.js
@@ -42,50 +42,7 @@ function parseSrcKey(srckey) {
return [keytype, uri];
}
-function resolveComponent(manifest, componentMeta) {
- let result = {};
- Lang.copyProperties(componentMeta, result);
- let origSrc = componentMeta['src'];
- let didExpand = false;
- let vcsConfig = manifest['vcsconfig'];
- for (let vcsprefix in vcsConfig) {
- let expansion = vcsConfig[vcsprefix];
- let prefix = vcsprefix + ':';
- if (origSrc.indexOf(prefix) == 0) {
- result['src'] = expansion + origSrc.substr(prefix.length);
- didExpand = true;
- break;
- }
- }
-
- let name = componentMeta['name'];
- let src, idx, name;
- if (name == undefined) {
- if (didExpand) {
- src = origSrc;
- idx = src.lastIndexOf(':');
- name = src.substr(idx+1);
- } else {
- src = result['src'];
- idx = src.lastIndexOf('/');
- name = src.substr(idx+1);
- }
- let i = name.lastIndexOf('.git');
- if (i != -1 && i == name.length - 4) {
- name = name.substr(0, name.length - 4);
- }
- name = name.replace(/\//g, '-');
- result['name'] = name;
- }
-
- let branchOrTag = result['branch'] || result['tag'];
- if (!branchOrTag) {
- result['branch'] = 'master';
- }
-
- return result;
-}
function getPatchPathsForComponent(patchdir, component) {
let patches = component['patches'];
@@ -127,3 +84,30 @@ function getBaseUserChrootArgs() {
let path = findUserChrootPath();
return [path.get_path(), '--unshare-pid', '--unshare-ipc', '--unshare-net'];
}
+
+function compareVersions(a, b) {
+ let adot = a.indexOf('.');
+ while (adot != -1) {
+ let bdot = b.indexOf('.');
+ if (bdot == -1)
+ return 1;
+ let aSub = parseInt(a.substr(0, adot));
+ let bSub = parseInt(b.substr(0, bdot));
+ if (aSub > bSub)
+ return 1;
+ else if (aSub < bSub)
+ return -1;
+ a = a.substr(adot + 1);
+ b = b.substr(bdot + 1);
+ adot = a.indexOf('.');
+ }
+ if (b.indexOf('.') != -1)
+ return -1;
+ let aSub = parseInt(a);
+ let bSub = parseInt(b);
+ if (aSub > bSub)
+ return 1;
+ else if (aSub < bSub)
+ return -1;
+ return 0;
+}
diff --git a/src/ostbuild/js/builtins/autobuilder.js b/src/ostbuild/js/builtins/autobuilder.js
index 17268c8..18709fb 100644
--- a/src/ostbuild/js/builtins/autobuilder.js
+++ b/src/ostbuild/js/builtins/autobuilder.js
@@ -23,7 +23,7 @@ const Format = imports.format;
const GSystem = imports.gi.GSystem;
const Builtin = imports.builtin;
-const SubTask = imports.subtask;
+const Task = imports.task;
const JsonDB = imports.jsondb;
const ProcUtil = imports.procutil;
const JsonUtil = imports.jsonutil;
@@ -54,14 +54,12 @@ const Autobuilder = new Lang.Class({
this._stages = ['resolve', 'build', 'builddisks', 'smoke'];
- this._build_needed = true;
- this._do_builddisks = false;
- this._do_qa = false;
- this._full_resolve_needed = true;
- this._queued_force_resolve = [];
- this._resolve_timeout = 0;
- this._source_snapshot_path = null;
- this._prev_source_snapshot_path = null;
+ this._buildNeeded = true;
+ this._fullResolveNeeded = true;
+ this._resolveNeeded = false;
+ this._resolveTimeout = 0;
+ this._sourceSnapshotPath = null;
+ this._prevSourceSnapshotPath = null;
},
execute: function(args, loop, cancellable) {
@@ -69,13 +67,16 @@ const Autobuilder = new Lang.Class({
this._autoupdate_self = args.autoupdate_self;
if (!args.stage)
- args.stage = 'smoke';
+ args.stage = 'build';
this._stageIndex = this._stages.indexOf(args.stage);
if (this._stageIndex < 0)
throw new Error("Unknown stage " + args.stage);
this._do_builddisks = this._stageIndex >= this._stages.indexOf('builddisks');
this._do_smoke = this._stageIndex >= this._stages.indexOf('smoke');
+ this._resolveTaskName = 'resolve/' + this.prefix;
+ this._buildTaskName = 'build/' + this.prefix;
+
this._status_path = this.workdir.get_child('autobuilder-' + this.prefix + '.json');
this._manifestPath = Gio.File.new_for_path('manifest.json');
@@ -89,35 +90,43 @@ const Autobuilder = new Lang.Class({
this._snapshot_dir = this.workdir.get_child('snapshots').get_child(this.prefix);
this._src_db = new JsonDB.JsonDB(this._snapshot_dir);
- let taskdir = this.workdir.get_child('tasks');
- this._resolve_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-resolve'));
- this._build_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-build'));
- this._builddisks_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-build-disks'));
- this._smoke_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-smoke'));
-
- this._source_snapshot_path = this._src_db.getLatestPath();
+ this._taskmaster = new Task.TaskMaster(this.workdir.get_child('tasks'),
+ { onEmpty: Lang.bind(this, this._onTasksComplete) });
+ this._taskmaster.connect('task-complete', Lang.bind(this, this._onTaskCompleted));
+ this._sourceSnapshotPath = this._src_db.getLatestPath();
this._status_path = this.workdir.get_child('autobuilder-' + this.prefix + '.json');
- this._resolve_timeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
- 60 * 10, Lang.bind(this, this._fetchAll));
- this._fetchAll();
- if (this._source_snapshot_path != null)
- this._run_build();
+ this._resolveTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
+ 60 * 10, Lang.bind(this, this._triggerFullResolve));
+ this._runResolve();
+ if (this._sourceSnapshotPath != null)
+ this._runBuild();
this._updateStatus();
loop.run();
},
+ _onTasksComplete: function() {
+ },
+
+ _onTaskCompleted: function(taskmaster, task, success, error) {
+ if (task.name == this._resolveTaskName) {
+ this._onResolveExited(task, success, error);
+ } else if (task.name == this._buildTaskName) {
+ this._onBuildExited(task, success, error);
+ }
+ this._updateStatus();
+ },
+
_updateStatus: function() {
let newStatus = "";
- if (this._resolve_taskset.isRunning())
- newStatus += "[resolving] ";
- if (this._build_taskset.isRunning())
- newStatus += " [building] ";
- if (this._builddisks_taskset.isRunning())
- newStatus += " [disks] ";
+ let taskstateList = this._taskmaster.getTaskState();
+ for (let i = 0; i < taskstateList.length; i++) {
+ let taskstate = taskstateList[i];
+ newStatus += (taskstate.task.name + " ");
+ }
if (newStatus == "")
newStatus = "[idle]";
if (newStatus != this._status) {
@@ -125,8 +134,6 @@ const Autobuilder = new Lang.Class({
print(this._status);
this._impl.emit_property_changed('Status', new GLib.Variant("s", this._status));
}
-
- this._writeStatusFile();
},
get Status() {
@@ -135,234 +142,89 @@ const Autobuilder = new Lang.Class({
queueResolve: function(srcUrls) {
let matchingComponents = [];
- let snapshotData = this._src_db.loadFromPath(this._source_snapshot_path, null);
- let snapshot = new Snapshot.Snapshot(snapshotData, this._source_snapshot_path);
+ let snapshotData = this._src_db.loadFromPath(this._sourceSnapshotPath, null);
+ let snapshot = new Snapshot.Snapshot(snapshotData, this._sourceSnapshotPath);
+ let matched = false;
for (let i = 0; i < srcUrls.length; i++) {
let matches = snapshot.getMatchingSrc(srcUrls[i]);
- for (let j = 0; j < matches.length; j++)
- matchingComponents.push(matches[j]['name']);
- }
- if (matchingComponents.length > 0) {
- this._queued_force_resolve.push.apply(this._queued_force_resolve, matchingComponents);
- print("queued resolves: " + matchingComponents.join(' '));
- if (!this._resolve_taskset.isRunning())
- this._fetch();
- } else {
- print("Ignored fetch requests for unknown URLs: " + srcUrls.join(','));
+ for (let j = 0; j < matches.length; j++) {
+ this._queuedForceResolve.push.apply(this._queuedForceResolve, matches[i]['name']);
+ matched = true;
+ }
}
+ if (matched)
+ this._resolveNeeded = true;
+ this._runResolve();
},
- _fetchAll: function() {
- this._full_resolve_needed = true;
- if (!this._resolve_taskset.isRunning())
- this._fetch();
+ _triggerFullResolve: function() {
+ this._fullResolveNeeded = true;
+ this._runResolve();
return true;
},
- _fetch: function() {
+ _runResolve: function() {
let cancellable = null;
+
+ if (!(this._resolveNeeded || this._fullResolveNeeded))
+ return;
+
+ if (this._taskmaster.isTaskQueued(this._resolveTaskName))
+ return;
if (this._autoupdate_self)
ProcUtil.runSync(['git', 'pull', '-r'], cancellable)
- let args = ['ostbuild', 'resolve', '--manifest=manifest.json',
- '--fetch', '--fetch-keep-going'];
- let isFull;
- if (this._full_resolve_needed) {
- this._full_resolve_needed = false;
- isFull = true;
- } else if (this._queued_force_resolve.length > 0) {
- args.push.apply(args, this._queued_force_resolve);
- isFull = false;
+ if (this._fullResolveNeeded) {
+ this._fullResolveNeeded = false;
+ this._taskmaster.pushTask(this._resolveTaskName,
+ { fetchAll: true });
} else {
- throw new Error("_fetch() when not needed");
+ this._taskmaster.pushTask(this._resolveTaskName,
+ { fetchComponents: this._queuedForceResolve });
}
- this._queued_force_resolve = [];
- let context = new GSystem.SubprocessContext({ argv: args });
- let workdir = this._resolve_taskset.prepare();
- let tmpManifest = workdir.get_child(this._manifestPath.get_basename());
- GSystem.file_linkcopy(this._manifestPath, tmpManifest, Gio.FileCopyFlags.OVERWRITE, cancellable);
- let t = this._resolve_taskset.start(context,
- cancellable,
- Lang.bind(this, this._onResolveExited));
- print(Format.vprintf("Resolve task %s started (%s)", [t.versionstr, isFull ? "full" : "incremental"]));
+ this._queuedForceResolve = [];
this._updateStatus();
-
- return false;
},
_onResolveExited: function(resolveTask, success, msg) {
print(Format.vprintf("resolve exited; success=%s msg=%s", [success, msg]))
- this._prev_source_snapshot_path = this._source_snapshot_path;
- this._source_snapshot_path = this._src_db.getLatestPath();
- let changed = (this._prev_source_snapshot_path == null ||
- !this._prev_source_snapshot_path.equal(this._source_snapshot_path));
+ this._prevSourceSnapshotPath = this._sourceSnapshotPath;
+ this._sourceSnapshotPath = this._src_db.getLatestPath();
+ let changed = (this._prevSourceSnapshotPath == null ||
+ !this._prevSourceSnapshotPath.equal(this._sourceSnapshotPath));
if (changed)
- print(Format.vprintf("New version is %s", [this._source_snapshot_path.get_path()]))
- if (!this._build_needed)
- this._build_needed = changed;
- if (this._build_needed && !this._build_taskset.isRunning())
- this._run_build();
-
- if (this._full_resolve_needed || this._queued_force_resolve.length > 0) {
- this._fetch();
- }
-
+ print(Format.vprintf("New version is %s", [this._sourceSnapshotPath.get_path()]))
+ if (!this._buildNeeded)
+ this._buildNeeded = changed;
+ this._runBuild();
+ this._runResolve();
this._updateStatus();
},
-
- _run_build: function() {
- let cancellable = null;
- if (this._build_taskset.isRunning()) throw new Error();
- if (!this._build_needed) throw new Error();
- this._build_needed = false;
-
- let snapshotName = this._source_snapshot_path.get_basename();
-
- let workdir = this._build_taskset.prepare();
- let tmpSnapshotPath = workdir.get_child(snapshotName);
- GSystem.file_linkcopy(this._source_snapshot_path, tmpSnapshotPath,
- Gio.FileCopyFlags.OVERWRITE, cancellable);
-
- let version = this._src_db.parseVersionStr(this._source_snapshot_path.get_basename());
- let meta = {'version': version,
- 'version-path': this._snapshot_dir.get_relative_path(this._source_snapshot_path)};
- let metaPath = workdir.get_child('meta.json');
- JsonUtil.writeJsonFileAtomic(metaPath, meta, cancellable);
-
- let args = ['ostbuild', 'build', '--snapshot=' + snapshotName];
-
- let context = new GSystem.SubprocessContext({ argv: args });
- let task = this._build_taskset.start(context,
- cancellable,
- Lang.bind(this, this._onBuildExited));
- print(Format.vprintf("Build task %s started", [task.versionstr]));
-
- this._updateStatus();
+ _onBuildExited: function(buildTaskset, success, msg) {
+ print(Format.vprintf("build exited; success=%s msg=%s", [success, msg]))
+ if (this._buildNeeded)
+ this._runBuild()
+
+ this._updateStatus();
},
- _run_builddisks: function() {
+
+ _runBuild: function() {
let cancellable = null;
-
- if (!this._do_builddisks || this._builddisks_taskset.isRunning())
+ if (this._taskmaster.isTaskQueued(this._buildTaskName))
return;
-
- let args = ['ostbuild', 'build-disks'];
-
- let context = new GSystem.SubprocessContext({ argv: args });
- let task = this._builddisks_taskset.start(context,
- cancellable,
- Lang.bind(this, this._onBuildDisksExited));
- print(Format.vprintf("Builddisks task %s started", [task.versionstr]));
-
- this._updateStatus();
- },
-
- _run_smoke: function() {
- let cancellable = null;
-
- if (!this._do_smoke || this._smoke_taskset.isRunning())
+ if (!this._buildNeeded)
return;
- let args = ['ostbuild', 'qa-smoketest'];
+ this._buildNeeded = false;
- let context = new GSystem.SubprocessContext({ argv: args });
- let task = this._smoke_taskset.start(context,
- cancellable,
- Lang.bind(this, this._onSmokeExited));
- print(Format.vprintf("Smoke task %s started", [task.versionstr]));
+ let snapshotName = this._sourceSnapshotPath.get_basename();
- this._updateStatus();
- },
+ this._taskmaster.pushTask(this._buildTaskName);
- _onBuildExited: function(buildTaskset, success, msg) {
- print(Format.vprintf("build exited; success=%s msg=%s", [success, msg]))
- if (this._build_needed)
- this._run_build()
- if (success)
- this._run_builddisks();
-
- this._updateStatus();
- },
-
- _onBuildDisksExited: function(buildTaskset, success, msg) {
- print(Format.vprintf("builddisks exited; success=%s msg=%s", [success, msg]))
this._updateStatus();
-
- if (success)
- this._run_smoke();
-
- this._updateStatus();
- },
-
- _getBuildDiffForTask: function(task) {
- let cancellable = null;
- if (task.build_diff != undefined)
- return task.build_diff;
- let metaPath = task.path.get_child('meta.json');
- if (!metaPath.query_exists(null)) {
- task.build_diff = null;
- return task.build_diff;
- }
- let meta = JsonUtil.loadJson(metaPath, cancellable);
- let snapshotPath = this._snapshot_dir.get_child(meta['version-path']);
- let prevSnapshotPath = this._src_db.getPreviousPath(snapshotPath);
- if (prevSnapshotPath == null) {
- task.build_diff = null;
- } else {
- task.build_diff = Snapshot.snapshotDiff(this._src_db.loadFromPath(snapshotPath, cancellable),
- this._src_db.loadFromPath(prevSnapshotPath, cancellable));
- }
- return task.build_diff;
- },
-
- _buildHistoryToJson: function() {
- let cancellable = null;
- let history = this._build_taskset.getHistory();
- let l = history.length;
- let MAXITEMS = 5;
- let entries = [];
- for (let i = Math.max(l - MAXITEMS, 0); i >= 0 && i < l; i++) {
- let item = history[i];
- let data = {v: item.versionstr,
- state: item.state,
- timestamp: item.timestamp};
- entries.push(data);
- let metaPath = item.path.get_child('meta.json');
- if (metaPath.query_exists(cancellable)) {
- data['meta'] = JsonUtil.loadJson(metaPath, cancellable);
- }
- data['diff'] = this._getBuildDiffForTask(item);
- }
- return entries;
- },
-
- _writeStatusFile: function() {
- let cancellable = null;
- let status = {'prefix': this.prefix};
- if (this._source_snapshot_path != null) {
- let version = this._src_db.parseVersionStr(this._source_snapshot_path.get_basename());
- status['version'] = version;
- status['version-path'] = this._snapshot_dir.get_relative_path(this._source_snapshot_path);
- } else {
- status['version'] = '';
- }
-
- status['build'] = this._buildHistoryToJson();
-
- if (this._build_proc != null) {
- let buildHistory = this._build_taskset.getHistory();
- let activeBuild = buildHistory[buildHistory.length-1];
- let buildStatus = status['build'];
- let activeBuildJson = buildStatus[buildStatus.length-1];
- let statusPath = activeBuild.path.get_child('status.json');
- if (statusPath.query_exists(null)) {
- activeBuildJson['build-status'] = JsonUtil.loadJson(statusPath);
- }
- }
-
- JsonUtil.writeJsonFileAtomic(this._status_path, status, cancellable);
}
});
diff --git a/src/ostbuild/js/builtins/git_mirror.js b/src/ostbuild/js/builtins/git_mirror.js
index b1101c3..d1c05cb 100644
--- a/src/ostbuild/js/builtins/git_mirror.js
+++ b/src/ostbuild/js/builtins/git_mirror.js
@@ -41,6 +41,7 @@ const GitMirror = new Lang.Class({
this.parser.addArgument('--prefix');
this.parser.addArgument('--manifest');
this.parser.addArgument('--snapshot');
+ this.parser.addArgument('--timeout-sec', { help: "Cache fetch results for provided number of seconds" });
this.parser.addArgument('--fetch', {action:'storeTrue',
help:"Also do a git fetch for components"});
this.parser.addArgument(['-k', '--keep-going'], {action:'storeTrue',
@@ -51,17 +52,13 @@ const GitMirror = new Lang.Class({
execute: function(args, loop, cancellable) {
let parser = new ArgParse.ArgumentParser();
+ if (!args.timeout_sec)
+ args.timeout_sec = 0;
+
if (args.manifest != null) {
- let snapshotData = JsonUtil.loadJson(Gio.File.new_for_path(args.manifest), cancellable);
- let resolvedComponents = [];
- let components = snapshotData['components'];
- for (let i = 0; i < components.length; i++) {
- resolvedComponents.push(BuildUtil.resolveComponent(snapshotData, components[i]));
- }
- snapshotData['components'] = resolvedComponents;
- snapshotData['patches'] = BuildUtil.resolveComponent(snapshotData, snapshotData['patches']);
- snapshotData['base'] = BuildUtil.resolveComponent(snapshotData, snapshotData['base']);
- this._snapshot = new Snapshot.Snapshot(snapshotData, null);
+ let manifestPath = Gio.File.new_for_path(args.manifest)
+ let manifestData = JsonUtil.loadJson(manifestPath, cancellable);
+ this._snapshot = new Snapshot.Snapshot(manifestData, manifestPath, { prepareResolve: true });
} else {
this._initSnapshot(args.prefix, args.snapshot, cancellable);
}
@@ -74,18 +71,15 @@ const GitMirror = new Lang.Class({
}
componentNames.forEach(Lang.bind(this, function (name) {
- let component = this._snapshot.getComponent(name);
- let src = component['src']
- let [keytype, uri] = Vcs.parseSrcKey(src);
- let branch = component['branch'];
- let tag = component['tag'];
- let branchOrTag = branch || tag;
+ let [keytype, uri, branchOrTag] = this._snapshot.getVcsInfo(name);
if (!args.fetch) {
Vcs.ensureVcsMirror(this.mirrordir, keytype, uri, branchOrTag, cancellable);
} else {
print("Running git fetch for " + name);
- Vcs.fetch(this.mirrordir, keytype, uri, branchOrTag, cancellable, {keepGoing:args.keep_going});
+ Vcs.fetch(this.mirrordir, keytype, uri, branchOrTag, cancellable,
+ { keepGoing:args.keep_going,
+ timeoutSec: args.timeout_sec });
}
}));
}
diff --git a/src/ostbuild/js/builtins/make.js b/src/ostbuild/js/builtins/make.js
new file mode 100644
index 0000000..153cdfd
--- /dev/null
+++ b/src/ostbuild/js/builtins/make.js
@@ -0,0 +1,108 @@
+// Copyright (C) 2012,2013 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 GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const Builtin = imports.builtin;
+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 Make = new Lang.Class({
+ Name: 'Make',
+ Extends: Builtin.Builtin,
+
+ DESCRIPTION: "Execute tasks",
+
+ _init: function() {
+ this.parent();
+ this.parser.addArgument('taskname');
+ this.parser.addArgument('parameters', { nargs: '*' });
+ },
+
+ execute: function(args, loop, cancellable) {
+ this._loop = loop;
+ this._err = null;
+ let taskmaster = new Task.TaskMaster(this.workdir.get_child('tasks'),
+ { onEmpty: Lang.bind(this, this._onTasksComplete) });
+ this._taskmaster = taskmaster;
+ taskmaster.connect('task-completed', Lang.bind(this, this._onTaskCompleted));
+ let params = {};
+ for (let i = 0; i < args.parameters.length; i++) {
+ let param = args.parameters[i];
+ let idx = param.indexOf('=');
+ if (idx == -1)
+ throw new Error("Invalid key=value syntax");
+ let k = param.substr(0, idx);
+ let v = JSON.parse(param.substr(idx+1));
+ params[k] = v;
+ }
+ taskmaster.pushTask(args.taskname, params);
+ this._console = GSystem.Console.get();
+ if (this._console) {
+ this._console.begin_status_line("", cancellable);
+ GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, Lang.bind(this, this._idleUpdateStatus));
+ }
+ loop.run();
+ if (this._console)
+ this._console.end_status_line(cancellable);
+ if (this._err)
+ throw new Error("Error: " + this._err);
+ else
+ print("Success!")
+ },
+
+ _idleUpdateStatus: function() {
+ let [success, loadavg, len] = GLib.file_get_contents('/proc/loadavg');
+ loadavg = loadavg.toString();
+ let elts = loadavg.split(' ');
+ let loadAvg = elts[0];
+ let schedulables = elts[3];
+
+ let taskstateList = this._taskmaster.getTaskState();
+ let taskNames = "";
+ for (let i = 0; i < taskstateList.length; i++) {
+ let taskstate = taskstateList[i];
+ taskNames += (taskstate.task.name + " ");
+ }
+
+ this._console.begin_status_line("running: " + taskNames + "; load=" + loadAvg + " sched=" + schedulables, null);
+
+ return true;
+ },
+
+ _onTaskCompleted: function(taskmaster, task, result, error) {
+ print("Task " + task.TaskName + " complete: " + task.dir.get_path());
+ },
+
+ _onTasksComplete: function(success, err) {
+ if (!success)
+ this._err = err;
+ this._loop.quit();
+ }
+});
diff --git a/src/ostbuild/js/builtins/run_task.js b/src/ostbuild/js/builtins/run_task.js
new file mode 100644
index 0000000..4a34c32
--- /dev/null
+++ b/src/ostbuild/js/builtins/run_task.js
@@ -0,0 +1,55 @@
+// Copyright (C) 2012,2013 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 GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const Builtin = imports.builtin;
+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 RunTask = new Lang.Class({
+ Name: 'RunTask',
+ Extends: Builtin.Builtin,
+
+ DESCRIPTION: "Internal helper to execute a task",
+
+ _init: function() {
+ this.parent();
+ this.parser.addArgument('taskName');
+ this.parser.addArgument('parameters');
+ },
+
+ execute: function(args, loop, cancellable) {
+ let taskset = Task.TaskSet.prototype.getInstance();
+ let [taskDef, vars] = taskset.getTask(args.taskName);
+ let params = JSON.parse(args.parameters);
+ let instance = new taskDef(null, args.taskName, vars, params);
+ instance.execute(cancellable);
+ }
+});
diff --git a/src/ostbuild/js/jsonutil.js b/src/ostbuild/js/jsonutil.js
index 04a82ca..24deb88 100644
--- a/src/ostbuild/js/jsonutil.js
+++ b/src/ostbuild/js/jsonutil.js
@@ -27,6 +27,44 @@ function writeJsonToStream(stream, data, cancellable) {
stream.write_bytes(new GLib.Bytes(buf), cancellable);
}
+function writeJsonToStreamAsync(stream, data, cancellable, onComplete) {
+ let buf = JSON.stringify(data, null, " ");
+ stream.write_bytes_async(new GLib.Bytes(buf), GLib.PRIORITY_DEFAULT,
+ cancellable, function(stream, result) {
+ let err = null;
+ try {
+ stream.write_bytes_finish(result);
+ } catch (e) {
+ err = e;
+ }
+ onComplete(err != null, err);
+ });
+}
+
+function loadJsonFromStream(stream, cancellable) {
+ let membuf = Gio.MemoryOutputStream.new_resizable();
+ membuf.splice(stream, Gio.OutputStreamSpliceFlags.CLOSE_TARGET, cancellable);
+ let bytes = membuf.steal_as_bytes();
+ return JSON.parse(bytes.toArray().toString());
+}
+
+function loadJsonFromStreamAsync(stream, cancellable, onComplete) {
+ let membuf = Gio.MemoryOutputStream.new_resizable();
+ membuf.splice_async(stream, Gio.OutputStreamSpliceFlags.CLOSE_TARGET, GLib.PRIORITY_DEFAULT,
+ cancellable, function(stream, result) {
+ let err = null;
+ let res = null;
+ try {
+ stream.splice_finish(result);
+ let bytes = membuf.steal_as_bytes();
+ res = JSON.parse(bytes.toArray().toString());
+ } catch (e) {
+ err = e;
+ }
+ onComplete(res, err);
+ });
+}
+
function writeJsonFileAtomic(path, data, cancellable) {
let s = path.replace(null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
writeJsonToStream(s, data, cancellable);
diff --git a/src/ostbuild/js/main.js b/src/ostbuild/js/main.js
index ae92800..0f1e2e2 100755
--- a/src/ostbuild/js/main.js
+++ b/src/ostbuild/js/main.js
@@ -26,7 +26,9 @@ const BUILTINS = ['autobuilder',
'resolve',
'build',
'build-disks',
+ 'make',
'shell',
+ 'run-task',
'qa-make-disk',
'qa-pull-deploy',
'qa-smoketest'];
diff --git a/src/ostbuild/js/procutil.js b/src/ostbuild/js/procutil.js
index 0af1ab3..3c30ab3 100644
--- a/src/ostbuild/js/procutil.js
+++ b/src/ostbuild/js/procutil.js
@@ -92,7 +92,7 @@ function _runSyncGetOutputInternal(argv, cancellable, params, splitLines) {
let [line, len] = dataIn.read_line_utf8(cancellable);
if (line == null)
break;
- result += line;
+ result += (line + '\n');
}
}
_wait_sync_check_internal(proc, cancellable);
diff --git a/src/ostbuild/js/snapshot.js b/src/ostbuild/js/snapshot.js
index 51dca5f..fbe760c 100644
--- a/src/ostbuild/js/snapshot.js
+++ b/src/ostbuild/js/snapshot.js
@@ -21,14 +21,17 @@ const Lang = imports.lang;
const JsonDB = imports.jsondb;
const JsonUtil = imports.jsonutil;
+const Vcs = imports.vcs;
const Params = imports.params;
function _componentDict(snapshot) {
let r = {};
let components = snapshot['components'];
- for (let i = 0; i< components.length; i++) {
+ for (let i = 0; i < components.length; i++) {
let component = components[i];
let name = component['name'];
+ if (r[name])
+ throw new Error("Duplicate component name " + name);
r[name] = component;
}
let patches = snapshot['patches'];
@@ -66,15 +69,69 @@ function snapshotDiff(a, b) {
const Snapshot = new Lang.Class({
Name: 'Snapshot',
- _init: function(data, path) {
+ _init: function(data, path, params) {
+ params = Params.parse(params, { prepareResolve: false });
this.data = data;
this.path = path;
+ if (params.prepareResolve) {
+ data['patches'] = this._resolveComponent(data, data['patches']);
+ data['base'] = this._resolveComponent(data, data['base']);
+ for (let i = 0; i < data['components'].length; i++) {
+ let component = this._resolveComponent(data, data['components'][i]);
+ data['components'][i] = component;
+ }
+ }
this._componentDict = _componentDict(data);
this._componentNames = [];
for (let k in this._componentDict)
this._componentNames.push(k);
},
+ _resolveComponent: function(manifest, componentMeta) {
+ let result = {};
+ Lang.copyProperties(componentMeta, result);
+ let origSrc = componentMeta['src'];
+
+ let didExpand = false;
+ let vcsConfig = manifest['vcsconfig'];
+ for (let vcsprefix in vcsConfig) {
+ let expansion = vcsConfig[vcsprefix];
+ let prefix = vcsprefix + ':';
+ if (origSrc.indexOf(prefix) == 0) {
+ result['src'] = expansion + origSrc.substr(prefix.length);
+ didExpand = true;
+ break;
+ }
+ }
+
+ let name = componentMeta['name'];
+ let src, idx, name;
+ if (name == undefined) {
+ if (didExpand) {
+ src = origSrc;
+ idx = src.lastIndexOf(':');
+ name = src.substr(idx+1);
+ } else {
+ src = result['src'];
+ idx = src.lastIndexOf('/');
+ name = src.substr(idx+1);
+ }
+ let i = name.lastIndexOf('.git');
+ if (i != -1 && i == name.length - 4) {
+ name = name.substr(0, name.length - 4);
+ }
+ name = name.replace(/\//g, '-');
+ result['name'] = name;
+ }
+
+ let branchOrTag = result['branch'] || result['tag'];
+ if (!branchOrTag) {
+ result['branch'] = 'master';
+ }
+
+ return result;
+ },
+
_expandComponent: function(component) {
let r = {};
Lang.copyProperties(component, r);
@@ -98,6 +155,10 @@ const Snapshot = new Lang.Class({
return this._componentNames;
},
+ getComponentMap: function() {
+ return this._componentDict;
+ },
+
getComponent: function(name, allowNone) {
let r = this._componentDict[name] || null;
if (!r && !allowNone)
@@ -118,5 +179,15 @@ const Snapshot = new Lang.Class({
getExpanded: function(name) {
return this._expandComponent(this.getComponent(name));
+ },
+
+ getVcsInfo: function(name) {
+ let component = this.getComponent(name);
+ let src = component['src']
+ let [keytype, uri] = Vcs.parseSrcKey(src);
+ let branch = component['branch'];
+ let tag = component['tag'];
+ let branchOrTag = branch || tag;
+ return [keytype, uri, branchOrTag];
}
});
diff --git a/src/ostbuild/js/task.js b/src/ostbuild/js/task.js
new file mode 100644
index 0000000..665033c
--- /dev/null
+++ b/src/ostbuild/js/task.js
@@ -0,0 +1,345 @@
+// Copyright (C) 2012,2013 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 GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const format = imports.format;
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+const GSystem = imports.gi.GSystem;
+const Config = imports.config;
+const Params = imports.params;
+const JsonUtil = imports.jsonutil;
+const JsonDB = imports.jsondb;
+const ProcUtil = imports.procutil;
+const BuildUtil = imports.buildutil;
+
+const VERSION_RE = /(\d+)\.(\d+)/;
+
+var _tasksetInstance = null;
+const TaskSet = new Lang.Class({
+ Name: 'TaskSet',
+
+ _init: function() {
+ this._tasks = [];
+ let taskdir = Gio.File.new_for_path(GLib.getenv('OSTBUILD_DATADIR')).resolve_relative_path('js/tasks');
+ let denum = taskdir.enumerate_children('standard::*', 0, null);
+ let finfo;
+
+ for (let taskmodname in imports.tasks) {
+ let taskMod = imports.tasks[taskmodname];
+ for (let defname in taskMod) {
+ if (defname.indexOf('Task') !== 0
+ || defname == 'Task')
+ continue;
+ let cls = taskMod[defname];
+ this.register(cls);
+ }
+ }
+ },
+
+ register: function(taskdef) {
+ this._tasks.push(taskdef);
+ },
+
+ getAllTasks: function() {
+ return this._tasks;
+ },
+
+ getTask: function(taskName, params) {
+ params = Params.parse(params, { allowNone: false })
+ for (let i = 0; i < this._tasks.length; i++) {
+ let taskDef = this._tasks[i];
+ let pattern = taskDef.prototype.TaskPattern;
+ let re = pattern[0];
+ let match = re.exec(taskName);
+ if (!match)
+ continue;
+ let vars = {};
+ for (let i = 1; i < pattern.length; i++) {
+ vars[pattern[i]] = match[i];
+ }
+ return [taskDef, vars];
+ }
+ if (!params.allowNone)
+ throw new Error("No task definition matches " + taskName);
+ return null;
+ },
+
+ getInstance: function() {
+ if (!_tasksetInstance)
+ _tasksetInstance = new TaskSet();
+ return _tasksetInstance;
+ }
+});
+
+const TaskMaster = new Lang.Class({
+ Name: 'TaskMaster',
+
+ _init: function(path, params) {
+ params = Params.parse(params, {onEmpty: null});
+ this.path = path;
+ this.maxConcurrent = GLib.get_num_processors();
+ this._onEmpty = params.onEmpty;
+ this.cancellable = null;
+ this._idleRecalculateId = 0;
+ this._executing = [];
+ this._pendingTasksList = [];
+ this._seenTasks = {};
+ this._taskErrors = {};
+ this._caughtError = false;
+
+ this._taskset = TaskSet.prototype.getInstance();
+ },
+
+ pushTask: function(taskName, parameters) {
+ if (this.isTaskQueued(taskName))
+ return;
+ let [taskDef, vars] = this._taskset.getTask(taskName);
+ let instance = new taskDef(this, taskName, vars, parameters);
+ instance.onComplete = Lang.bind(this, this._onComplete, instance);
+ this._pendingTasksList.push(instance);
+ this._queueRecalculate();
+ },
+
+ isTaskQueued: function(taskName) {
+ for (let i = 0; i < this._pendingTasksList.length; i++) {
+ let pending = this._pendingTasksList[i];
+ if (pending.TaskName == taskName)
+ return true;
+ }
+ for (let i = 0; i < this._executing.length; i++) {
+ let executingTask = this._executing[i];
+ if (executingTask.TaskName == taskName)
+ return true;
+ }
+ return false;
+ },
+
+ getTaskState: function() {
+ let r = [];
+ for (let i = 0; i < this._pendingTasksList.length; i++) {
+ r.push({running: false, task: this._pendingTasksList[i] });
+ }
+ for (let i = 0; i < this._executing.length; i++) {
+ r.push({running: true, task: this._executing[i] });
+ }
+ return r;
+ },
+
+ _queueRecalculate: function() {
+ if (this._idleRecalculateId > 0)
+ return;
+ this._idleRecalculateId = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._recalculate));
+ },
+
+ _recalculate: function() {
+ this._idleRecalculateId = 0;
+
+ if (this._executing.length == 0 &&
+ this._pendingTasksList.length == 0) {
+ this._onEmpty(true, null);
+ return;
+ } else if (this._pendingTasksList.length == 0) {
+ return;
+ }
+
+ this._reschedule();
+ },
+
+ _onComplete: function(success, error, task) {
+ this.emit('task-complete', task, success, error);
+ let idx = -1;
+ for (let i = 0; i < this._executing.length; i++) {
+ let executingTask = this._executing[i];
+ if (executingTask !== task)
+ continue;
+ idx = i;
+ break;
+ }
+ if (idx == -1)
+ throw new Error("TaskMaster: Internal error - Failed to find completed task:" + task.TaskName);
+ this._executing.splice(idx, 1);
+ this._queueRecalculate();
+ },
+
+ _reschedule: function() {
+ while (this._executing.length < this.maxConcurrent &&
+ this._pendingTasksList.length > 0) {
+ let task = this._pendingTasksList.shift();
+ task._executeInSubprocessInternal(this.cancellable);
+ this._executing.push(task);
+ }
+ }
+});
+Signals.addSignalMethods(TaskMaster.prototype);
+
+const TaskDef = new Lang.Class({
+ Name: 'TaskDef',
+
+ TaskPattern: null,
+
+ PreserveStdout: true,
+ RetainFailed: 1,
+ RetainSuccess: 5,
+
+ DefaultParameters: {},
+
+ _VERSION_RE: /(\d+\d\d\d\d)\.(\d+)/,
+
+ _init: function(taskmaster, name, vars, parameters) {
+ this.taskmaster = taskmaster;
+ this.name = name;
+ this.vars = vars;
+ this.parameters = Params.parse(parameters, this.DefaultParameters);
+
+ this.config = Config.get();
+ this.workdir = Gio.File.new_for_path(this.config.getGlobal('workdir'));
+ this.resultdir = this.workdir.get_child('results');
+ this.mirrordir = Gio.File.new_for_path(this.config.getGlobal('mirrordir'));
+ this.libdir = Gio.File.new_for_path(GLib.getenv('OSTBUILD_LIBDIR'));
+ this.repo = this.workdir.get_child('repo');
+ },
+
+ getDepends: function() {
+ return [];
+ },
+
+ _getResultDb: function(taskname) {
+ let path = this.resultdir.resolve_relative_path(taskname);
+ return new JsonDB.JsonDB(path);
+ },
+
+ _loadVersionsFrom: function(dir, cancellable) {
+ let e = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, cancellable);
+ let info;
+ let results = [];
+ while ((info = e.next_file(cancellable)) != null) {
+ let name = info.get_name();
+ let match = this._VERSION_RE.exec(name);
+ if (!match)
+ continue;
+ results.push(name);
+ }
+ results.sort(BuildUtil.compareVersions);
+ return results;
+ },
+
+ _cleanOldVersions: function(dir, retain, cancellable) {
+ let versions = this._loadVersionsFrom(dir, cancellable);
+ while (versions.length > retain) {
+ let child = dir.get_child(versions.shift());
+ GSystem.shutil_rm_rf(child, cancellable);
+ }
+ },
+
+ execute: function(cancellable) {
+ throw new Error("Not implemented");
+ },
+
+ _executeInSubprocessInternal: function(cancellable) {
+ this._cancellable = cancellable;
+
+ this.dir = this.taskmaster.path.resolve_relative_path(this.name);
+ GSystem.file_ensure_directory(this.dir, true, cancellable);
+
+ let allVersions = [];
+
+ this._successDir = this.dir.get_child('successful');
+ GSystem.file_ensure_directory(this._successDir, true, cancellable);
+ let successVersions = this._loadVersionsFrom(this._successDir, cancellable);
+ for (let i = 0; i < successVersions.length; i++) {
+ allVersions.push([true, successVersions[i]]);
+ }
+
+ this._failedDir = this.dir.get_child('failed');
+ GSystem.file_ensure_directory(this._failedDir, true, cancellable);
+ let failedVersions = this._loadVersionsFrom(this._failedDir, cancellable);
+ for (let i = 0; i < failedVersions.length; i++) {
+ allVersions.push([false, failedVersions[i]]);
+ }
+
+ allVersions.sort(function (a, b) {
+ let [successA, versionA] = a;
+ let [successB, versionB] = b;
+ return BuildUtil.compareVersions(versionA, versionB);
+ });
+
+ let currentTime = GLib.DateTime.new_now_utc();
+
+ let currentYmd = Format.vprintf('%d%02d%02d', [currentTime.get_year(),
+ currentTime.get_month(),
+ currentTime.get_day_of_month()]);
+ let version = null;
+ if (allVersions.length > 0) {
+ let [lastSuccess, lastVersion] = allVersions[allVersions.length-1];
+ let match = this._VERSION_RE.exec(lastVersion);
+ if (!match) throw new Error();
+ let lastYmd = match[1];
+ let lastSerial = match[2];
+ if (lastYmd == currentYmd) {
+ version = currentYmd + '.' + (parseInt(lastSerial) + 1);
+ }
+ }
+ if (version === null) {
+ version = currentYmd + '.0';
+ }
+
+ this._workdir = this.dir.get_child(version);
+ GSystem.shutil_rm_rf(this._workdir, cancellable);
+ GSystem.file_ensure_directory(this._workdir, true, cancellable);
+
+ let baseArgv = ['ostbuild', 'run-task', this.name, JSON.stringify(this.parameters)];
+ let context = new GSystem.SubprocessContext({ argv: baseArgv });
+ context.set_cwd(this._workdir.get_path());
+ context.set_stdin_disposition(GSystem.SubprocessStreamDisposition.PIPE);
+ if (this.PreserveStdout) {
+ let outPath = this._workdir.get_child('output.txt');
+ context.set_stdout_file_path(outPath.get_path());
+ context.set_stderr_disposition(GSystem.SubprocessStreamDisposition.STDERR_MERGE);
+ } else {
+ context.set_stdout_disposition(GSystem.SubprocessStreamDisposition.NULL);
+ let errPath = this._workdir.get_child('errors.txt');
+ context.set_stderr_file_path(errPath.get_path());
+ }
+ this._proc = new GSystem.Subprocess({ context: context });
+ this._proc.init(cancellable);
+
+ this._proc.wait(cancellable, Lang.bind(this, this._onChildExited));
+ },
+
+ _onChildExited: function(proc, result) {
+ let [success, errmsg] = ProcUtil.asyncWaitCheckFinish(proc, result);
+ let target;
+ if (!success) {
+ target = this._failedDir.get_child(this._workdir.get_basename());
+ GSystem.file_rename(this._workdir, target, null);
+ this.dir = target;
+ this._cleanOldVersions(this._failedDir, this.RetainFailed, null);
+ this.onComplete(success, errmsg);
+ } else {
+ target = this._successDir.get_child(this._workdir.get_basename());
+ GSystem.file_rename(this._workdir, target, null);
+ this.dir = target;
+ this._cleanOldVersions(this._successDir, this.RetainSuccess, null);
+ this.onComplete(success, null);
+ }
+ // Also remove any old interrupted versions
+ this._cleanOldVersions(this.dir, 0, null);
+ }
+});
diff --git a/src/ostbuild/js/tasks/task-bdiff.js b/src/ostbuild/js/tasks/task-bdiff.js
new file mode 100644
index 0000000..0a96ae1
--- /dev/null
+++ b/src/ostbuild/js/tasks/task-bdiff.js
@@ -0,0 +1,153 @@
+// Copyright (C) 2011,2012,2013 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 GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const Builtin = imports.builtin;
+const Task = imports.task;
+const JsonDB = imports.jsondb;
+const ProcUtil = imports.procutil;
+const StreamUtil = imports.streamutil;
+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 TaskBdiff = new Lang.Class({
+ Name: "TaskBdiff",
+ Extends: Task.TaskDef,
+
+ TaskPattern: [/bdiff\/(.*?)$/, 'prefix'],
+
+ _gitLogToJson: function(repoDir, specification) {
+ let log = ProcUtil.runSyncGetOutputLines(['git', 'log', '--format=email', specification],
+ null,
+ { cwd: repoDir });
+ let r = [];
+ if (log.length == 0)
+ return r;
+ let currentItem = null;
+ let parsingHeaders = false;
+ let fromRegex = /^From ([0-9a-f]{40}) /;
+ for (let i = 0; i < log.length; i++) {
+ let line = log[i];
+ let match = fromRegex.exec(line);
+ if (match) {
+ if (currentItem !== null) {
+ r.push(currentItem);
+ }
+ currentItem = {'Checksum': match[1]};
+ parsingHeaders = true;
+ } else if (parsingHeaders) {
+ if (line.length == 0) {
+ parsingHeaders = false;
+ } else {
+ let idx = line.indexOf(':');
+ let k = line.substr(0, idx);
+ let v = line.substr(idx+1);
+ currentItem[k] = v;
+ }
+ }
+ }
+ return r;
+ },
+
+ _diffstat: function(repoDir, specification) {
+ return ProcUtil.runSyncGetOutputUTF8(['git', 'diff', '--stat', specification], null,
+ { cwd: repoDir });
+ },
+
+ execute: function(cancellable) {
+ let prefix = this.vars['prefix'];
+
+ this.subworkdir = Gio.File.new_for_path('.');
+
+ let builddb = this._getResultDb('build/' + prefix);
+ let latestPath = builddb.getLatestPath();
+ if (!latestPath)
+ throw new Error("No builds!")
+ let latestBuildVersion = builddb.parseVersionStr(latestPath.get_basename());
+
+ let previousPath = builddb.getPreviousPath(latestPath);
+ if (!previousPath)
+ throw new Error("No build previous to " + latestBuildVersion)
+
+ let latestBuildData = builddb.loadFromPath(latestPath, cancellable);
+ let latestBuildSnapshot = new Snapshot.Snapshot(latestBuildData['snapshot'], null);
+ let previousBuildData = builddb.loadFromPath(previousPath, cancellable);
+ let previousBuildSnapshot = new Snapshot.Snapshot(previousBuildData['snapshot'], null);
+
+ let added = [];
+ let modified = [];
+ let removed = [];
+
+ let result = {fromBuildVersion: builddb.parseVersionStr(previousPath.get_basename()),
+ toBuildVersion: builddb.parseVersionStr(latestPath.get_basename()),
+ fromSrcVersion: builddb.parseVersionStr(previousBuildData['snapshotName']),
+ toSrcVersion: builddb.parseVersionStr(latestBuildData['snapshotName']),
+ added: added,
+ modified: modified,
+ removed: removed};
+
+ let modifiedNames = [];
+
+ let latestComponentMap = latestBuildSnapshot.getComponentMap();
+ let previousComponentMap = previousBuildSnapshot.getComponentMap();
+ for (let componentName in latestComponentMap) {
+ let componentA = latestBuildSnapshot.getComponent(componentName);
+ let componentB = previousBuildSnapshot.getComponent(componentName, true);
+
+ if (componentB === null)
+ added.push(componentName);
+ else if (componentB.revision != componentA.revision)
+ modifiedNames.push(componentName);
+ }
+ for (let componentName in previousComponentMap) {
+ let componentA = latestBuildSnapshot.getComponent(componentName, true);
+
+ if (componentA === null)
+ removed.push(componentName);
+ }
+
+ for (let i = 0; i < modifiedNames.length; i++) {
+ let componentName = modifiedNames[i];
+ let latestComponent = latestBuildSnapshot.getComponent(componentName);
+ let previousComponent = previousBuildSnapshot.getComponent(componentName);
+ let latestRevision = latestComponent.revision;
+ let previousRevision = previousComponent.revision;
+ let [keytype, uri, branchOrTag] = latestBuildSnapshot.getVcsInfo(componentName);
+ let mirrordir = Vcs.getMirrordir(this.mirrordir, keytype, uri);
+
+ let gitlog = this._gitLogToJson(mirrordir, previousRevision + '...' + latestRevision);
+ let diffstat = this._diffstat(mirrordir, previousRevision + '..' + latestRevision);
+ modified.push({ previous: previousComponent,
+ latest: latestComponent,
+ gitlog: gitlog,
+ diffstat: diffstat });
+ }
+
+ let bdiffdb = this._getResultDb('bdiff/' + prefix);
+ bdiffdb.store(result, cancellable);
+ }
+});
diff --git a/src/ostbuild/js/builtins/build.js b/src/ostbuild/js/tasks/task-build.js
similarity index 92%
rename from src/ostbuild/js/builtins/build.js
rename to src/ostbuild/js/tasks/task-build.js
index 7c54a8a..c68a097 100644
--- a/src/ostbuild/js/builtins/build.js
+++ b/src/ostbuild/js/tasks/task-build.js
@@ -23,7 +23,7 @@ const Format = imports.format;
const GSystem = imports.gi.GSystem;
const Builtin = imports.builtin;
-const SubTask = imports.subtask;
+const Task = imports.task;
const JsonDB = imports.jsondb;
const ProcUtil = imports.procutil;
const StreamUtil = imports.streamutil;
@@ -35,13 +35,13 @@ const Vcs = imports.vcs;
const ArgParse = imports.argparse;
const OPT_COMMON_CFLAGS = {'i686': '-O2 -g -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables',
- 'x86_64': '-O2 -g -m64 -mtune=generic'}
+ 'x86_64': '-O2 -g -m64 -mtune=generic'};
-const Build = new Lang.Class({
- Name: "Build",
- Extends: Builtin.Builtin,
+const TaskBuild = new Lang.Class({
+ Name: "TaskBuild",
+ Extends: Task.TaskDef,
- DESCRIPTION: "Build multiple components and generate trees",
+ TaskPattern: [/build\/(.*?)$/, 'prefix'],
_resolveRefs: function(refs) {
if (refs.length == 0)
@@ -290,6 +290,7 @@ const Build = new Lang.Class({
let prefix = this._snapshot.data['prefix'];
let buildname = Format.vprintf('%s/%s/%s', [prefix, basename, architecture]);
+ let unixBuildname = buildname.replace(/\//g, '_');
let buildRef = 'components/' + buildname;
let currentVcsVersion = component['revision'];
@@ -320,16 +321,13 @@ const Build = new Lang.Class({
let patchdir;
if (expandedComponent['patches']) {
let patchesRevision = expandedComponent['patches']['revision'];
- if (this.args.patches_path) {
- patchdir = Gio.File.new_for_path(this.args.patches_path);
- } else if (this._cachedPatchdirRevision == patchesRevision) {
+ if (this._cachedPatchdirRevision == patchesRevision) {
patchdir = this.patchdir;
} else {
patchdir = Vcs.checkoutPatches(this.mirrordir,
this.patchdir,
expandedComponent,
- cancellable,
- {patchesPath: this.args.patches_path});
+ cancellable);
this._cachedPatchdirRevision = patchesRevision;
}
if ((previousMetadata != null) &&
@@ -366,33 +364,31 @@ const Build = new Lang.Class({
}
}
- let taskdir = this.workdir.get_child('tasks');
- let buildTaskset = new SubTask.TaskSet(taskdir.get_child(buildname));
+ let cwd = Gio.File.new_for_path('.');
+ let buildWorkdir = cwd.get_child('tmp-' + unixBuildname);
+ GSystem.file_ensure_directory(buildWorkdir, true, cancellable);
- let workdir = buildTaskset.prepare();
-
- let tempMetadataPath = workdir.get_child('_ostbuild-meta.json');
+ let tempMetadataPath = buildWorkdir.get_child('_ostbuild-meta.json');
JsonUtil.writeJsonFileAtomic(tempMetadataPath, expandedComponent, cancellable);
- let componentSrc = workdir.get_child(basename);
+ let componentSrc = buildWorkdir.get_child(basename);
let childArgs = ['ostbuild', 'checkout', '--snapshot=' + this._snapshot.path.get_path(),
'--checkoutdir=' + componentSrc.get_path(),
'--metadata-path=' + tempMetadataPath.get_path(),
'--overwrite', basename];
- if (this.args.patches_path)
- childArgs.push('--patches-path=' + this.args.patches_path);
- else if (patchdir)
+ if (patchdir) {
childArgs.push('--patches-path=' + patchdir.get_path());
- ProcUtil.runSync(childArgs, cancellable, { logInitiation: true });
+ }
+ ProcUtil.runSync(childArgs, cancellable);
GSystem.file_unlink(tempMetadataPath, cancellable);
- let componentResultdir = workdir.get_child('results');
+ let componentResultdir = buildWorkdir.get_child('results');
GSystem.file_ensure_directory(componentResultdir, true, cancellable);
- let rootdir = this._composeBuildroot(workdir, basename, architecture, cancellable);
+ let rootdir = this._composeBuildroot(buildWorkdir, basename, architecture, cancellable);
- let tmpdir=workdir.get_child('tmp');
+ let tmpdir=buildWorkdir.get_child('tmp');
GSystem.file_ensure_directory(tmpdir, true, cancellable);
let srcCompileOnePath = this.libdir.get_child('ostree-build-compile-one');
@@ -424,26 +420,11 @@ const Build = new Lang.Class({
let context = new GSystem.SubprocessContext({ argv: childArgs });
context.set_environment(ProcUtil.objectToEnvironment(envCopy));
-
- let mainContext = new GLib.MainContext();
- mainContext.push_thread_default();
- let loop = GLib.MainLoop.new(mainContext, true);
- let t;
- try {
- t = buildTaskset.start(context, cancellable, Lang.bind(this, this._onBuildComplete, loop));
- print("Started child process " + context.argv.map(GLib.shell_quote).join(' '));
- loop.run();
- } finally {
- mainContext.pop_thread_default();
- }
- let buildSuccess = this._currentBuildSucceded;
- let msg = this._currentBuildSuccessMsg;
-
- if (!buildSuccess) {
- this._analyzeBuildFailure(t, architecture, component, componentSrc,
- currentVcsVersion, previousVcsVersion, cancellable);
- throw new Error("Build failure in component " + buildname + " : " + msg);
- }
+
+ let proc = new GSystem.Subprocess({ context: context });
+ proc.init(cancellable);
+ print("Started child process " + context.argv.map(GLib.shell_quote).join(' '));
+ proc.wait_sync_check(cancellable);
let recordedMetaPath = componentResultdir.get_child('_ostbuild-meta.json');
JsonUtil.writeJsonFileAtomic(recordedMetaPath, expandedComponent, cancellable);
@@ -472,7 +453,7 @@ const Build = new Lang.Class({
if (statoverridePath != null)
GSystem.file_unlink(statoverridePath, cancellable);
- GSystem.shutil_rm_rf(tmpdir, cancellable);
+ GSystem.shutil_rm_rf(buildWorkdir, cancellable);
let ostreeRevision = this._saveComponentBuild(buildname, expandedComponent, cancellable);
@@ -485,8 +466,7 @@ const Build = new Lang.Class({
let runtimeName = 'bases/' + base['runtime'];
let develName = 'bases/' + base['devel'];
- let rootdir = this.workdir.get_child('roots');
- let composeRootdir = rootdir.get_child(target['name']);
+ let composeRootdir = this.subworkdir.get_child(target['name']);
GSystem.shutil_rm_rf(composeRootdir, cancellable);
GSystem.file_ensure_directory(composeRootdir, true, cancellable);
@@ -623,8 +603,7 @@ const Build = new Lang.Class({
Lang.copyProperties(BuildUtil.BUILD_ENV, env);
env['DL_DIR'] = downloads.get_path();
env['SSTATE_DIR'] = sstateDir.get_path();
- ProcUtil.runSync(cmd, cancellable, { logInitiation: true,
- env:ProcUtil.objectToEnvironment(env) });
+ ProcUtil.runSync(cmd, cancellable, {env:ProcUtil.objectToEnvironment(env)});
let componentTypes = ['runtime', 'devel'];
for (let i = 0; i < componentTypes.length; i++) {
@@ -642,26 +621,30 @@ const Build = new Lang.Class({
builtRevisionPath.replace_contents(basemeta['revision'], null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
},
- _init: function() {
- this.parent();
- this.parser.addArgument('--prefix');
- this.parser.addArgument('--snapshot');
- this.parser.addArgument('--patches-path');
+ execute: function(cancellable) {
+ let prefix = this.vars['prefix'];
+
+ this.subworkdir = Gio.File.new_for_path('.');
this.forceBuildComponents = {};
this.cachedPatchdirRevision = null;
- },
-
- execute: function(args, loop, cancellable) {
- this._initSnapshot(args.prefix, args.snapshot, cancellable);
- this.args = args;
+
+ this.prefix = prefix;
+ this.patchdir = this.workdir.get_child('patches');
+ let snapshotDir = this.workdir.get_child('snapshots');
+ let srcdb = new JsonDB.JsonDB(snapshotDir.get_child(prefix));
+ let snapshotPath = srcdb.getLatestPath();
+ let workingSnapshotPath = this.subworkdir.get_child(snapshotPath.get_basename());
+ GSystem.file_linkcopy(snapshotPath, workingSnapshotPath, Gio.FileCopyFlags.OVERWRITE,
+ cancellable);
+ let data = srcdb.loadFromPath(workingSnapshotPath, cancellable);
+ this._snapshot = new Snapshot.Snapshot(data, workingSnapshotPath);
let components = this._snapshot.data['components'];
- let buildresultDir = this.workdir.get_child('builds').get_child(this.prefix);
- let builddb = new JsonDB.JsonDB(buildresultDir);
+ let builddb = this._getResultDb('build/' + this.prefix);
- let targetSourceVersion = builddb.parseVersionStr(this._snapshot.path.get_basename())
+ let targetSourceVersion = builddb.parseVersionStr(this._snapshot.path.get_basename());
let haveLocalComponent = false;
for (let i = 0; i < components.length; i++) {
diff --git a/src/ostbuild/js/builtins/build_disks.js b/src/ostbuild/js/tasks/task-builddisks.js
similarity index 55%
rename from src/ostbuild/js/builtins/build_disks.js
rename to src/ostbuild/js/tasks/task-builddisks.js
index dccafeb..70170cf 100644
--- a/src/ostbuild/js/builtins/build_disks.js
+++ b/src/ostbuild/js/tasks/task-builddisks.js
@@ -25,6 +25,7 @@ const GSystem = imports.gi.GSystem;
const Builtin = imports.builtin;
const ArgParse = imports.argparse;
+const Task = imports.task;
const ProcUtil = imports.procutil;
const LibQA = imports.libqa;
const JsonDB = imports.jsondb;
@@ -32,29 +33,38 @@ const Config = imports.config;
const JsonUtil = imports.jsonutil;
const GuestFish = imports.guestfish;
-const loop = GLib.MainLoop.new(null, true);
+const TaskBuildDisks = new Lang.Class({
+ Name: 'TaskBuildDisks',
+ Extends: Task.TaskDef,
-const BuildDisks = new Lang.Class({
- Name: 'BuildDisks',
- Extends: Builtin.Builtin,
+ TaskPattern: [/builddisks\/(.*?)$/, 'prefix'],
- DESCRIPTION: "Generate disk images",
+ execute: function(cancellable) {
+ this.prefix = this.vars['prefix'];
- execute: function(args, loop, cancellable) {
- this._initPrefix(null);
+ this.subworkdir = Gio.File.new_for_path('.');
this.imageDir = this.workdir.get_child('images').get_child(this.prefix);
this.currentImageLink = this.imageDir.get_child('current');
this.previousImageLink = this.imageDir.get_child('previous');
GSystem.file_ensure_directory(this.imageDir, true, cancellable);
- let buildresultDir = this.workdir.get_child('builds').get_child(this.prefix);
- let builddb = new JsonDB.JsonDB(buildresultDir);
+ let builddb = this._getResultDb('build/' + this.prefix);
let latestPath = builddb.getLatestPath();
let buildVersion = builddb.parseVersionStr(latestPath.get_basename());
this._buildData = builddb.loadFromPath(latestPath, cancellable);
+ let targetImageDir = this.imageDir.get_child(buildVersion);
+
+ if (targetImageDir.query_exists(null)) {
+ print("Already created " + targetImageDir.get_path());
+ return;
+ }
+
+ let newImageDir = this.subworkdir.get_child('images');
+ GSystem.file_ensure_directory(newImageDir, true, cancellable);
+
let targets = this._buildData['targets'];
// Special case the default target - we do a pull, then clone
@@ -63,43 +73,54 @@ const BuildDisks = new Lang.Class({
// copying data via libguestfs repeatedly.
let defaultTarget = this._buildData['snapshot']['default-target'];
let defaultRevision = this._buildData['targets'][defaultTarget];
- this._defaultDiskPath = this._diskPathForTarget(defaultTarget, false);
+ let defaultTargetDiskName = this._diskNameForTarget(defaultTarget);
+
+ let currentDefaultDiskPath = this.currentImageLink.get_child(defaultTargetDiskName);
- let tmppath = this._defaultDiskPath.get_parent().get_child(this._defaultDiskPath.get_basename() + '.tmp');
- GSystem.shutil_rm_rf(tmppath, cancellable);
+ let tmpDefaultDiskPath = newImageDir.get_child(defaultTargetDiskName);
+ GSystem.shutil_rm_rf(tmpDefaultDiskPath, cancellable);
- if (!this._defaultDiskPath.query_exists(null)) {
- LibQA.createDisk(tmppath, cancellable);
+ if (!currentDefaultDiskPath.query_exists(null)) {
+ LibQA.createDisk(tmpDefaultDiskPath, cancellable);
} else {
- LibQA.copyDisk(this._defaultDiskPath, tmppath, cancellable);
+ LibQA.copyDisk(currentDefaultDiskPath, tmpDefaultDiskPath, cancellable);
}
let osname = this._buildData['snapshot']['osname'];
- ProcUtil.runSync(['ostbuild', 'qa-pull-deploy', tmppath.get_path(),
+ ProcUtil.runSync(['ostbuild', 'qa-pull-deploy', tmpDefaultDiskPath.get_path(),
this.repo.get_path(), osname, defaultTarget, defaultRevision],
cancellable, { logInitiation: true });
- GSystem.file_rename(tmppath, this._defaultDiskPath, cancellable);
-
for (let targetName in targets) {
if (targetName == defaultTarget)
continue;
let targetRevision = this._buildData['targets'][targetName];
- let diskPath = this._diskPathForTarget(targetName, true);
- tmppath = diskPath.get_parent().get_child(diskPath.get_basename() + '.tmp');
+ let diskName = this._diskNameForTarget(targetName, true);
+ let tmppath = newImageDir.get_child(diskName);
GSystem.shutil_rm_rf(tmppath, cancellable);
- LibQA.createDiskSnapshot(this._defaultDiskPath, tmppath, cancellable);
+ LibQA.createDiskSnapshot(tmpDefaultDiskPath, tmppath, cancellable);
ProcUtil.runSync(['ostbuild', 'qa-pull-deploy', tmppath.get_path(),
this.repo.get_path(), osname, targetName, targetRevision],
cancellable, { logInitiation: true });
}
- GSystem.file_linkcopy(latestPath, imageDir.get_child(latestPath.get_basename()),
- Gio.FileCopyFlags.OVERWRITE, cancellable);
+ GSystem.file_rename(newImageDir, this.imageDir.get_child(newImageDir.get_basename()),
+ cancellable);
+
+ let tmpLinkPath = Gio.File.new_for_path(this.imageDir, 'current-new.tmp');
+ GSystem.shutil_rm_rf(tmpLinkPath, cancellable);
+ tmpLinkPath.make_symbolic_link(newImageDir.get_basename(), cancellable);
+ let currentInfo = currentImageLink.query_info('standard::symlink-target', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable);
+ let newPreviousTmppath = this.imageDir.get_child('previous-new.tmp');
+ GSystem.shutil_rm_rf(newPreviousTmppath, cancellable);
+ let currentLinkTarget = currentInfo.get_symlink_target();
+ newPreviousTmppath.make_symbolic_link(currentLinkTarget, cancellable);
+ GSystem.file_rename(newPreviousTmppath, previousImageLink);
+ GSystem.file_rename(tmpLinkPath, currentImageLink);
},
- _diskPathForTarget: function(targetName, isSnap) {
+ _diskNameForTarget: function(targetName, isSnap) {
let squashedName = targetName.replace(/\//g, '_');
let suffix;
if (isSnap) {
@@ -107,6 +128,6 @@ const BuildDisks = new Lang.Class({
} else {
suffix = '-disk.qcow2';
}
- return this.imageDir.get_child(this.prefix + '-' + squashedName + suffix);
+ return this.prefix + '-' + squashedName + suffix;
}
});
diff --git a/src/ostbuild/js/tasks/task-resolve.js b/src/ostbuild/js/tasks/task-resolve.js
new file mode 100644
index 0000000..1080b8a
--- /dev/null
+++ b/src/ostbuild/js/tasks/task-resolve.js
@@ -0,0 +1,84 @@
+// 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 GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const JsonDB = imports.jsondb;
+const Builtin = imports.builtin;
+const Task = imports.task;
+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 TaskResolve = new Lang.Class({
+ Name: "TaskResolve",
+ Extends: Task.TaskDef,
+
+ TaskPattern: [/resolve\/(.*?)$/, 'prefix'],
+
+ DefaultParameters: {fetchAll: false,
+ fetchComponents: [],
+ timeoutSec: 10},
+
+ execute: function(cancellable) {
+ this.prefix = this.vars['prefix'];
+ let manifest = this.config.getGlobal('manifest');
+ let manifestPath = Gio.File.new_for_path(manifest);
+ let data = JsonUtil.loadJson(manifestPath, cancellable);
+ this._snapshot = new Snapshot.Snapshot(data, manifestPath, { prepareResolve: true });
+
+ let gitMirrorArgs = ['ostbuild', 'git-mirror', '--timeout-sec=' + this.parameters.timeoutSec,
+ '--manifest=' + manifest];
+ if (this.parameters.fetchAll || this.parameters.fetchComponents.length > 0) {
+ gitMirrorArgs.push('--fetch');
+ gitMirrorArgs.push('-k');
+ gitMirrorArgs.push.apply(gitMirrorArgs, this.parameters.fetchComponents);
+ }
+ ProcUtil.runSync(gitMirrorArgs, cancellable, { logInitiation: true });
+
+ let componentNames = this._snapshot.getAllComponentNames();
+ for (let i = 0; i < componentNames.length; i++) {
+ let component = this._snapshot.getComponent(componentNames[i]);
+ let src = component['src'];
+ let [keytype, uri] = Vcs.parseSrcKey(src);
+ let branch = component['branch'];
+ let tag = component['tag'];
+ let branchOrTag = branch || tag;
+ let mirrordir = Vcs.ensureVcsMirror(this.mirrordir, keytype, uri, branchOrTag, cancellable);
+ let revision = Vcs.describeVersion(mirrordir, branchOrTag);
+ component['revision'] = revision;
+ }
+
+ let snapshotdir = this.workdir.get_child('snapshots');
+ this._src_db = new JsonDB.JsonDB(snapshotdir.get_child(this.prefix));
+ let [path, modified] = this._src_db.store(this._snapshot.data, cancellable);
+ if (modified) {
+ print("New source snapshot: " + path.get_path());
+ } else {
+ print("Source snapshot unchanged: " + path.get_path());
+ }
+ }
+});
diff --git a/src/ostbuild/js/vcs.js b/src/ostbuild/js/vcs.js
index e2f82ad..3d324c2 100644
--- a/src/ostbuild/js/vcs.js
+++ b/src/ostbuild/js/vcs.js
@@ -173,22 +173,40 @@ function _listSubmodules(mirrordir, mirror, keytype, uri, branch, cancellable) {
function ensureVcsMirror(mirrordir, keytype, uri, branch, cancellable,
params) {
- params = Params.parse(params, {fetch: false,
- fetchKeepGoing: false});
+ params = Params.parse(params, { fetch: false,
+ fetchKeepGoing: false,
+ timeoutSec: 0 });
+ let fetch = params.fetch;
let mirror = getMirrordir(mirrordir, keytype, uri);
let tmpMirror = mirror.get_parent().get_child(mirror.get_basename() + '.tmp');
let didUpdate = false;
let lastFetchPath = getLastfetchPath(mirrordir, keytype, uri, branch);
let lastFetchContents = null;
- if (lastFetchPath.query_exists(cancellable)) {
+ let currentTime = GLib.DateTime.new_now_utc();
+ let lastFetchContents = null;
+ let lastFetchInfo = null;
+ try {
+ lastFetchInfo = lastFetchPath.query_info('time::modified', Gio.FileQueryInfoFlags.NONE, cancellable);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
+ throw e;
+ }
+ if (lastFetchInfo != null) {
lastFetchContents = GSystem.file_load_contents_utf8(lastFetchPath, cancellable).replace(/[ \n]/g, '');
+ if (params.timeoutSec > 0) {
+ let lastFetchTime = GLib.DateTime.new_from_unix_local(lastFetchInfo.get_attribute_uint64('time::modified'));
+ let diff = currentTime.difference(lastFetchTime) / 1000 / 1000;
+ if (diff < params.timeoutSec) {
+ fetch = false;
+ }
+ }
}
GSystem.shutil_rm_rf(tmpMirror, cancellable);
if (!mirror.query_exists(cancellable)) {
ProcUtil.runSync(['git', 'clone', '--mirror', uri, tmpMirror.get_path()], cancellable);
ProcUtil.runSync(['git', 'config', 'gc.auto', '0'], cancellable, {cwd: tmpMirror});
GSystem.file_rename(tmpMirror, mirror, cancellable);
- } else if (params.fetch) {
+ } else if (fetch) {
try {
ProcUtil.runSync(['git', 'fetch'], cancellable, {cwd:mirror});
} catch (e) {
@@ -210,17 +228,24 @@ function ensureVcsMirror(mirrordir, keytype, uri, branch, cancellable,
});
}
- if (changed) {
+ if (changed || (fetch && params.timeoutSec > 0)) {
lastFetchPath.replace_contents(currentVcsVersion, null, false, 0, cancellable);
}
return mirror;
}
+function uncacheRepository(mirrordir, keytype, uri, branch, cancellable) {
+ let lastFetchPath = getLastfetchPath(mirrordir, keytype, uri, branch);
+ GSystem.shutil_rm_rf(lastFetchPath, cancellable);
+}
+
function fetch(mirrordir, keytype, uri, branch, cancellable, params) {
- params = Params.parse(params, {keepGoing: false});
+ params = Params.parse(params, {keepGoing: false, timeoutSec: 0});
ensureVcsMirror(mirrordir, keytype, uri, branch, cancellable,
- {fetch:true, fetchKeepGoing: params.keepGoing});
+ { fetch:true,
+ fetchKeepGoing: params.keepGoing,
+ timeoutSec: params.timeoutSec });
}
function describeVersion(dirpath, branch) {
diff --git a/src/ostbuild/ostbuild.in b/src/ostbuild/ostbuild.in
index 01d75bc..258d9d0 100755
--- a/src/ostbuild/ostbuild.in
+++ b/src/ostbuild/ostbuild.in
@@ -25,4 +25,4 @@ export GIO_USE_VFS=local
export OSTBUILD_DATADIR= pkgdatadir@
export OSTBUILD_LIBDIR= pkglibdir@
-exec gjs -I "${jsdir}" "${jsdir}/main.js" "$@"
+exec $OSTBUILD_GDB gjs -I "${jsdir}" "${jsdir}/main.js" "$@"
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]