@@ -164,10 +164,20 @@ def repo_config(self, settings):
164164 def get_resolved_ref (self ):
165165 raise NotImplementedError ("Must be overridden in child class" )
166166
167+ @gen .coroutine
168+ def get_resolved_spec (self ):
169+ """Return the spec with resolved ref."""
170+ raise NotImplementedError ("Must be overridden in child class" )
171+
167172 def get_repo_url (self ):
168173 """Return the git clone-able repo URL"""
169174 raise NotImplementedError ("Must be overridden in the child class" )
170175
176+ @gen .coroutine
177+ def get_resolved_ref_url (self ):
178+ """Return the URL of repository at this commit in history"""
179+ raise NotImplementedError ("Must be overridden in child class" )
180+
171181 def get_build_slug (self ):
172182 """Return a unique build slug"""
173183 raise NotImplementedError ("Must be overriden in the child class" )
@@ -185,9 +195,15 @@ class FakeProvider(RepoProvider):
185195 async def get_resolved_ref (self ):
186196 return "1a2b3c4d5e6f"
187197
198+ async def get_resolved_spec (self ):
199+ return "fake/repo/1a2b3c4d5e6f"
200+
188201 def get_repo_url (self ):
189202 return "https://example.com/fake/repo.git"
190203
204+ async def get_resolved_ref_url (self ):
205+ return "https://example.com/fake/repo/tree/1a2b3c4d5e6f"
206+
191207 def get_build_slug (self ):
192208 return '{user}-{repo}' .format (user = 'Rick' , repo = 'Morty' )
193209
@@ -208,11 +224,25 @@ def get_resolved_ref(self):
208224 self .record_id = r .effective_url .rsplit ("/" , maxsplit = 1 )[1 ]
209225 return self .record_id
210226
227+ async def get_resolved_spec (self ):
228+ if not hasattr (self , 'record_id' ):
229+ self .record_id = await self .get_resolved_ref ()
230+ # zenodo registers a DOI which represents all versions of a software package
231+ # and it always resolves to latest version
232+ # for that case, we have to replace the version number in DOIs with
233+ # the specific (resolved) version (record_id)
234+ resolved_spec = self .spec .split ("zenodo" )[0 ] + "zenodo." + self .record_id
235+ return resolved_spec
236+
211237 def get_repo_url (self ):
212238 # While called repo URL, the return value of this function is passed
213239 # as argument to repo2docker, hence we return the spec as is.
214240 return self .spec
215241
242+ async def get_resolved_ref_url (self ):
243+ resolved_spec = await self .get_resolved_spec ()
244+ return f"https://doi.org/{ resolved_spec } "
245+
216246 def get_build_slug (self ):
217247 return "zenodo-{}" .format (self .record_id )
218248
@@ -241,11 +271,25 @@ def get_resolved_ref(self):
241271
242272 return self .record_id
243273
274+ async def get_resolved_spec (self ):
275+ if not hasattr (self , 'record_id' ):
276+ self .record_id = await self .get_resolved_ref ()
277+
278+ # spec without version is accepted as version 1 - check get_resolved_ref method
279+ # for that case, we have to replace the version number in DOIs with
280+ # the specific (resolved) version (record_id)
281+ resolved_spec = self .spec .split ("figshare" )[0 ] + "figshare." + self .record_id
282+ return resolved_spec
283+
244284 def get_repo_url (self ):
245285 # While called repo URL, the return value of this function is passed
246286 # as argument to repo2docker, hence we return the spec as is.
247287 return self .spec
248288
289+ async def get_resolved_ref_url (self ):
290+ resolved_spec = await self .get_resolved_spec ()
291+ return f"https://doi.org/{ resolved_spec } "
292+
249293 def get_build_slug (self ):
250294 return "figshare-{}" .format (self .record_id )
251295
@@ -270,8 +314,8 @@ class GitRepoProvider(RepoProvider):
270314
271315 def __init__ (self , * args , ** kwargs ):
272316 super ().__init__ (* args , ** kwargs )
273- url , unresolved_ref = self .spec .split ('/' , 1 )
274- self .repo = urllib .parse .unquote (url )
317+ self . url , unresolved_ref = self .spec .split ('/' , 1 )
318+ self .repo = urllib .parse .unquote (self . url )
275319 self .unresolved_ref = urllib .parse .unquote (unresolved_ref )
276320 if not self .unresolved_ref :
277321 raise ValueError ("`unresolved_ref` must be specified as a query parameter for the basic git provider" )
@@ -302,9 +346,18 @@ def get_resolved_ref(self):
302346
303347 return self .resolved_ref
304348
349+ async def get_resolved_spec (self ):
350+ if not hasattr (self , 'resolved_ref' ):
351+ self .resolved_ref = await self .get_resolved_ref ()
352+ return f"{ self .url } /{ self .resolved_ref } "
353+
305354 def get_repo_url (self ):
306355 return self .repo
307356
357+ async def get_resolved_ref_url (self ):
358+ # not possible to construct ref url of unknown git provider
359+ return self .get_repo_url ()
360+
308361 def get_build_slug (self ):
309362 return self .repo
310363
@@ -374,8 +427,8 @@ def _default_git_credentials(self):
374427
375428 def __init__ (self , * args , ** kwargs ):
376429 super ().__init__ (* args , ** kwargs )
377- quoted_namespace , unresolved_ref = self .spec .split ('/' , 1 )
378- self .namespace = urllib .parse .unquote (quoted_namespace )
430+ self . quoted_namespace , unresolved_ref = self .spec .split ('/' , 1 )
431+ self .namespace = urllib .parse .unquote (self . quoted_namespace )
379432 self .unresolved_ref = urllib .parse .unquote (unresolved_ref )
380433 if not self .unresolved_ref :
381434 raise ValueError ("An unresolved ref is required" )
@@ -410,13 +463,22 @@ def get_resolved_ref(self):
410463 self .resolved_ref = ref_info ['id' ]
411464 return self .resolved_ref
412465
466+ async def get_resolved_spec (self ):
467+ if not hasattr (self , 'resolved_ref' ):
468+ self .resolved_ref = await self .get_resolved_ref ()
469+ return f"{ self .quoted_namespace } /{ self .resolved_ref } "
470+
413471 def get_build_slug (self ):
414472 # escape the name and replace dashes with something else.
415473 return '-' .join (p .replace ('-' , '_-' ) for p in self .namespace .split ('/' ))
416474
417475 def get_repo_url (self ):
418- return "https://{hostname}/{namespace}.git" .format (
419- hostname = self .hostname , namespace = self .namespace )
476+ return f"https://{ self .hostname } /{ self .namespace } .git"
477+
478+ async def get_resolved_ref_url (self ):
479+ if not hasattr (self , 'resolved_ref' ):
480+ self .resolved_ref = await self .get_resolved_ref ()
481+ return f"https://{ self .hostname } /{ self .namespace } /tree/{ self .resolved_ref } "
420482
421483
422484class GitHubRepoProvider (RepoProvider ):
@@ -500,8 +562,12 @@ def __init__(self, *args, **kwargs):
500562 self .repo = strip_suffix (self .repo , ".git" )
501563
502564 def get_repo_url (self ):
503- return "https://{hostname}/{user}/{repo}" .format (
504- hostname = self .hostname , user = self .user , repo = self .repo )
565+ return f"https://{ self .hostname } /{ self .user } /{ self .repo } "
566+
567+ async def get_resolved_ref_url (self ):
568+ if not hasattr (self , 'resolved_ref' ):
569+ self .resolved_ref = await self .get_resolved_ref ()
570+ return f"https://{ self .hostname } /{ self .user } /{ self .repo } /tree/{ self .resolved_ref } "
505571
506572 @gen .coroutine
507573 def github_api_request (self , api_url , etag = None ):
@@ -621,6 +687,11 @@ def get_resolved_ref(self):
621687 )
622688 return self .resolved_ref
623689
690+ async def get_resolved_spec (self ):
691+ if not hasattr (self , 'resolved_ref' ):
692+ self .resolved_ref = await self .get_resolved_ref ()
693+ return f"{ self .user } /{ self .repo } /{ self .resolved_ref } "
694+
624695 def get_build_slug (self ):
625696 return '{user}-{repo}' .format (user = self .user , repo = self .repo )
626697
@@ -639,6 +710,7 @@ class GistRepoProvider(GitHubRepoProvider):
639710 """
640711
641712 name = Unicode ('Gist' )
713+ hostname = Unicode ('gist.github.com' )
642714
643715 allow_secret_gist = Bool (
644716 default_value = False ,
@@ -657,7 +729,12 @@ def __init__(self, *args, **kwargs):
657729 self .unresolved_ref = ''
658730
659731 def get_repo_url (self ):
660- return f'https://gist.github.com/{ self .user } /{ self .gist_id } .git'
732+ return f'https://{ self .hostname } /{ self .user } /{ self .gist_id } .git'
733+
734+ async def get_resolved_ref_url (self ):
735+ if not hasattr (self , 'resolved_ref' ):
736+ self .resolved_ref = await self .get_resolved_ref ()
737+ return f'https://{ self .hostname } /{ self .user } /{ self .gist_id } /{ self .resolved_ref } '
661738
662739 @gen .coroutine
663740 def get_resolved_ref (self ):
@@ -689,5 +766,10 @@ def get_resolved_ref(self):
689766
690767 return self .resolved_ref
691768
769+ async def get_resolved_spec (self ):
770+ if not hasattr (self , 'resolved_ref' ):
771+ self .resolved_ref = await self .get_resolved_ref ()
772+ return f'{ self .user } /{ self .gist_id } /{ self .resolved_ref } '
773+
692774 def get_build_slug (self ):
693775 return self .gist_id
0 commit comments