summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Coldrick <adam.coldrick@codethink.co.uk>2015-02-19 14:37:19 +0000
committerAdam Coldrick <adam.coldrick@codethink.co.uk>2015-04-10 13:52:25 +0000
commitfb179589b968494affb5ce92dd6944df6bfaa7c6 (patch)
treec3b1034492fd42752abc23c50efe488071964106
parent10bd8e077afd0e6e46d1e8dc11f8bb70a73c8cc8 (diff)
downloadmorph-fb179589b968494affb5ce92dd6944df6bfaa7c6.tar.gz
Add a class to wrap the OSTree API
-rw-r--r--morphlib/__init__.py1
-rw-r--r--morphlib/ostree.py139
-rw-r--r--without-test-modules1
3 files changed, 141 insertions, 0 deletions
diff --git a/morphlib/__init__.py b/morphlib/__init__.py
index 0c9284d8..e2641402 100644
--- a/morphlib/__init__.py
+++ b/morphlib/__init__.py
@@ -72,6 +72,7 @@ import morphologyfinder
import morphology
import morphloader
import morphset
+import ostree
import remoteartifactcache
import remoterepocache
import repoaliasresolver
diff --git a/morphlib/ostree.py b/morphlib/ostree.py
new file mode 100644
index 00000000..a2c133f2
--- /dev/null
+++ b/morphlib/ostree.py
@@ -0,0 +1,139 @@
+from gi.repository import OSTree
+from gi.repository import Gio
+from gi.repository import GLib
+
+import os
+
+
+class OSTreeRepo(object):
+
+ """Class to wrap the OSTree API."""
+
+ OSTREE_GIO_FAST_QUERYINFO = 'standard::name,standard::type,standard::' \
+ 'size,standard::is-symlink,standard::syml' \
+ 'ink-target,unix::device,unix::inode,unix' \
+ '::mode,unix::uid,unix::gid,unix::rdev'
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS = Gio.FileQueryInfoFlags(1)
+ cancellable = Gio.Cancellable.new()
+
+ def __init__(self, path, disable_fsync=True):
+ self.path = path
+ self.repo = self._open_repo(path, disable_fsync)
+
+ def _open_repo(self, path, disable_fsync=True):
+ """Create and open and OSTree.Repo, and return it."""
+ repo_dir = Gio.file_new_for_path(path)
+ repo = OSTree.Repo.new(repo_dir)
+ try:
+ repo.open(self.cancellable)
+ except GLib.GError:
+ if not os.path.exists(path):
+ os.makedirs(path)
+ repo.create(OSTree.RepoMode.ARCHIVE_Z2, self.cancellable)
+ repo.set_disable_fsync(disable_fsync)
+ return repo
+
+ def refsdir(self):
+ """Return the abspath to the refs/heads directory in the repo."""
+ return os.path.join(os.path.abspath(self.path), 'refs/heads')
+
+ def touch_ref(self, ref):
+ """Update the mtime of a ref file in repo/refs/heads."""
+ os.utime(os.path.join(self.refsdir(), ref), None)
+
+ def resolve_rev(self, branch, allow_noent=True):
+ """Return the SHA256 corresponding to 'branch'."""
+ return self.repo.resolve_rev(branch, allow_noent)[1]
+
+ def read_commit(self, branch):
+ """Return an OSTree.RepoFile representing a committed tree."""
+ return self.repo.read_commit(branch, self.cancellable)[1]
+
+ def query_info(self, file_object):
+ """Quickly return a Gio.FileInfo for file_object."""
+ return file_object.query_info(self.OSTREE_GIO_FAST_QUERYINFO,
+ self.G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ self.cancellable)
+
+ def checkout(self, branch, destdir):
+ """Checkout branch into destdir."""
+ checkout_path = destdir
+ if not os.path.exists(checkout_path):
+ os.makedirs(checkout_path)
+ checkout = Gio.file_new_for_path(checkout_path)
+
+ commit = self.read_commit(branch)
+ commit_info = self.query_info(commit)
+ self.repo.checkout_tree(0, 1, checkout, commit,
+ commit_info, self.cancellable)
+
+ def commit(self, subject, srcdir, branch):
+ """Commit the contents of 'srcdir' to 'branch'."""
+ self.repo.prepare_transaction(self.cancellable)
+ parent = self.resolve_rev(branch)
+ if parent:
+ parent_root = self.read_commit(parent)
+
+ mtree = OSTree.MutableTree()
+ src = Gio.file_new_for_path(srcdir)
+ self.repo.write_directory_to_mtree(src, mtree, None, self.cancellable)
+ root = self.repo.write_mtree(mtree, self.cancellable)[1]
+ if parent and root.equal(parent_root):
+ return
+ checksum = self.repo.write_commit(parent, subject, '', None,
+ root, self.cancellable)[1]
+ self.repo.transaction_set_ref(None, branch, checksum)
+ stats = self.repo.commit_transaction(self.cancellable)
+
+ def cat_file(self, ref, path):
+ """Return the file descriptor of path at ref."""
+ commit = self.read_commit(ref)
+ relative = commit.resolve_relative_path(path)
+ ret, content, etag = relative.load_contents()
+ return content
+
+ def list_refs(self, resolved=False):
+ """Return a list of all refs in the repo."""
+ refs = self.repo.list_refs()[1]
+ if not resolved:
+ return refs.keys()
+ return refs
+
+ def delete_ref(self, ref):
+ """Remove refspec from the repo."""
+ if not self.list_refs(ref):
+ raise Exception("Failed to delete ref, it doesn't exist")
+ self.repo.set_ref_immediate(None, ref, None, self.cancellable)
+
+ def prune(self):
+ """Remove unreachable objects from the repo."""
+ return self.repo.prune(OSTree.RepoPruneFlags.REFS_ONLY,
+ -1, self.cancellable)
+
+ def add_remote(self, name, url):
+ """Add a remote with a given name and url."""
+ options_type = GLib.VariantType.new('a{sv}')
+ options_builder = GLib.VariantBuilder.new(options_type)
+ options = options_builder.end()
+ self.repo.remote_add(name, url, options, self.cancellable)
+
+ def remove_remote(self, name):
+ """Remove a remote with a given name."""
+ self.repo.remote_delete(name, self.cancellable)
+
+ def get_remote_url(self, name):
+ """Return the URL for a remote."""
+ return self.repo.remote_get_url(name)[1]
+
+ def list_remotes(self):
+ """Return a list of all remotes for this repo."""
+ return self.repo.remote_list()
+
+ def has_remote(self, name):
+ """Return True if name is a remote for the repo."""
+ return name in self.list_remotes()
+
+ def pull(self, refs, remote):
+ """Pull ref from remote into the local repo."""
+ flags = OSTree.RepoPullFlags.NONE
+ self.repo.pull(remote, refs, flags, None, self.cancellable)
diff --git a/without-test-modules b/without-test-modules
index caf10c8b..c3e7f5a2 100644
--- a/without-test-modules
+++ b/without-test-modules
@@ -54,3 +54,4 @@ distbuild/timer_event_source.py
distbuild/worker_build_scheduler.py
# Not unit tested, since it needs a full system branch
morphlib/buildbranch.py
+morphlib/ostree.py