Skip to content

Commit 9de90ce

Browse files
committed
guyzmo#18: Gogs support
1 parent 18a3d4f commit 9de90ce

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed

git_repo/services/ext/gogs.py

+248
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#!/usr/bin/env python
2+
3+
import logging
4+
log = logging.getLogger('git_repo.gogs')
5+
6+
from ..service import register_target, RepositoryService, os
7+
from ...exceptions import ResourceError, ResourceExistsError, ResourceNotFoundError
8+
9+
import requests
10+
from urllib.parse import urlparse, urlunparse
11+
import functools
12+
13+
from git import config as git_config
14+
from git.exc import GitCommandError
15+
16+
@register_target('gg', 'gogs')
17+
class GoGSService(RepositoryService):
18+
fqdn = 'try.gogs.io'
19+
20+
@classmethod
21+
def _url_parse(cls, url):
22+
if '://' not in url:
23+
url = 'https://'+url
24+
parse = urlparse(url)
25+
url_base = urlunparse((parse.scheme, parse.netloc)+('',)*4)
26+
fqdn = parse.hostname
27+
return url_base, fqdn
28+
29+
#@property
30+
#def git_user(self):
31+
# return self.username
32+
33+
@property
34+
def url_ro(self):
35+
return self.url_base
36+
37+
@property
38+
def url_rw(self):
39+
url = self.ssh_url
40+
if '@' in url:
41+
return url
42+
return '@'.join([self.git_user, url])
43+
44+
def url_api(self, rest):
45+
return '{}/api/v1/{}'.format(self.url_base, rest)
46+
47+
@classmethod
48+
def get_auth_token(cls, login, password, prompt=None):
49+
import platform
50+
name = 'git-repo2 token used on {}'.format(platform.node()),
51+
if '/' in login:
52+
url, login = login.rsplit('/', 1)
53+
else:
54+
url = input('URL [{}]> '.format(cls.fqdn))
55+
url_base, fqdn = cls._url_parse(url)
56+
url_api = functools.partial('{}/api/v1/{}'.format, url_base)
57+
r = requests.get(url_api('users/{}/tokens'.format(login)), auth=(login, password), verify=False)
58+
r.raise_for_status()
59+
tokens = r.json()
60+
tokens = dict((o['name'], o['sha1']) for o in tokens)
61+
if name in tokens:
62+
return tokens[name]
63+
if 'git-repo2 token' in tokens:
64+
return tokens['git-repo2 token']
65+
r = requests.get(url_api('users/{}/tokens'.format(login)), auth=(login, password), verify=False)
66+
r.raise_for_status()
67+
token = r.json()
68+
return token['sha1']
69+
70+
@property
71+
def user(self):
72+
r = self.session.get(self.url_api('user'))
73+
r.raise_for_status()
74+
user = r.json()
75+
return user['username']
76+
77+
def orgs(self):
78+
r = self.session.get(self.url_api('user/orgs'))
79+
r.raise_for_status()
80+
return [o['username'] for o in r.json()]
81+
82+
def connect(self):
83+
self.url_base, self.fqdn = self._url_parse(self.fqdn)
84+
verify = self.fqdn == 'try.gogs.io'
85+
#verify = True
86+
verify = self.config.get('verify', ['no','yes'][verify])
87+
if verify.lower().strip() in ('0','no','false',''):
88+
verify = False
89+
elif verify.lower().strip() in ('1','yes','true'):
90+
verify = True
91+
self.default_private = self.config.get('default_private', 'true').lower() not in ('0','no','false')
92+
self.ssh_url = self.config.get('ssh-url', None) or self.fqdn
93+
if not self.repository:
94+
config = git_config.GitConfigParser(os.path.join(os.environ['HOME'], '.gitconfig'), True)
95+
else:
96+
config = self.repository.config_reader()
97+
proxies = {}
98+
for scheme in 'http https'.split():
99+
proxy = config.get_value(scheme, 'proxy', '')
100+
if proxy:
101+
proxies[scheme] = proxy
102+
try:
103+
self.session = requests.Session()
104+
self.session.verify = verify
105+
self.session.proxies.update(proxies)
106+
if not verify:
107+
try:
108+
import urllib3
109+
urllib3.disable_warnings()
110+
except ImportError:
111+
pass
112+
self.session.headers.update({'Authorization': 'token '+self._privatekey})
113+
self.username = self.user
114+
except requests.HTTPError as err:
115+
if err.response and err.response.status_code == 401:
116+
if not self._privatekey:
117+
raise ConnectionError('Could not connect to GoGS. '
118+
'Please configure .gitconfig '
119+
'with your github private key.') from err
120+
else:
121+
raise ConnectionError('Could not connect to GoGS. '
122+
'Check your configuration and try again.') from err
123+
else:
124+
raise err
125+
126+
def create(self, user, repo, add=False):
127+
print('create(%r,%r,%r)' % (user, repo, add))
128+
args = dict(name=repo, private=self.default_private)
129+
try:
130+
if user != self.username:
131+
if user in self.orgs():
132+
r = self.session.post(self.url_api('org/{}/repos'.format(user)), json=args)
133+
else:
134+
raise ResourceNotFoundError("Namespace {} neither an organization or current user.".format(user))
135+
else:
136+
r = self.session.post(self.url_api('user/repos'), json=args)
137+
except requests.HTTPError as err:
138+
if err.response and err.response.status_code == 422:
139+
raise ResourceExistsError("Project already exists.") from err
140+
else: # pragma: no cover
141+
raise ResourceError("Unhandled error.") from err
142+
if add:
143+
self.add(user=self.username, repo=repo, tracking=self.name)
144+
145+
def fork(self, user, repo):
146+
raise NotImplementedError
147+
148+
def delete(self, repo, user=None):
149+
if not user:
150+
user = self.username
151+
r = self.session.delete(self.url_api('repos/{}/{}'.format(user, repo)))
152+
r.raise_for_status()
153+
154+
def list(self, user, _long=False):
155+
import shutil, sys
156+
from datetime import datetime
157+
term_width = shutil.get_terminal_size((80, 20)).columns
158+
def col_print(lines, indent=0, pad=2):
159+
# prints a list of items in a fashion similar to the dir command
160+
# borrowed from https://gist.github.com/critiqjo/2ca84db26daaeb1715e1
161+
n_lines = len(lines)
162+
if n_lines == 0:
163+
return
164+
col_width = max(len(line) for line in lines)
165+
n_cols = int((term_width + pad - indent)/(col_width + pad))
166+
n_cols = min(n_lines, max(1, n_cols))
167+
col_len = int(n_lines/n_cols) + (0 if n_lines % n_cols == 0 else 1)
168+
if (n_cols - 1) * col_len >= n_lines:
169+
n_cols -= 1
170+
cols = [lines[i*col_len : i*col_len + col_len] for i in range(n_cols)]
171+
rows = list(zip(*cols))
172+
rows_missed = zip(*[col[len(rows):] for col in cols[:-1]])
173+
rows.extend(rows_missed)
174+
for row in rows:
175+
print(" "*indent + (" "*pad).join(line.ljust(col_width) for line in row))
176+
177+
if user == self.username:
178+
r = self.session.get(self.url_api('user/repos'))
179+
elif user in self.orgs():
180+
r = self.session.get(self.url_api('orgs/{}/repos'.format(user)))
181+
else:
182+
raise ResourceNotFoundError("User {} does not exists.".format(user))
183+
184+
r.raise_for_status()
185+
repositories = r.json()
186+
if not _long:
187+
repositories = list(repositories)
188+
col_print([repo['full_name'] for repo in repositories])
189+
else:
190+
print('Status\tCommits\tReqs\tIssues\tForks\tCoders\tWatch\tLikes\tLang\tModif\t\t\t\tName', file=sys.stderr)
191+
for repo in repositories:
192+
status = ''.join([
193+
'F' if repo['fork'] else ' ', # is a fork?
194+
'P' if repo['private'] else ' ', # is private?
195+
])
196+
try:
197+
r = self.session.get(self.url_api('repos/{}/issues'.format(repo['full_name'])))
198+
issues = r.json()
199+
except Exception:
200+
issues = []
201+
print('\t'.join([
202+
# status
203+
status,
204+
# stats
205+
str(len(list(()))), # number of commits
206+
str(len(list(()))), # number of pulls
207+
str(len(list(issues))), # number of issues
208+
str(repo.get('forks_count') or 0), # number of forks
209+
str(len(list(()))), # number of contributors
210+
str(repo.get('watchers_count') or 0), # number of subscribers
211+
str(repo.get('stars_count') or 0), # number of ♥
212+
# info
213+
repo.get('language') or '?', # language
214+
repo['updated_at'], # date
215+
repo['full_name'], # name
216+
]))
217+
218+
def get_repository(self, user, repo):
219+
r = self.session.get(self.url_api('repos/{}/{}'.format(user, repo)))
220+
r.raise_for_status()
221+
repository = r.json()
222+
if not repository:
223+
raise ResourceNotFoundError('Repository {}/{} does not exists.'.format(user, repo))
224+
return repository
225+
226+
def gist_list(self, gist=None):
227+
raise NotImplementedError
228+
229+
def gist_fetch(self, gist, fname=None):
230+
raise NotImplementedError
231+
232+
def gist_clone(self, gist):
233+
raise NotImplementedError
234+
235+
def gist_create(self, gist_pathes, description, secret=False):
236+
raise NotImplementedError
237+
238+
def gist_delete(self, gist_id):
239+
raise NotImplementedError
240+
241+
def request_create(self, user, repo, local_branch, remote_branch, title, description=None):
242+
raise NotImplementedError
243+
244+
def request_list(self, user, repo):
245+
raise NotImplementedError
246+
247+
def request_fetch(self, user, repo, request, pull=False):
248+
raise NotImplementedError

0 commit comments

Comments
 (0)