kopia lustrzana https://github.com/jupyterhub/repo2docker
86 wiersze
3.4 KiB
Python
86 wiersze
3.4 KiB
Python
import subprocess
|
|
import sys
|
|
|
|
from .base import ContentProvider, ContentProviderException
|
|
from ..utils import execute_cmd, check_ref
|
|
|
|
|
|
class Git(ContentProvider):
|
|
"""Provide contents of a remote git repository."""
|
|
|
|
def detect(self, source, ref=None, extra_args=None):
|
|
# Git is our content provider of last resort. This is to maintain the
|
|
# old behaviour when git and local directories were the only supported
|
|
# content providers. This means that this content provider will always
|
|
# match. The downside is that the call to `fetch()` later on might fail
|
|
return {"repo": source, "ref": ref}
|
|
|
|
def fetch(self, spec, output_dir, yield_output=False):
|
|
repo = spec["repo"]
|
|
ref = spec.get("ref", None)
|
|
|
|
# make a, possibly shallow, clone of the remote repository
|
|
try:
|
|
cmd = ["git", "clone"]
|
|
if ref is None:
|
|
# check out of HEAD is performed after the clone is complete
|
|
cmd.extend(["--depth", "1"])
|
|
else:
|
|
# don't check out HEAD, the given ref will be checked out later
|
|
# this prevents HEAD's submodules to be cloned if ref doesn't have them
|
|
cmd.extend(["--no-checkout"])
|
|
cmd.extend([repo, output_dir])
|
|
for line in execute_cmd(cmd, capture=yield_output):
|
|
yield line
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
msg = "Failed to clone repository from {repo}".format(repo=repo)
|
|
if ref is not None:
|
|
msg += " (ref {ref})".format(ref=ref)
|
|
msg += "."
|
|
raise ContentProviderException(msg) from e
|
|
|
|
# check out the specific ref given by the user
|
|
if ref is not None:
|
|
hash = check_ref(ref, output_dir)
|
|
if hash is None:
|
|
self.log.error(
|
|
"Failed to check out ref %s", ref, extra=dict(phase="failed")
|
|
)
|
|
if ref == "master":
|
|
msg = (
|
|
"Failed to check out the 'master' branch. "
|
|
"Maybe the default branch is not named 'master' "
|
|
"for this repository.\n\nTry not explicitly "
|
|
"specifying `--ref`."
|
|
)
|
|
else:
|
|
msg = "Failed to check out ref {}".format(ref)
|
|
raise ValueError(msg)
|
|
# We don't need to explicitly checkout things as the reset will
|
|
# take care of that. If the hash is resolved above, we should be
|
|
# able to reset to it
|
|
for line in execute_cmd(
|
|
["git", "reset", "--hard", hash], cwd=output_dir, capture=yield_output
|
|
):
|
|
yield line
|
|
|
|
# ensure that git submodules are initialised and updated
|
|
for line in execute_cmd(
|
|
["git", "submodule", "update", "--init", "--recursive"],
|
|
cwd=output_dir,
|
|
capture=yield_output,
|
|
):
|
|
yield line
|
|
|
|
cmd = ["git", "rev-parse", "HEAD"]
|
|
sha1 = subprocess.Popen(cmd, stdout=subprocess.PIPE, cwd=output_dir)
|
|
self._sha1 = sha1.stdout.read().decode().strip()
|
|
|
|
@property
|
|
def content_id(self):
|
|
"""A unique ID to represent the version of the content.
|
|
Uses the first seven characters of the git commit ID of the repository.
|
|
"""
|
|
return self._sha1[:7]
|