Skip to content

Commit 7b37bc2

Browse files
author
Daniel Widerin
committed
Add basic GitLab repo sync support
1 parent b220f81 commit 7b37bc2

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

readthedocs/oauth/constants.py

+2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55

66
OAUTH_SOURCE_GITHUB = 'github'
7+
OAUTH_SOURCE_GITLAB = 'gitlab'
78
OAUTH_SOURCE_BITBUCKET = 'bitbucket'
89

910
OAUTH_SOURCE = (
1011
(OAUTH_SOURCE_GITHUB, _('GitHub')),
12+
(OAUTH_SOURCE_GITLAB, _('GitLab')),
1113
(OAUTH_SOURCE_BITBUCKET, _('Bitbucket')),
1214
)

readthedocs/oauth/managers.py

+34-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from readthedocs.privacy.loader import RelatedUserManager
99

10-
from .constants import OAUTH_SOURCE_GITHUB, OAUTH_SOURCE_BITBUCKET
10+
from .constants import OAUTH_SOURCE_GITHUB, OAUTH_SOURCE_GITLAB, OAUTH_SOURCE_BITBUCKET
1111

1212

1313
logger = logging.getLogger(__name__)
@@ -90,6 +90,39 @@ def create_from_bitbucket_api(self, api_json, user, organization=None,
9090
logger.debug('Not importing %s because mismatched type' %
9191
api_json['name'])
9292

93+
def create_from_gitlab_api(self, api_json, user, organization=None,
94+
privacy=DEFAULT_PRIVACY_LEVEL):
95+
logger.info('Trying GitLab: %s' % api_json['name_with_namespace'])
96+
if (api_json['public'] is False and privacy == 'private' or
97+
api_json['public'] is True and privacy == 'public'):
98+
project, created = self.get_or_create(
99+
full_name=api_json['name_with_namespace'])
100+
if project.organization and project.organization != organization:
101+
logger.debug('Not importing %s because mismatched orgs' %
102+
api_json['name'])
103+
return None
104+
else:
105+
project.organization = organization
106+
project.users.add(user)
107+
project.name = api_json['name']
108+
project.description = api_json['description']
109+
project.clone_url = api_json['http_url_to_repo']
110+
project.ssh_url = api_json['ssh_url_to_repo']
111+
project.html_url = api_json['web_url']
112+
project.vcs = 'git'
113+
project.private = False
114+
project.source = OAUTH_SOURCE_GITLAB
115+
if api_json['avatar_url']:
116+
project.avatar_url = api_json['avatar_url']
117+
else:
118+
project.avatar_url = None
119+
project.json = json.dumps(api_json)
120+
project.save()
121+
return project
122+
else:
123+
logger.debug('Not importing %s because mismatched type' %
124+
api_json['name'])
125+
93126

94127
class RemoteOrganizationManager(RelatedUserManager):
95128

readthedocs/oauth/tasks.py

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from readthedocs.core.utils.tasks import user_id_matches
77
from .utils import import_bitbucket
88
from .utils import import_github
9+
from .utils import import_gitlab
910

1011

1112
@permission_check(user_id_matches)
@@ -17,6 +18,7 @@ def run_public(self, user_id):
1718
user = User.objects.get(pk=user_id)
1819
import_github(user, sync=True)
1920
import_bitbucket(user, sync=True)
21+
import_gitlab(user, sync=True)
2022

2123

2224
sync_remote_repositories = celery_app.tasks[SyncRemoteRepositories.name]

readthedocs/oauth/utils.py

+66
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from allauth.socialaccount.models import SocialToken, SocialAccount
1010
from allauth.socialaccount.providers.bitbucket.provider import BitbucketProvider
1111
from allauth.socialaccount.providers.github.provider import GitHubProvider
12+
from allauth.socialaccount.providers.gitlab.provider import GitLabProvider
13+
from allauth.socialaccount.providers.gitlab.views import GitLabOAuth2Adapter
1214

1315
from readthedocs.builds import utils as build_utils
1416
from readthedocs.restapi.client import api
@@ -42,6 +44,14 @@ def get_oauth_session(user, provider):
4244
resource_owner_key=token.token,
4345
resource_owner_secret=token.token_secret
4446
)
47+
elif provider == GitLabProvider.id:
48+
session = OAuth2Session(
49+
client_id=token.app.client_id,
50+
token={
51+
'access_token': str(token.token),
52+
'token_type': 'bearer'
53+
}
54+
)
4555

4656
return session or None
4757

@@ -217,3 +227,59 @@ def import_bitbucket(user, sync):
217227
'try reconnecting your account')
218228

219229
return session is not None
230+
231+
###
232+
# GitLab
233+
###
234+
235+
def gitlab_paginate(session, url):
236+
"""Combines results from GitLab pagination
237+
238+
:param session: requests client instance
239+
:param url: start url to get the data from.
240+
241+
"""
242+
result = []
243+
while url:
244+
r = session.get(url)
245+
result.extend([r.json()])
246+
if 'next' in r.links:
247+
url = r.links['next']['url']
248+
else:
249+
url = None
250+
return result
251+
252+
253+
def process_gitlab_json(user, json):
254+
try:
255+
for page in json:
256+
for repo in page:
257+
if repo.get('archived'):
258+
continue
259+
RemoteRepository.objects.create_from_gitlab_api(repo,
260+
user=user)
261+
except TypeError, e:
262+
print e
263+
264+
265+
def import_gitlab(user, sync):
266+
"""Import from GitLab"""
267+
session = get_oauth_session(user, provider=GitLabProvider.id)
268+
try:
269+
social_account = user.socialaccount_set.get(provider=GitLabProvider.id)
270+
except SocialAccount.DoesNotExist:
271+
pass
272+
if sync and session:
273+
# Get user repos
274+
try:
275+
owner_resp = gitlab_paginate(
276+
session, '{0}/api/v3/projects'.format(
277+
GitLabOAuth2Adapter.provider_base_url
278+
)
279+
)
280+
process_gitlab_json(user, owner_resp)
281+
except (TypeError, ValueError):
282+
raise Exception('Could not sync your GitLab repositories, '
283+
'try reconnecting your account')
284+
285+
return session is not None

0 commit comments

Comments
 (0)