Skip to content

Commit cb95d2b

Browse files
committed
Add MercurialRepoProvider
1 parent 856589e commit cb95d2b

File tree

7 files changed

+80
-3
lines changed

7 files changed

+80
-3
lines changed

binderhub/app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from .repoproviders import (GitHubRepoProvider, GitRepoProvider,
3636
GitLabRepoProvider, GistRepoProvider,
3737
ZenodoProvider, FigshareProvider, HydroshareProvider,
38-
DataverseProvider)
38+
DataverseProvider, MercurialRepoProvider)
3939
from .metrics import MetricsHandler
4040

4141
from .utils import ByteSpecification, url_path_join
@@ -420,6 +420,7 @@ def _add_slash(self, proposal):
420420
'figshare': FigshareProvider,
421421
'hydroshare': HydroshareProvider,
422422
'dataverse': DataverseProvider,
423+
'hg': MercurialRepoProvider,
423424
},
424425
config=True,
425426
help="""

binderhub/event-schemas/launch.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"Zenodo",
1515
"Figshare",
1616
"Hydroshare",
17-
"Dataverse"
17+
"Dataverse",
18+
"Mercurial"
1819
],
1920
"description": "Provider for the repository being launched"
2021
},

binderhub/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"figshare": "Figshare",
2020
"hydroshare": "Hydroshare",
2121
"dataverse": "Dataverse",
22+
"hg": "Mercurial",
2223
}
2324

2425

binderhub/repoproviders.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,3 +899,46 @@ async def get_resolved_spec(self):
899899

900900
def get_build_slug(self):
901901
return self.gist_id
902+
903+
904+
class MercurialRepoProvider(GitRepoProvider):
905+
"""Bare bones Mercurial repo provider.
906+
907+
Users must provide a spec of the following form.
908+
909+
<url-escaped-namespace>/<unresolved_ref>
910+
<url-escaped-namespace>/<resolved_ref>
911+
912+
eg:
913+
https://foss.heptapod.net/pypy/pypy/release-pypy3.7-v7.3.2rc3
914+
https://foss.heptapod.net/graphics/plot-covid19-fr/8e29b5b0064d
915+
916+
"""
917+
918+
name = Unicode("Mercurial")
919+
920+
@gen.coroutine
921+
def get_resolved_ref(self):
922+
if hasattr(self, 'resolved_ref'):
923+
return self.resolved_ref
924+
925+
try:
926+
# Check if the reference is a valid SHA hash
927+
self.sha1_validate(self.unresolved_ref)
928+
except ValueError:
929+
# The ref is a head/tag and we resolve it using `hg identify`
930+
command = ["hg", "identify", self.repo, "-r", self.unresolved_ref, "-T", "{id}"]
931+
result = subprocess.run(command, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
932+
if result.returncode:
933+
raise RuntimeError("Unable to run hg identify to get the `resolved_ref`: {}".format(result.stderr))
934+
if not result.stdout:
935+
raise ValueError("The specified branch, tag or commit SHA ('{}') was not found on the remote repository."
936+
.format(self.unresolved_ref))
937+
resolved_ref = result.stdout.strip()
938+
self.sha1_validate(resolved_ref)
939+
self.resolved_ref = resolved_ref
940+
else:
941+
# The ref already was a valid SHA hash
942+
self.resolved_ref = self.unresolved_ref
943+
944+
return self.resolved_ref

binderhub/static/js/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ function updateRepoText() {
9999
$("#ref").prop("disabled", true);
100100
$("label[for=ref]").prop("disabled", true);
101101
}
102+
else if (provider === "hg") {
103+
text = "Arbitrary Mercurial repository URL (http://hg.example.com/repo)";
104+
tag_text = "Mercurial branch, tag, topic or commit SHA";
105+
}
102106
$("#repository").attr('placeholder', text);
103107
$("label[for=repository]").text(text);
104108
$("#ref").attr('placeholder', tag_text);

binderhub/templates/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ <h4 id="form-header" class='row'>Build and launch a repository</h4>
5656
<li class="dropdown-item" value="gist"><a href="#">Gist</a></li>
5757
<li class="dropdown-item" value="gl"><a href="#">GitLab.com</a></li>
5858
<li class="dropdown-item" value="git"><a href="#">Git repository</a></li>
59+
<li class="dropdown-item" value="hg"><a href="#">Mercurial repository</a></li>
5960
<li class="dropdown-item" value="zenodo"><a href="#">Zenodo DOI</a></li>
6061
<li class="dropdown-item" value="figshare"><a href="#">Figshare DOI</a></li>
6162
<li class="dropdown-item" value="hydroshare"><a href="#">Hydroshare resource</a></li>

binderhub/tests/test_repoproviders.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from binderhub.repoproviders import (
88
tokenize_spec, strip_suffix, GitHubRepoProvider, GitRepoProvider,
99
GitLabRepoProvider, GistRepoProvider, ZenodoProvider, FigshareProvider,
10-
HydroshareProvider, DataverseProvider
10+
HydroshareProvider, DataverseProvider, MercurialRepoProvider
1111
)
1212

1313

@@ -404,3 +404,29 @@ def test_gist_secret():
404404

405405
provider = GistRepoProvider(spec=spec, allow_secret_gist=True)
406406
assert IOLoop().run_sync(provider.get_resolved_ref) is not None
407+
408+
409+
@pytest.mark.parametrize('url,unresolved_ref,resolved_ref', [
410+
['https://foss.heptapod.net/graphics/plot-covid19-fr',
411+
'3f1209cb613a',
412+
'3f1209cb613a1286e555d8cabe722c1be2a6f123'],
413+
['https://foss.heptapod.net/pypy/pypy',
414+
'release-pypy3.7-v7.3.2rc3',
415+
'87875bf2dfd8fe682a49e010f6636a871b1308e6']
416+
])
417+
def test_mercurial_ref(url, unresolved_ref, resolved_ref):
418+
spec = '{}/{}'.format(
419+
quote(url, safe=''),
420+
quote(unresolved_ref)
421+
)
422+
provider = MercurialRepoProvider(spec=spec)
423+
slug = provider.get_build_slug()
424+
assert slug == url
425+
full_url = provider.get_repo_url()
426+
assert full_url == url
427+
ref = IOLoop().run_sync(provider.get_resolved_ref)
428+
assert ref == resolved_ref
429+
ref_url = IOLoop().run_sync(provider.get_resolved_ref_url)
430+
assert ref_url == full_url
431+
resolved_spec = IOLoop().run_sync(provider.get_resolved_spec)
432+
assert resolved_spec == quote(url, safe='') + f'/{resolved_ref}'

0 commit comments

Comments
 (0)