Skip to content

Commit b260b2c

Browse files
committed
🚒 Fixes cloning empty repositories
for Gogs/Gitea, API is not yet out, cf following PRs: cf unfoldingWord-dev/python-gitea-client#9 cf go-gitea/go-sdk#56 cf gogs/gogs#4484 cf gogs/go-gogs-client#64 cf https://github.com/gogits/gogs/pull/4484o fixes #129
1 parent e89d557 commit b260b2c

9 files changed

+207
-48
lines changed

git_repo/services/ext/bitbucket.py

+14-10
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,6 @@ def list(self, user, _long=False):
137137
'/'.join([user.username, repo.name]), # name
138138
]
139139

140-
def get_repository(self, user, repo):
141-
try:
142-
return next(self.bb.repositoryByOwnerAndRepositoryName(owner=user, repository_name=repo))
143-
except HTTPError as err:
144-
raise ResourceNotFoundError('Cannot retrieve repository: {}/{} does not exists.'.format(user, repo))
145-
146140
def _format_gist(self, gist):
147141
return gist.split('/')[-1] if gist.startswith('http') else gist
148142

@@ -240,10 +234,6 @@ def gist_delete(self, gist_id):
240234
raise ResourceNotFoundError("Could not find snippet {}.".format(gist_id)) from err
241235
raise ResourceError("Couldn't delete snippet: {}".format(err)) from err
242236

243-
@staticmethod
244-
def get_project_default_branch(project):
245-
return project.mainbranch.get('name', 'master')
246-
247237
def request_create(self, onto_user, onto_repo, from_branch, onto_branch, title=None, description=None, auto_slug=False, edit=None):
248238
try:
249239
onto_project = self.get_repository(onto_user, onto_repo)
@@ -485,3 +475,17 @@ def user(self):
485475
except (HTTPError, AttributeError) as err:
486476
raise ResourceError("Couldn't find the current user: {}".format(err)) from err
487477

478+
479+
def get_repository(self, user, repo):
480+
try:
481+
return next(self.bb.repositoryByOwnerAndRepositoryName(owner=user, repository_name=repo))
482+
except HTTPError as err:
483+
raise ResourceNotFoundError('Cannot retrieve repository: {}/{} does not exists.'.format(user, repo))
484+
485+
@staticmethod
486+
def is_repository_empty(project):
487+
return project.size == 0
488+
489+
@staticmethod
490+
def get_project_default_branch(project):
491+
return project.mainbranch.get('name', 'master')

git_repo/services/ext/github.py

+14-10
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,6 @@ def list(self, user, _long=False):
150150
else:
151151
print("Cannot show repository {}: {}".format('/'.join([user, repo.name]), err))
152152

153-
def get_repository(self, user, repo):
154-
repository = self.gh.repository(user, repo)
155-
if not repository:
156-
raise ResourceNotFoundError('Cannot delete: repository {}/{} does not exists.'.format(user, repo))
157-
return repository
158-
159153
def _format_gist(self, gist):
160154
return gist.split('https://gist.github.com/')[-1].split('.git')[0]
161155

@@ -231,10 +225,6 @@ def gist_delete(self, gist_id):
231225
raise ResourceNotFoundError('Could not find gist')
232226
gist.delete()
233227

234-
@staticmethod
235-
def get_project_default_branch(project):
236-
return project.default_branch or 'master'
237-
238228
def request_create(self, onto_user, onto_repo, from_branch, onto_branch, title=None, description=None, auto_slug=False, edit=None):
239229
onto_project = self.gh.repository(onto_user, onto_repo)
240230

@@ -373,3 +363,17 @@ def get_parent_project_url(self, user, project, rw=True):
373363
def user(self):
374364
return self.gh.user().login
375365

366+
def get_repository(self, user, repo):
367+
repository = self.gh.repository(user, repo)
368+
if not repository:
369+
raise ResourceNotFoundError('Cannot delete: repository {}/{} does not exists.'.format(user, repo))
370+
return repository
371+
372+
@staticmethod
373+
def is_repository_empty(project):
374+
return project.size == 0
375+
376+
@staticmethod
377+
def get_project_default_branch(project):
378+
return project.default_branch or 'master'
379+

git_repo/services/ext/gitlab.py

+19-11
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,6 @@ def list(self, user, _long=False):
118118
repo.name_with_namespace, # name
119119
]
120120

121-
def get_repository(self, user, repo):
122-
try:
123-
return self.gl.projects.get('{}/{}'.format(user, repo))
124-
except GitlabGetError as err:
125-
if err.response_code == 404:
126-
raise ResourceNotFoundError("Cannot get: repository {}/{} does not exists.".format(user, repo)) from err
127-
128121
@classmethod
129122
def get_auth_token(cls, login, password, prompt=None):
130123
gl = gitlab.Gitlab(url='https://{}'.format(cls.fqdn), email=login, password=password)
@@ -257,10 +250,6 @@ def gist_delete(self, snippet):
257250

258251
return snippet.delete()
259252

260-
@staticmethod
261-
def get_project_default_branch(project):
262-
return project.default_branch or 'master'
263-
264253
def request_create(self, onto_user, onto_repo, from_branch, onto_branch, title=None, description=None, auto_slug=False, edit=None):
265254
try:
266255
onto_project = self.gl.projects.get('/'.join([onto_user, onto_repo]))
@@ -374,3 +363,22 @@ def get_parent_project_url(self, user, project, rw=True):
374363
def user(self):
375364
return self.gl.user.username
376365

366+
def get_repository(self, user, repo):
367+
try:
368+
return self.gl.projects.get('{}/{}'.format(user, repo))
369+
except GitlabGetError as err:
370+
if err.response_code == 404:
371+
raise ResourceNotFoundError("Cannot get: repository {}/{} does not exists.".format(user, repo)) from err
372+
373+
@staticmethod
374+
def get_project_default_branch(project):
375+
return project.default_branch or 'master'
376+
377+
@staticmethod
378+
def is_repository_empty(project):
379+
try:
380+
project.repository_tree()
381+
return True
382+
except gitlab.exceptions.GitlabGetError:
383+
return False
384+

git_repo/services/ext/gogs.py

+26-15
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,6 @@ def get_auth_token(cls, login, password, prompt=None):
116116
token = gg.create_token(auth, name, login)
117117
return token.token
118118

119-
@staticmethod
120-
def get_project_default_branch(project):
121-
# TODO get default branch
122-
return 'master'
123-
124119
@property
125120
def user(self):
126121
return self.gg.username
@@ -200,16 +195,6 @@ def list(self, user, _long=False):
200195
repo['full_name'], # name
201196
]
202197

203-
def get_repository(self, user, repo):
204-
try:
205-
return self.gg.repository(user, repo)
206-
except ApiFailure as err:
207-
if err.status_code == 404:
208-
raise ResourceNotFoundError("Cannot get: repository {}/{} does not exists.".format(user, repo)) from err
209-
raise ResourceError("Unhandled error: {}".format(err)) from err
210-
except Exception as err:
211-
raise ResourceError("Unhandled exception: {}".format(err)) from err
212-
213198
def gist_list(self, gist=None):
214199
raise NotImplementedError
215200

@@ -233,3 +218,29 @@ def request_list(self, user, repo):
233218

234219
def request_fetch(self, user, repo, request, pull=False):
235220
raise NotImplementedError
221+
222+
def get_repository(self, user, repo):
223+
try:
224+
return self.gg.repository(user, repo)
225+
except ApiFailure as err:
226+
if err.status_code == 404:
227+
raise ResourceNotFoundError("Cannot get: repository {}/{} does not exists.".format(user, repo)) from err
228+
raise ResourceError("Unhandled error: {}".format(err)) from err
229+
except Exception as err:
230+
raise ResourceError("Unhandled exception: {}".format(err)) from err
231+
232+
@staticmethod
233+
def get_project_default_branch(project):
234+
# not yet in gogs_client
235+
# cf https://github.com/unfoldingWord-dev/python-gogs-client/pull/8
236+
if hasattr(project, 'default_branch'):
237+
return project.default_branch
238+
return 'master'
239+
240+
@staticmethod
241+
def is_repository_empty(project):
242+
# not yet in gogs_client
243+
# cf https://github.com/unfoldingWord-dev/python-gogs-client/pull/9
244+
if hasattr(project, 'empty'):
245+
return project.empty()
246+
return False

git_repo/services/service.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,17 @@ def clone(self, user, repo, branch=None, rw=True):
329329
'''
330330
log.info('Cloning {}…'.format(repo))
331331

332+
project = self.get_repository(user, repo)
332333
if not branch:
333-
project = self.get_repository(user, repo)
334334
branch = self.get_project_default_branch(project)
335335

336+
is_empty = self.is_repository_empty(project)
337+
if is_empty:
338+
self.repository.init()
339+
336340
remote, *_ = self.add(user=user, repo=repo, tracking=True, rw=rw)
337-
self.pull(remote, branch)
341+
if not is_empty:
342+
self.pull(remote, branch)
338343

339344
def add(self, repo, user=None, name=None, tracking=False, alone=False, rw=True, auto_slug=False):
340345
'''Adding repository as remote
@@ -578,6 +583,13 @@ def get_parent_project_url(self, user, repo, rw=True): #pragma: no cover
578583
def user(self): #pragma: no cover
579584
raise NotImplementedError
580585

586+
@staticmethod
587+
def is_repository_empty(project):
588+
raise NotImplementedError
589+
590+
@staticmethod
591+
def get_project_default_branch(project):
592+
raise NotImplementedError
581593

582594
'''
583595
register all services by importing their modules, from the ext pagckage

tests/helpers.py

+1
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ def action_clone(self, namespace, repository):
539539
with self.mockup_git(namespace, repository):
540540
local_slug = self.service.format_path(namespace=namespace, repository=repository, rw=True)
541541
self.set_mock_popen_commands([
542+
('git init', b'Initialized empty Git repository in /tmp/bar/.git/', b'', 0),
542543
('git remote add all {}'.format(local_slug), b'', b'', 0),
543544
('git remote add {} {}'.format(self.service.name, local_slug), b'', b'', 0),
544545
('git version', b'git version 2.8.0', b'', 0),

tests/integration/cassettes/test_gitlab_test_04_clone.json

+41
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,47 @@
8181
},
8282
"url": "https://gitlab.com/api/v3/projects/guyzmo%2Fgit-repo"
8383
}
84+
},
85+
{
86+
"recorded_at": "2017-05-04T10:32:22",
87+
"request": {
88+
"body": {
89+
"encoding": "utf-8",
90+
"string": ""
91+
},
92+
"headers": {
93+
"Accept": "*/*",
94+
"Accept-Encoding": "identity",
95+
"Connection": "keep-alive",
96+
"PRIVATE-TOKEN": "<PRIVATE_KEY_GITLAB>",
97+
"User-Agent": "python-requests/2.13.0"
98+
},
99+
"method": "GET",
100+
"uri": "https://gitlab.com/api/v3/projects/1016797/repository/tree"
101+
},
102+
"response": {
103+
"body": {
104+
"encoding": null,
105+
"string": "[{\"id\":\"996e370ca098a6f8ad4de7f290337be3d2872847\",\"name\":\"git_repo\",\"type\":\"tree\",\"path\":\"git_repo\",\"mode\":\"040000\"},{\"id\":\"14b7a932a44744ef896569fa61eff6c765cfb24b\",\"name\":\"tests\",\"type\":\"tree\",\"path\":\"tests\",\"mode\":\"040000\"},{\"id\":\"51ceaea89e237f5886f409bcb078d6dbe0bcbb23\",\"name\":\".editorconfig\",\"type\":\"blob\",\"path\":\".editorconfig\",\"mode\":\"100644\"},{\"id\":\"7a5782d8822078d2230887e3359dd48452c6a3de\",\"name\":\".gitignore\",\"type\":\"blob\",\"path\":\".gitignore\",\"mode\":\"100644\"},{\"id\":\"d0a4e8237e65ef257110a6c6ec9274e88f993fa6\",\"name\":\".gitlab-ci.yml\",\"type\":\"blob\",\"path\":\".gitlab-ci.yml\",\"mode\":\"100644\"},{\"id\":\"0f3b6b1adf2c419dc5e391917236d82fff7ab40a\",\"name\":\".travis.yml\",\"type\":\"blob\",\"path\":\".travis.yml\",\"mode\":\"100644\"},{\"id\":\"c767d63f397a24306badb542b64a8b33aac42d37\",\"name\":\"LICENSE\",\"type\":\"blob\",\"path\":\"LICENSE\",\"mode\":\"100644\"},{\"id\":\"f0cd0f60924cf80dbb2366f85621de52cd340c6f\",\"name\":\"MANIFEST.in\",\"type\":\"blob\",\"path\":\"MANIFEST.in\",\"mode\":\"100644\"},{\"id\":\"bcfa7d36f8abca75d5586e61cdb1765dd1f051ab\",\"name\":\"README.md\",\"type\":\"blob\",\"path\":\"README.md\",\"mode\":\"100644\"},{\"id\":\"afa2b3515e910d7f01eb4eb95480e0fbd9385f75\",\"name\":\"VERSION\",\"type\":\"blob\",\"path\":\"VERSION\",\"mode\":\"100644\"},{\"id\":\"559f42ec779c0df90883b99f2f2be4f14bd120ea\",\"name\":\"buildout.cfg\",\"type\":\"blob\",\"path\":\"buildout.cfg\",\"mode\":\"100644\"},{\"id\":\"b0abd8c1d717ce2bbecab572355798a2dbada6f1\",\"name\":\"requirements-test.txt\",\"type\":\"blob\",\"path\":\"requirements-test.txt\",\"mode\":\"100644\"},{\"id\":\"6a165a3f064946ee2553409091586e0e8ce06a83\",\"name\":\"requirements.txt\",\"type\":\"blob\",\"path\":\"requirements.txt\",\"mode\":\"100644\"},{\"id\":\"ec8880a7b98cf1e579552dc04f832121ade57396\",\"name\":\"setup.py\",\"type\":\"blob\",\"path\":\"setup.py\",\"mode\":\"100644\"},{\"id\":\"90999d5df1b573144a6e139f09b00ba9e3128f41\",\"name\":\"tox.ini\",\"type\":\"blob\",\"path\":\"tox.ini\",\"mode\":\"100644\"}]"
106+
},
107+
"headers": {
108+
"Cache-Control": "max-age=0, private, must-revalidate",
109+
"Content-Length": "1819",
110+
"Content-Type": "application/json",
111+
"Date": "Thu, 04 May 2017 10:32:22 GMT",
112+
"Etag": "W/\"96885a58bce1762b8fa9c8dc3a428ac5\"",
113+
"Server": "nginx",
114+
"Strict-Transport-Security": "max-age=31536000",
115+
"Vary": "Origin",
116+
"X-Request-Id": "7aba5f66-61da-4832-8a08-32190375f7c1",
117+
"X-Runtime": "0.100891"
118+
},
119+
"status": {
120+
"code": 200,
121+
"message": "OK"
122+
},
123+
"url": "https://gitlab.com/api/v3/projects/1016797/repository/tree"
124+
}
84125
}
85126
],
86127
"recorded_with": "betamax/0.5.1"

tests/integration/cassettes/test_gitlab_test_19_request_fetch.json

+39
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,45 @@
8181
},
8282
"url": "https://gitlab.com/api/v3/projects/<GITLAB_NAMESPACE>%2Fgit-repo"
8383
}
84+
},
85+
{
86+
"recorded_at": "2017-05-04T10:32:29",
87+
"request": {
88+
"body": {
89+
"encoding": "utf-8",
90+
"string": ""
91+
},
92+
"headers": {
93+
"Accept": "*/*",
94+
"Accept-Encoding": "identity",
95+
"Connection": "keep-alive",
96+
"PRIVATE-TOKEN": "<PRIVATE_KEY_GITLAB>",
97+
"User-Agent": "python-requests/2.13.0"
98+
},
99+
"method": "GET",
100+
"uri": "https://gitlab.com/api/v3/projects/1848932/repository/tree"
101+
},
102+
"response": {
103+
"body": {
104+
"encoding": null,
105+
"string": "{\"message\":\"404 Project Not Found\"}"
106+
},
107+
"headers": {
108+
"Cache-Control": "no-cache",
109+
"Content-Length": "35",
110+
"Content-Type": "application/json",
111+
"Date": "Thu, 04 May 2017 10:32:29 GMT",
112+
"Server": "nginx",
113+
"Vary": "Origin",
114+
"X-Request-Id": "405f9670-f3d3-4d9b-a04f-22d4d393224c",
115+
"X-Runtime": "0.036389"
116+
},
117+
"status": {
118+
"code": 404,
119+
"message": "Not Found"
120+
},
121+
"url": "https://gitlab.com/api/v3/projects/1848932/repository/tree"
122+
}
84123
}
85124
],
86125
"recorded_with": "betamax/0.5.1"

tests/integration/cassettes/test_gitlab_test_19_request_fetch__bad_request.json

+39
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,45 @@
8181
},
8282
"url": "https://gitlab.com/api/v3/projects/<GITLAB_NAMESPACE>%2Fgit-repo"
8383
}
84+
},
85+
{
86+
"recorded_at": "2017-05-04T10:32:34",
87+
"request": {
88+
"body": {
89+
"encoding": "utf-8",
90+
"string": ""
91+
},
92+
"headers": {
93+
"Accept": "*/*",
94+
"Accept-Encoding": "identity",
95+
"Connection": "keep-alive",
96+
"PRIVATE-TOKEN": "<PRIVATE_KEY_GITLAB>",
97+
"User-Agent": "python-requests/2.13.0"
98+
},
99+
"method": "GET",
100+
"uri": "https://gitlab.com/api/v3/projects/1848932/repository/tree"
101+
},
102+
"response": {
103+
"body": {
104+
"encoding": null,
105+
"string": "{\"message\":\"404 Project Not Found\"}"
106+
},
107+
"headers": {
108+
"Cache-Control": "no-cache",
109+
"Content-Length": "35",
110+
"Content-Type": "application/json",
111+
"Date": "Thu, 04 May 2017 10:32:34 GMT",
112+
"Server": "nginx",
113+
"Vary": "Origin",
114+
"X-Request-Id": "fe0f3be1-0c3d-4bb8-b9e1-b5f0353bc230",
115+
"X-Runtime": "0.061090"
116+
},
117+
"status": {
118+
"code": 404,
119+
"message": "Not Found"
120+
},
121+
"url": "https://gitlab.com/api/v3/projects/1848932/repository/tree"
122+
}
84123
}
85124
],
86125
"recorded_with": "betamax/0.5.1"

0 commit comments

Comments
 (0)