Mercurial optional
MODIFY
repo2docker/contentproviders/mercurial.py
repo2docker/contentproviders/mercurial.py
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."""
@@ -16,6 +47,8 @@ class Mercurial(ContentProvider):
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}
@@ -26,7 +59,14 @@ class Mercurial(ContentProvider):
# 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
@@ -35,9 +75,9 @@ class Mercurial(ContentProvider):
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

MODIFY
tests/unit/contentproviders/test_mercurial.py
tests/unit/contentproviders/test_mercurial.py
@@ -5,6 +5,29 @@ from tempfile import TemporaryDirectory
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):
@@ -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)."""
@@ -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
@@ -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
@@ -72,9 +105,10 @@ 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:
@@ -82,3 +116,30 @@ def test_bad_ref(hg_repo_with_content):
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

Mercurial optional