Skip to content

Commit

Permalink
Mercurial optional
Browse files Browse the repository at this point in the history
  • Loading branch information
paugier committed Sep 10, 2020
1 parent 9691454 commit 5bb5869
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 4 deletions.
46 changes: 43 additions & 3 deletions repo2docker/contentproviders/mercurial.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
import subprocess
import os
from distutils.util import strtobool

from .base import ContentProvider, ContentProviderException
from ..utils import execute_cmd

HG_EVOLVE_REQUIRED = strtobool(
os.environ.get("REPO2DOCKER_HG_EVOLVE_REQUIRED", "False")
)

if HG_EVOLVE_REQUIRED:
if "REPO2DOCKER_HG_REQUIRED" in os.environ:
HG_REQUIRED = strtobool(os.environ["REPO2DOCKER_HG_REQUIRED"])
if not HG_REQUIRED:
raise ValueError(
"Incompatible values for environment variables "
"REPO2DOCKER_HG_EVOLVE_REQUIRED=1 and REPO2DOCKER_HG_REQUIRED=0"
)
else:
HG_REQUIRED = True
else:
HG_REQUIRED = strtobool(os.environ.get("REPO2DOCKER_HG_REQUIRED", "False"))


def is_mercurial_available():
try:
subprocess.check_output(["hg", "version"])
except subprocess.CalledProcessError:
return False
return True


if HG_REQUIRED and not is_mercurial_available():
raise RuntimeError("REPO2DOCKER_HG_REQUIRED but the command `hg` is not available")


class Mercurial(ContentProvider):
"""Provide contents of a remote Mercurial repository."""
Expand All @@ -16,6 +47,8 @@ def detect(self, source, ref=None, extra_args=None):
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
# warning: if hg is not installed and `not HG_REQUIRED`,
# we return None even for a hg repo
return None

return {"repo": source, "ref": ref}
Expand All @@ -26,7 +59,14 @@ def fetch(self, spec, output_dir, yield_output=False):

# make a clone of the remote repository
try:
cmd = ["hg", "clone", repo, output_dir]
cmd = [
"hg",
"clone",
repo,
output_dir,
"--config",
"phases.publish=False",
]
if ref is not None:
# don't update so the clone will include an empty working
# directory, the given ref will be updated out later
Expand All @@ -35,9 +75,9 @@ def fetch(self, spec, output_dir, yield_output=False):
yield line

except subprocess.CalledProcessError as error:
msg = "Failed to clone repository from {repo}".format(repo=repo)
msg = f"Failed to clone repository from {repo}"
if ref is not None:
msg += " (ref {ref})".format(ref=ref)
msg += f" (ref {ref})"
msg += "."
raise ContentProviderException(msg) from error

Expand Down
63 changes: 62 additions & 1 deletion tests/unit/contentproviders/test_mercurial.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@
import pytest

from repo2docker.contentproviders import Mercurial
from repo2docker.contentproviders.mercurial import (
HG_REQUIRED,
HG_EVOLVE_REQUIRED,
is_mercurial_available,
)

skip_if_no_hg = pytest.mark.skipif(
not HG_REQUIRED and not is_mercurial_available(),
reason="not HG_REQUIRED and Mercurial not available",
)


def is_evolve_available():
if not is_mercurial_available():
return False
output = subprocess.getoutput("hg version -v")
return " evolve " in output


EVOLVE_AVAILABLE = is_evolve_available()

if HG_EVOLVE_REQUIRED and not EVOLVE_AVAILABLE:
raise RuntimeError("HG_EVOLVE_REQUIRED and not EVOLVE_AVAILABLE")


def _add_content_to_hg(repo_dir):
Expand All @@ -16,6 +39,14 @@ def _add_content_to_hg(repo_dir):
subprocess.check_call(["hg", "add", "test"], cwd=repo_dir)
subprocess.check_call(["hg", "commit", "-m", "Test commit"], cwd=repo_dir)

if EVOLVE_AVAILABLE:
subprocess.check_call(["hg", "topic", "test-topic"], cwd=repo_dir)
subprocess.check_call(
["hg", "commit", "-m", "Test commit in topic test-topic"],
cwd=repo_dir,
)
subprocess.check_call(["hg", "up", "default"], cwd=repo_dir)


def _get_node_id(repo_dir):
"""Get repository's current commit node ID (currently SHA1)."""
Expand Down Expand Up @@ -46,6 +77,7 @@ def hg_repo_with_content(hg_repo):
yield hg_repo, node_id


@skip_if_no_hg
def test_detect_mercurial(hg_repo_with_content, repo_with_content):
mercurial = Mercurial()
assert mercurial.detect("this-is-not-a-directory") is None
Expand All @@ -58,6 +90,7 @@ def test_detect_mercurial(hg_repo_with_content, repo_with_content):
assert mercurial.detect(hg_repo) == {"repo": hg_repo, "ref": None}


@skip_if_no_hg
def test_clone(hg_repo_with_content):
"""Test simple hg clone to a target dir"""
upstream, node_id = hg_repo_with_content
Expand All @@ -72,13 +105,41 @@ def test_clone(hg_repo_with_content):
assert mercurial.content_id == node_id


@skip_if_no_hg
def test_bad_ref(hg_repo_with_content):
"""
Test trying to checkout a ref that doesn't exist
Test trying to update to a ref that doesn't exist
"""
upstream, node_id = hg_repo_with_content
with TemporaryDirectory() as clone_dir:
spec = {"repo": upstream, "ref": "does-not-exist"}
with pytest.raises(ValueError):
for _ in Mercurial().fetch(spec, clone_dir):
pass


@pytest.mark.skipif(
not HG_EVOLVE_REQUIRED and not EVOLVE_AVAILABLE,
reason="not HG_EVOLVE_REQUIRED and hg-evolve not available",
)
@skip_if_no_hg
def test_ref_topic(hg_repo_with_content):
"""
Test trying to update to a topic
"""
upstream, node_id = hg_repo_with_content
node_id = subprocess.Popen(
["hg", "identify", "-i", "-r", "topic(test-topic)"],
stdout=subprocess.PIPE,
cwd=upstream,
)
node_id = node_id.stdout.read().decode().strip()

with TemporaryDirectory() as clone_dir:
spec = {"repo": upstream, "ref": "test-topic"}
mercurial = Mercurial()
for _ in mercurial.fetch(spec, clone_dir):
pass
assert (Path(clone_dir) / "test").exists()

assert mercurial.content_id == node_id

0 comments on commit 5bb5869

Please sign in to comment.