Beispiel #1
0
def resolve_document_using_git(git, docref):
    document = docref_to_dict(docref)
    kind = docref.kind
    id = docref.id
    get = lambda f, n=docref.name: call(
        git + ["show", "--quiet", "--format=format:%s" % (f,), n],
        do_check=False)
    get = lambda a: git_show(git, docref.name, a)
    if kind == "branches":
        branches = [trim(a, prefix="  remotes/origin/")
                    for a in read_lines(call(git + ["branch", "-a"]))]
        document["branches"] = []
        for branch in branches:
            document["branches"].append(docref_to_dict(BranchDocref(branch)))
    elif kind == "branch":
        sha = get1(
            read_lines(
                call(git + ["rev-parse", "remotes/origin/" + docref.name])))
        document["commit"] = docref_to_dict(ShaDocRef("commit", sha))
    elif kind == "commit":
        document.update(
            {"author": {"name": get("%an"),
                        "email": get("%ae"),
                        "date": get("%ai")},
             "committer": {"name": get("%cn"),
                           "email": get("%ce"),
                           "date": get("%ci")},
             "message": get("%B"),
             "tree": docref_to_dict(ShaDocRef("tree", get("%T"))),
             "parents": [],
             })
        for p in sorted(get("%P").split(" ")):
            if p == "":
                continue
            document["parents"].append(
                docref_to_dict(ShaDocRef("commit", p)))
    elif kind == "tree":
        document["children"] = []
        for line in read_lines(call(git + ["ls-tree", docref.name])):
            child_mode, child_kind, rest = line.split(" ", 2)
            child_sha, child_basename = rest.split("\t", 1)
            ref = {"child": docref_to_dict(ShaDocRef(child_kind, child_sha)),
                   "basename": child_basename,
                   "mode": octal_to_symbolic_mode(child_mode)}
            document["children"].append(ref)
        document["children"].sort(key=lambda a: a["child"]["sha"])
    elif kind == "blob":
        blob = call(git + ["show", docref.name], do_crlf_fix=False)
        if is_text(blob):
            document["encoding"] = "raw"
            document["raw"] = blob
        else:
            document["encoding"] = "base64"
            document["base64"] = base64.b64encode(blob)
    else:
        raise NotImplementedError(kind)
    return document
Beispiel #2
0
def force_couchdb_put_with_rev(couchdb_url, *documents):
    for document in documents:
        doc_url = posixpath.join(couchdb_url, document["_id"]).encode("ascii")
        i = 0
        while True:
            if i % 1000 == 0 and i != 0:
                print i, "The race is on!"
            old_doc = get(doc_url)
            if (old_doc.get("error") == "not_found" 
                and old_doc.get("reason") == "missing"):
                result = put(doc_url, document)
                if result.get("error") is None:
                    break
            else:
                assert old_doc.get("error") is None, old_doc
                rev = old_doc.pop("_rev")
                if document == old_doc:
                    break
                else:
                    d2 = dict(document)
                    d2["_rev"] = rev
                    result = put(doc_url, d2)
                    if result.get("error") is None:
                        break
            i += 1                
Beispiel #3
0
def blob_to_fs(git_couchdb_url, file_path, blob):
    blob_data = get(posixpath.join(git_couchdb_url, blob))
    if blob_data["encoding"] == "raw":
        write_file(file_path, blob_data["raw"])
    elif blob_data["encoding"] == "base64":
        write_file(file_path, base64.b64decode(blob_data["base64"]))
    else:
        raise NotImplementedError(blob_data)
Beispiel #4
0
def fetch_all(resolve_document, couchdb_url, seeds):
    to_fetch = list(seeds)
    push = lambda x: to_fetch.append(x)
    pop = lambda: to_fetch.pop()
    def priority_sort_key(docref):
        priority_items = ["commit", "tree"]
        i = dict((a, idx) for (idx, a) in enumerate(priority_items)).get(
            docref.kind, len(priority_items))
        return (i, docref.name, docref)
    def multipush(many, limit=None):
        for i, item in enumerate(reversed(
                sorted(many, key=priority_sort_key))):
            if limit is not None and i > limit:
                break
            push(item)
    multipush(seeds)
    mutable_buffer = {}
    local_buffer = {}
    fetched = set()
    for match in get(couchdb_url + "/_all_docs")["rows"]:
        if not match["id"].startswith("git-"):
            continue
        docref = id_to_docref(match["id"])
        if docref.kind not in MUTABLE_TYPES:
            fetched.add(docref)
    while len(to_fetch) > 0:
        docref = pop()
        if docref not in fetched:
            document = local_buffer.get(docref)
            if document is None:
                print "get", len(to_fetch), docref
                document = resolve_document(docref)
                local_buffer[docref] = document
                if docref.kind in ("branches", "branch"):
                    mutable_buffer[docref] = document
            local_dependencies = set(find_dependencies(document)) - fetched
            if len(local_dependencies) == 0:
                force_couchdb_put(couchdb_url, document)
                del local_buffer[docref]
                fetched.add(docref)
                print "put", len(to_fetch), docref
            else:
                push(docref)
                multipush(local_dependencies)
        if len(local_buffer) > BIG_NUMBER:
            local_buffer.clear()
            local_buffer.update(mutable_buffer)
        assert BIG_NUMBER > 15
        if len(to_fetch) > BIG_NUMBER:
            to_keep = to_fetch[:-SMALL_NUMBER]
            to_fetch[:] =  []
            multipush(seeds)
            multipush(to_keep)
            if len(to_fetch) > BIG_NUMBER:
                print "ouch, lots of seeds?"
            assert len(to_fetch) > 0
Beispiel #5
0
def sync_batch(git_couchdb_url, design_couchdb_url, branch, app_subdir):
    if branch is None:
        branches = [
            b["_id"] for b in get(
                posixpath.join(git_couchdb_url, "git-branches"))["branches"]]
    else:
        # TODO: Should really look for branches with this name using
        # an index
        branches = ["git-branch-" + branch]
    with mkdtemp() as temp_dir:
        for branch in branches:
            basename = urllib.quote(branch, safe="")
            local_dir = os.path.join(temp_dir, basename)
            commit = get(posixpath.join(git_couchdb_url, 
                                        branch))["commit"]["_id"]
            tree = get(posixpath.join(git_couchdb_url, 
                                      commit))["tree"]["_id"]
            if app_subdir != ".":
                # TODO: Support multi-level sub_dir
                assert "/" not in app_subdir, app_subdir
                for child in get(posixpath.join(git_couchdb_url, 
                                                tree))["children"]:
                    if child["basename"] == app_subdir:
                        assert child["child"]["type"] == "git-tree", child
                        tree = child["child"]["_id"]
                        break
                else:
                    raise Exception("Missing child %r" % (app_subdir,))
            doc_url = posixpath.join(design_couchdb_url, 
                                     "_design", url_quote(basename))
            existing = get(doc_url)
            if existing.get("couchapp_git_tree_id") == tree:
                continue
            print "Updating %r..." % (branch,)
            tree_to_fs(git_couchdb_url, local_dir, tree)
            write_file(os.path.join(local_dir, "couchapp_git_tree_id"),
                       tree)
            if os.path.exists(os.path.join(local_dir, "_id")):
                os.unlink(os.path.join(local_dir, "_id"))
            couchapp(doc_url, local_dir)
            print "...done %r" % (branch,)            
Beispiel #6
0
def tree_to_fs(git_couchdb_url, local_dir, tree):
    os.mkdir(local_dir)
    tree_data = get(posixpath.join(git_couchdb_url, tree))
    for entry in tree_data["children"]:
        out_path = os.path.join(local_dir, entry["basename"])
        if entry["child"]["type"] == "git-tree":
            tree_to_fs(git_couchdb_url, out_path, entry["child"]["_id"])
        elif entry["child"]["type"] == "git-blob":
            blob_to_fs(git_couchdb_url, out_path, entry["child"]["_id"])
        else:
            raise NotImplementedError(entry)
        mode = symbolic_to_octal_mode(entry["mode"])
    def test(self):
        # Clean up any partially aborted test runs
        def mark_aborted(doc):
            doc.update({"status": "aborted"})
        queue_url = posixpath.join(self.couchdb_url, "test-queue")
        queued_ids = set(i["_id"] for i in get(queue_url)["queue"])
        pending_map_func = """\
function (doc) {
  var prefix = "test-run-";
  if (doc._id.substr(0, prefix.length) == prefix) {
    if (doc.status == "pending") {
      emit(null, doc._id);
    }
  }
}
"""
        git_map_func = """\
function (doc) {
  var prefix  = "test-";
  if (doc._id.substr(0, prefix.length) != prefix) {
    emit(null, doc._id);
  }
}
"""
        design_doc = {
            "language": "javascript",
            "views": {
                "pending-tests": {
                    "map": pending_map_func,
                    }, 
                "git-documents": {
                    "map": git_map_func,
                    },
                },
            }
        put_update(posixpath.join(self.couchdb_url, "_design/test"),
                   lambda a=None: design_doc)
        result = get(posixpath.join(self.couchdb_url, 
                                    "_design/test/_view/pending-tests"))
        pending_ids = set(i["id"] for i in result["rows"])
        for missing_id in pending_ids - queued_ids:
            put_update(posixpath.join(self.couchdb_url, url_quote(missing_id)),
                       mark_aborted)
        # Add this test run to the queue on the shared database
        def now():
            return datetime.datetime.utcnow().isoformat() + "Z"
        document = {
            "request_time": now(),
            "start_time": "",
            "end_time": "",
            "status": "pending",
            }
        result = post_new(self.couchdb_url, document, 
                          id_template="test-run-%s")
        my_url = posixpath.join(self.couchdb_url, url_quote(result["id"]))
        def append_to_queue(queued):
            queued.setdefault("queue", []).append(
                {"_id": result["id"],
                 "request_time": document["request_time"],
                 })
        def remove_from_queue(queued):
            queue = queued.get("queue", [])
            queue = [e for e in queue if e.get("_id") != result["id"]]
            queued["queue"] = queue
        put_update(queue_url, append_to_queue)
        try:
            while True:
                queue = get(queue_url)
                for i, entry in enumerate(queue.get("queue", [])):
                    if entry["_id"] == result["id"]:
                        my_index = i
                        break
                else:
                    raise Exception("I seem to have been un queued")
        # Is there a non-expired entry in the queue ahead of mine?
                if my_index == 0:
                    def mark_running(job):
                        job.update(
                            {"status": "running",
                             "start_time": now()})
                    status = "aborted"
                    def mark_stopped(job):
                        job.update(
                            {"status": status,
                             "stop_time": now()})
        # Mark the test as running 
                    put_update(my_url, mark_running)
                    try:
                        try:
                            self._run_test()
                        except AssertionError:
                            status = "failed"
                            raise
                        except Exception:
                            status = "error"
                            raise
                        else:
                            status = "passed"
                            return
                    finally:
                        put_update(my_url, mark_stopped)
                else:
        # Set a timout for the next time to check, if necessary
                    print "sleeping..."
                    time.sleep(5)
        finally:
        # Mark the test as completed
            put_update(queue_url, remove_from_queue)
    def _run_test(self):
        # Wipe out all git-related documents and the design document
        result = get(posixpath.join(self.couchdb_url, 
                                    "_design/test/_view/git-documents"))
        for item in result["rows"]:
            delete(posixpath.join(self.couchdb_url, url_quote(item["id"])))
        # Upload the gitbrowser to the couchdb design document
        gitbrowser_source_path = os.path.join(self.source_path, "gitbrowser")
        couchapp(posixpath.join(self.couchdb_url, "_design/gitbrowser"), 
                 gitbrowser_source_path)
        # Populate a test GIT repository
        with mkdtemp() as temp_dir:
            cwd_script = ["bash", "-c", 'cd "$1" && shift && exec "$@"', "-", 
                          temp_dir]
            subprocess.check_call(cwd_script + ["git", "init"])
        ## write file README
            git = cwd_script + ["env", 
                                "HOME=%s" % (temp_dir,),
                                "git"]
            subprocess.check_call(git + ["config", "user.name", "Tester"])
            subprocess.check_call(git + ["config", "user.email", 
                                         "*****@*****.**"])
            path = os.path.join(temp_dir, "README")
            with open(path, "wb") as fh:
                fh.write("Initial version of text file\r\n")
            subprocess.check_call(git + ["add", path])
            subprocess.check_call(git + ["commit", "-m", 
                                         "Initial commit"])
        ## write file README
            with open(path, "wb") as fh:
                fh.write("""\
# Purpose

This is a minimal git repository for testing

# Example Markdown

Readme files like this are Markdown formatted:

 * Bullet points
 * Headings
 * Code blocks
 * _etc_

They can have hyperlinks:

 * to other hosts like [google](http://www.google.com/)
 * absolute paths, like the [hexdump file](/show/master/head/binary-file)
 * relative paths, like the [child folder](subfolder)

""")
            subprocess.check_call(git + ["add", path])
        ## write file binary 
            path = os.path.join(temp_dir, "binary-file")
            with open(path, "wb") as fh:
                fh.write("".join(chr(i) for i in range(2**8)))
            subprocess.check_call(git + ["add", path])
        ## write python file 
            path = os.path.join(temp_dir, "example.py")
            with open(path, "wb") as fh:
                fh.write("""\
## Example\r\n\
#This is an example python file\r\n\
#\r\n\
#  - Example bullet list\r\n\
#  - _etc_\r\n\
#\r\n\
#Because of the blank line, this documentation block should not\r\n\
#have any code on the right and the following code block should not\r\n\
#have any documentation to its left.\r\n\
\r\n\
from __future__ import division\r\n\
import os, sys, unittest\r\n\
\r\n\
class SomeTest(unittest.TestCase):\r\n\
\r\n\
  def testSomething(self):\r\n\
    '''Just an example test'''\r\n\
    self.fail("Just an example")\r\n\
\r\n\
## Main entry point\r\n\
#Some normal *Markdown* paragraph text, the example \r\n\
#heading should line up with the if statement.\r\n\
if __name__ == "__main__":\r\n\
  # This comment, starting after column 0, is a normal comment shown\r\n\
  # inline with the code instead of one that is shown in the left panel.\r\n\
  unittest.main()\r\n\
""")
            subprocess.check_call(git + ["add", path])
        ## write file .gitbrowser-project.json
            path = os.path.join(temp_dir, ".gitbrowser-project.json")
            with open(path, "wb") as fh:
                fh.write('{"title": "Example Project"}')
            subprocess.check_call(git + ["add", path])
        ## write file subfolder/README 
            path = os.path.join(temp_dir, "subfolder", "README")
            os.makedirs(os.path.dirname(path))
            with open(path, "wb") as fh:
                fh.write("A file in a subfolder with a [parent link](..)")
            subprocess.check_call(git + ["add", path])
            subprocess.check_call(git + ["commit", "-m", "Update"])
        # Copy the git repository to the couchdb
            sync_script = os.path.join(self.source_path, "gitcouchdbsync.py")
            subprocess.check_call(cwd_script + ["python", sync_script,
                                                self.couchdb_url])
        # Run selenium testing against the public URL
        print "Uploaded. Running tests..."
        self._go_to_the_selenium_stage()
Beispiel #9
0
def resolve_document_using_git(git, docref):
    document = docref_to_dict(docref)
    kind = docref.kind
    id = docref.id
    get = lambda a: git_show(git, docref.name, a)
    if kind == "branches":
        branches = set()
        for line in read_lines(call(git + ["branch", "-a"])):
            if line.startswith("  "):
                line = trim(line, prefix="  ")
            elif line.startswith("* "):
                line = trim(line, prefix="* ")
                if line == "(no branch)":
                    continue
            else:
                raise NotImplementedError(line)
            if " -> " in line:
                ba, bb = line.split(" -> ", 1)
                branches.add(posixpath.basename(ba))
                branches.add(posixpath.basename(bb))
            else:
                branches.add(posixpath.basename(line))
        branches = list(sorted(b for b in branches if b != "HEAD"))
        document["branches"] = []
        for branch in branches:
            document["branches"].append(docref_to_dict(BranchDocref(branch)))
    elif kind == "branch":
        sha = get1(
            read_lines(
                call(git + ["rev-parse", docref.name])))
        document["commit"] = docref_to_dict(ShaDocRef("commit", sha))
    elif kind == "commit":
        document.update(
            {"author": {"name": get("%an"),
                        "email": get("%ae"),
                        "date": get("%ai")},
             "committer": {"name": get("%cn"),
                           "email": get("%ce"),
                           "date": get("%ci")},
             "message": get("%B"),
             "tree": docref_to_dict(ShaDocRef("tree", get("%T"))),
             "parents": [],
             })
        for p in sorted(get("%P").split(" ")):
            if p == "":
                continue
            document["parents"].append(
                docref_to_dict(ShaDocRef("commit", p)))
    elif kind == "tree":
        document["children"] = []
        for line in read_lines(call(git + ["ls-tree", docref.name])):
            child_mode, child_kind, rest = line.split(" ", 2)
            child_sha, child_basename = rest.split("\t", 1)
            ref = {"child": docref_to_dict(ShaDocRef(child_kind, child_sha)),
                   "basename": child_basename,
                   "mode": octal_to_symbolic_mode(child_mode)}
            document["children"].append(ref)
        document["children"].sort(key=lambda a: a["child"]["sha"])
    elif kind == "blob":
        blob = call(git + ["show", docref.name], do_crlf_fix=False)
        if is_text(blob):
            document["encoding"] = "raw"
            document["raw"] = blob
        else:
            document["encoding"] = "base64"
            document["base64"] = base64.b64encode(blob)
    else:
        raise NotImplementedError(kind)
    return document