##// END OF EJS Templates
updated hg-app db manage and global settings
marcink -
r345:bb8f45f6 default
parent child Browse files
Show More
@@ -1,188 +1,193 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # database managment for hg app
3 # database managment for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20
20
21 """
21 """
22 Created on April 10, 2010
22 Created on April 10, 2010
23 database managment and creation for hg app
23 database managment and creation for hg app
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 from os.path import dirname as dn, join as jn
27 from os.path import dirname as dn, join as jn
28 import os
28 import os
29 import sys
29 import sys
30 import uuid
30 import uuid
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
32 sys.path.append(ROOT)
32 sys.path.append(ROOT)
33
33
34 from pylons_app.lib.auth import get_crypt_password
34 from pylons_app.lib.auth import get_crypt_password
35 from pylons_app.model import init_model
35 from pylons_app.model import init_model
36 from pylons_app.model.db import User, Permission, HgAppUi
36 from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings
37 from pylons_app.model.meta import Session, Base
37 from pylons_app.model.meta import Session, Base
38 from sqlalchemy.engine import create_engine
38 from sqlalchemy.engine import create_engine
39 import logging
39 import logging
40
40
41 log = logging.getLogger('db manage')
41 log = logging.getLogger('db manage')
42 log.setLevel(logging.DEBUG)
42 log.setLevel(logging.DEBUG)
43 console_handler = logging.StreamHandler()
43 console_handler = logging.StreamHandler()
44 console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d"
44 console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d"
45 " %(levelname)-5.5s [%(name)s] %(message)s"))
45 " %(levelname)-5.5s [%(name)s] %(message)s"))
46 log.addHandler(console_handler)
46 log.addHandler(console_handler)
47
47
48 class DbManage(object):
48 class DbManage(object):
49 def __init__(self, log_sql):
49 def __init__(self, log_sql):
50 self.dbname = 'hg_app.db'
50 self.dbname = 'hg_app.db'
51 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
51 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
52 engine = create_engine(dburi, echo=log_sql)
52 engine = create_engine(dburi, echo=log_sql)
53 init_model(engine)
53 init_model(engine)
54 self.sa = Session()
54 self.sa = Session()
55 self.db_exists = False
55 self.db_exists = False
56
56
57 def check_for_db(self, override):
57 def check_for_db(self, override):
58 log.info('checking for exisiting db')
58 log.info('checking for exisiting db')
59 if os.path.isfile(jn(ROOT, self.dbname)):
59 if os.path.isfile(jn(ROOT, self.dbname)):
60 self.db_exists = True
60 self.db_exists = True
61 log.info('database exisist')
61 log.info('database exisist')
62 if not override:
62 if not override:
63 raise Exception('database already exists')
63 raise Exception('database already exists')
64
64
65 def create_tables(self, override=False):
65 def create_tables(self, override=False):
66 """
66 """
67 Create a auth database
67 Create a auth database
68 """
68 """
69 self.check_for_db(override)
69 self.check_for_db(override)
70 if override:
70 if override:
71 log.info("database exisist and it's going to be destroyed")
71 log.info("database exisist and it's going to be destroyed")
72 if self.db_exists:
72 if self.db_exists:
73 os.remove(jn(ROOT, self.dbname))
73 os.remove(jn(ROOT, self.dbname))
74 Base.metadata.create_all(checkfirst=override)
74 Base.metadata.create_all(checkfirst=override)
75 log.info('Created tables for %s', self.dbname)
75 log.info('Created tables for %s', self.dbname)
76
76
77 def admin_prompt(self):
77 def admin_prompt(self):
78 import getpass
78 import getpass
79 username = raw_input('Specify admin username:')
79 username = raw_input('Specify admin username:')
80 password = getpass.getpass('Specify admin password:')
80 password = getpass.getpass('Specify admin password:')
81 self.create_user(username, password, True)
81 self.create_user(username, password, True)
82
82
83 def config_prompt(self):
83 def config_prompt(self):
84 log.info('Seting up repositories.config')
84 log.info('Setting up repositories config')
85
85
86
86
87 path = raw_input('Specify valid full path to your repositories'
87 path = raw_input('Specify valid full path to your repositories'
88 ' you can change this later application settings:')
88 ' you can change this later application settings:')
89
89
90 if not os.path.isdir(path):
90 if not os.path.isdir(path):
91 log.error('You entered wrong path')
91 log.error('You entered wrong path')
92 sys.exit()
92 sys.exit()
93
93
94 hooks = HgAppUi()
94 hooks = HgAppUi()
95 hooks.ui_section = 'hooks'
95 hooks.ui_section = 'hooks'
96 hooks.ui_key = 'changegroup'
96 hooks.ui_key = 'changegroup'
97 hooks.ui_value = 'hg update >&2'
97 hooks.ui_value = 'hg update >&2'
98
98
99 web1 = HgAppUi()
99 web1 = HgAppUi()
100 web1.ui_section = 'web'
100 web1.ui_section = 'web'
101 web1.ui_key = 'push_ssl'
101 web1.ui_key = 'push_ssl'
102 web1.ui_value = 'false'
102 web1.ui_value = 'false'
103
103
104 web2 = HgAppUi()
104 web2 = HgAppUi()
105 web2.ui_section = 'web'
105 web2.ui_section = 'web'
106 web2.ui_key = 'allow_archive'
106 web2.ui_key = 'allow_archive'
107 web2.ui_value = 'gz zip bz2'
107 web2.ui_value = 'gz zip bz2'
108
108
109 web3 = HgAppUi()
109 web3 = HgAppUi()
110 web3.ui_section = 'web'
110 web3.ui_section = 'web'
111 web3.ui_key = 'allow_push'
111 web3.ui_key = 'allow_push'
112 web3.ui_value = '*'
112 web3.ui_value = '*'
113
113
114 web4 = HgAppUi()
114 web4 = HgAppUi()
115 web4.ui_section = 'web'
115 web4.ui_section = 'web'
116 web4.ui_key = 'baseurl'
116 web4.ui_key = 'baseurl'
117 web4.ui_value = '/'
117 web4.ui_value = '/'
118
118
119 paths = HgAppUi()
119 paths = HgAppUi()
120 paths.ui_section = 'paths'
120 paths.ui_section = 'paths'
121 paths.ui_key = '/'
121 paths.ui_key = '/'
122 paths.ui_value = os.path.join(path, '*')
122 paths.ui_value = os.path.join(path, '*')
123
123
124
124
125 hgsettings = HgAppSettings()
126 hgsettings.app_auth_realm = 'hg-app authentication'
127 hgsettings.app_title = 'hg-app'
128
125 try:
129 try:
126 self.sa.add(hooks)
130 self.sa.add(hooks)
127 self.sa.add(web1)
131 self.sa.add(web1)
128 self.sa.add(web2)
132 self.sa.add(web2)
129 self.sa.add(web3)
133 self.sa.add(web3)
130 self.sa.add(web4)
134 self.sa.add(web4)
131 self.sa.add(paths)
135 self.sa.add(paths)
136 self.sa.add(hgsettings)
132 self.sa.commit()
137 self.sa.commit()
133 except:
138 except:
134 self.sa.rollback()
139 self.sa.rollback()
135 raise
140 raise
136 log.info('created ui config')
141 log.info('created ui config')
137
142
138 def create_user(self, username, password, admin=False):
143 def create_user(self, username, password, admin=False):
139
144
140 log.info('creating default user')
145 log.info('creating default user')
141 #create default user for handling default permissions.
146 #create default user for handling default permissions.
142 def_user = User()
147 def_user = User()
143 def_user.username = 'default'
148 def_user.username = 'default'
144 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
149 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
145 def_user.name = 'default'
150 def_user.name = 'default'
146 def_user.lastname = 'default'
151 def_user.lastname = 'default'
147 def_user.email = 'default@default.com'
152 def_user.email = 'default@default.com'
148 def_user.admin = False
153 def_user.admin = False
149 def_user.active = False
154 def_user.active = False
150
155
151 log.info('creating administrator user %s', username)
156 log.info('creating administrator user %s', username)
152 new_user = User()
157 new_user = User()
153 new_user.username = username
158 new_user.username = username
154 new_user.password = get_crypt_password(password)
159 new_user.password = get_crypt_password(password)
155 new_user.name = 'Hg'
160 new_user.name = 'Hg'
156 new_user.lastname = 'Admin'
161 new_user.lastname = 'Admin'
157 new_user.email = 'admin@localhost'
162 new_user.email = 'admin@localhost'
158 new_user.admin = admin
163 new_user.admin = admin
159 new_user.active = True
164 new_user.active = True
160
165
161 try:
166 try:
162 self.sa.add(def_user)
167 self.sa.add(def_user)
163 self.sa.add(new_user)
168 self.sa.add(new_user)
164 self.sa.commit()
169 self.sa.commit()
165 except:
170 except:
166 self.sa.rollback()
171 self.sa.rollback()
167 raise
172 raise
168
173
169 def create_permissions(self):
174 def create_permissions(self):
170 #module.(access|create|change|delete)_[name]
175 #module.(access|create|change|delete)_[name]
171 #module.(read|write|owner)
176 #module.(read|write|owner)
172 perms = [('repository.none', 'Repository no access'),
177 perms = [('repository.none', 'Repository no access'),
173 ('repository.read', 'Repository read access'),
178 ('repository.read', 'Repository read access'),
174 ('repository.write', 'Repository write access'),
179 ('repository.write', 'Repository write access'),
175 ('repository.admin', 'Repository admin access'),
180 ('repository.admin', 'Repository admin access'),
176 ('hg.admin', 'Hg Administrator'),
181 ('hg.admin', 'Hg Administrator'),
177 ]
182 ]
178
183
179 for p in perms:
184 for p in perms:
180 new_perm = Permission()
185 new_perm = Permission()
181 new_perm.permission_name = p[0]
186 new_perm.permission_name = p[0]
182 new_perm.permission_longname = p[1]
187 new_perm.permission_longname = p[1]
183 try:
188 try:
184 self.sa.add(new_perm)
189 self.sa.add(new_perm)
185 self.sa.commit()
190 self.sa.commit()
186 except:
191 except:
187 self.sa.rollback()
192 self.sa.rollback()
188 raise
193 raise
@@ -1,194 +1,202 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for hg app
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 from beaker.cache import cache_region
19 from beaker.cache import cache_region
20
20
21 """
21 """
22 Created on April 18, 2010
22 Created on April 18, 2010
23 Utilities for hg app
23 Utilities for hg app
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 import os
27 import os
28 import logging
28 import logging
29 from mercurial import ui, config, hg
29 from mercurial import ui, config, hg
30 from mercurial.error import RepoError
30 from mercurial.error import RepoError
31 from pylons_app.model.db import Repository, User, HgAppUi
31 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
32 from pylons_app.model.meta import Session
32 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
33
34
34
35
35 def get_repo_slug(request):
36 def get_repo_slug(request):
36 return request.environ['pylons.routes_dict'].get('repo_name')
37 return request.environ['pylons.routes_dict'].get('repo_name')
37
38
38 def is_mercurial(environ):
39 def is_mercurial(environ):
39 """
40 """
40 Returns True if request's target is mercurial server - header
41 Returns True if request's target is mercurial server - header
41 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
42 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
42 """
43 """
43 http_accept = environ.get('HTTP_ACCEPT')
44 http_accept = environ.get('HTTP_ACCEPT')
44 if http_accept and http_accept.startswith('application/mercurial'):
45 if http_accept and http_accept.startswith('application/mercurial'):
45 return True
46 return True
46 return False
47 return False
47
48
48 def check_repo_dir(paths):
49 def check_repo_dir(paths):
49 repos_path = paths[0][1].split('/')
50 repos_path = paths[0][1].split('/')
50 if repos_path[-1] in ['*', '**']:
51 if repos_path[-1] in ['*', '**']:
51 repos_path = repos_path[:-1]
52 repos_path = repos_path[:-1]
52 if repos_path[0] != '/':
53 if repos_path[0] != '/':
53 repos_path[0] = '/'
54 repos_path[0] = '/'
54 if not os.path.isdir(os.path.join(*repos_path)):
55 if not os.path.isdir(os.path.join(*repos_path)):
55 raise Exception('Not a valid repository in %s' % paths[0][1])
56 raise Exception('Not a valid repository in %s' % paths[0][1])
56
57
57 def check_repo_fast(repo_name, base_path):
58 def check_repo_fast(repo_name, base_path):
58 if os.path.isdir(os.path.join(base_path, repo_name)):return False
59 if os.path.isdir(os.path.join(base_path, repo_name)):return False
59 return True
60 return True
60
61
61 def check_repo(repo_name, base_path, verify=True):
62 def check_repo(repo_name, base_path, verify=True):
62
63
63 repo_path = os.path.join(base_path, repo_name)
64 repo_path = os.path.join(base_path, repo_name)
64
65
65 try:
66 try:
66 if not check_repo_fast(repo_name, base_path):
67 if not check_repo_fast(repo_name, base_path):
67 return False
68 return False
68 r = hg.repository(ui.ui(), repo_path)
69 r = hg.repository(ui.ui(), repo_path)
69 if verify:
70 if verify:
70 hg.verify(r)
71 hg.verify(r)
71 #here we hnow that repo exists it was verified
72 #here we hnow that repo exists it was verified
72 log.info('%s repo is already created', repo_name)
73 log.info('%s repo is already created', repo_name)
73 return False
74 return False
74 except RepoError:
75 except RepoError:
75 #it means that there is no valid repo there...
76 #it means that there is no valid repo there...
76 log.info('%s repo is free for creation', repo_name)
77 log.info('%s repo is free for creation', repo_name)
77 return True
78 return True
78
79
79
80
80 @cache_region('super_short_term', 'cached_hg_ui')
81 @cache_region('super_short_term', 'cached_hg_ui')
81 def get_hg_ui_cached():
82 def get_hg_ui_cached():
82 from pylons_app.model.meta import Session
83 sa = Session()
83 sa = Session()
84 return sa.query(HgAppUi).all()
84 return sa.query(HgAppUi).all()
85
85
86 def get_hg_settings():
87 sa = Session()
88 ret = sa.query(HgAppSettings).scalar()
89 if not ret:
90 raise Exception('Could not get application settings !')
91 return ret
92
86 def make_ui(read_from='file', path=None, checkpaths=True):
93 def make_ui(read_from='file', path=None, checkpaths=True):
87 """
94 """
88 A function that will read python rc files or database
95 A function that will read python rc files or database
89 and make an mercurial ui object from read options
96 and make an mercurial ui object from read options
90
97
91 @param path: path to mercurial config file
98 @param path: path to mercurial config file
92 @param checkpaths: check the path
99 @param checkpaths: check the path
93 @param read_from: read from 'file' or 'db'
100 @param read_from: read from 'file' or 'db'
94 """
101 """
95 #propagated from mercurial documentation
102 #propagated from mercurial documentation
96 sections = ['alias', 'auth',
103 sections = ['alias', 'auth',
97 'decode/encode', 'defaults',
104 'decode/encode', 'defaults',
98 'diff', 'email',
105 'diff', 'email',
99 'extensions', 'format',
106 'extensions', 'format',
100 'merge-patterns', 'merge-tools',
107 'merge-patterns', 'merge-tools',
101 'hooks', 'http_proxy',
108 'hooks', 'http_proxy',
102 'smtp', 'patch',
109 'smtp', 'patch',
103 'paths', 'profiling',
110 'paths', 'profiling',
104 'server', 'trusted',
111 'server', 'trusted',
105 'ui', 'web', ]
112 'ui', 'web', ]
106 baseui = ui.ui()
113 baseui = ui.ui()
107
114
108
115
109 if read_from == 'file':
116 if read_from == 'file':
110 if not os.path.isfile(path):
117 if not os.path.isfile(path):
111 log.warning('Unable to read config file %s' % path)
118 log.warning('Unable to read config file %s' % path)
112 return False
119 return False
113
120
114 cfg = config.config()
121 cfg = config.config()
115 cfg.read(path)
122 cfg.read(path)
116 for section in sections:
123 for section in sections:
117 for k, v in cfg.items(section):
124 for k, v in cfg.items(section):
118 baseui.setconfig(section, k, v)
125 baseui.setconfig(section, k, v)
119 if checkpaths:check_repo_dir(cfg.items('paths'))
126 if checkpaths:check_repo_dir(cfg.items('paths'))
120
127
121
128
122 elif read_from == 'db':
129 elif read_from == 'db':
123 hg_ui = get_hg_ui_cached()
130 hg_ui = get_hg_ui_cached()
124 for ui_ in hg_ui:
131 for ui_ in hg_ui:
125 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
132 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
126
133
127
134
128 return baseui
135 return baseui
129
136
130
137
131 def set_hg_app_config(config):
138 def set_hg_app_config(config):
132 config['hg_app_auth_realm'] = 'realm'
139 hgsettings = get_hg_settings()
133 config['hg_app_name'] = 'app name'
140 config['hg_app_auth_realm'] = hgsettings.app_auth_realm
141 config['hg_app_name'] = hgsettings.app_title
134
142
135 def invalidate_cache(name, *args):
143 def invalidate_cache(name, *args):
136 """Invalidates given name cache"""
144 """Invalidates given name cache"""
137
145
138 from beaker.cache import region_invalidate
146 from beaker.cache import region_invalidate
139 log.info('INVALIDATING CACHE FOR %s', name)
147 log.info('INVALIDATING CACHE FOR %s', name)
140
148
141 """propagate our arguments to make sure invalidation works. First
149 """propagate our arguments to make sure invalidation works. First
142 argument has to be the name of cached func name give to cache decorator
150 argument has to be the name of cached func name give to cache decorator
143 without that the invalidation would not work"""
151 without that the invalidation would not work"""
144 tmp = [name]
152 tmp = [name]
145 tmp.extend(args)
153 tmp.extend(args)
146 args = tuple(tmp)
154 args = tuple(tmp)
147
155
148 if name == 'cached_repo_list':
156 if name == 'cached_repo_list':
149 from pylons_app.model.hg_model import _get_repos_cached
157 from pylons_app.model.hg_model import _get_repos_cached
150 region_invalidate(_get_repos_cached, None, *args)
158 region_invalidate(_get_repos_cached, None, *args)
151
159
152 if name == 'full_changelog':
160 if name == 'full_changelog':
153 from pylons_app.model.hg_model import _full_changelog_cached
161 from pylons_app.model.hg_model import _full_changelog_cached
154 region_invalidate(_full_changelog_cached, None, *args)
162 region_invalidate(_full_changelog_cached, None, *args)
155
163
156 from vcs.backends.base import BaseChangeset
164 from vcs.backends.base import BaseChangeset
157 from vcs.utils.lazy import LazyProperty
165 from vcs.utils.lazy import LazyProperty
158 class EmptyChangeset(BaseChangeset):
166 class EmptyChangeset(BaseChangeset):
159
167
160 revision = -1
168 revision = -1
161 message = ''
169 message = ''
162
170
163 @LazyProperty
171 @LazyProperty
164 def raw_id(self):
172 def raw_id(self):
165 """
173 """
166 Returns raw string identifing this changeset, useful for web
174 Returns raw string identifing this changeset, useful for web
167 representation.
175 representation.
168 """
176 """
169 return '0' * 12
177 return '0' * 12
170
178
171
179
172 def repo2db_mapper(initial_repo_list):
180 def repo2db_mapper(initial_repo_list):
173 """
181 """
174 maps all found repositories into db
182 maps all found repositories into db
175 """
183 """
176 from pylons_app.model.meta import Session
184 from pylons_app.model.meta import Session
177 from pylons_app.model.repo_model import RepoModel
185 from pylons_app.model.repo_model import RepoModel
178
186
179 sa = Session()
187 sa = Session()
180 user = sa.query(User).filter(User.admin == True).first()
188 user = sa.query(User).filter(User.admin == True).first()
181
189
182 rm = RepoModel()
190 rm = RepoModel()
183
191
184 for name, repo in initial_repo_list.items():
192 for name, repo in initial_repo_list.items():
185 if not sa.query(Repository).get(name):
193 if not sa.query(Repository).get(name):
186 log.info('repository %s not found creating default', name)
194 log.info('repository %s not found creating default', name)
187
195
188 form_data = {
196 form_data = {
189 'repo_name':name,
197 'repo_name':name,
190 'description':repo.description if repo.description != 'unknown' else \
198 'description':repo.description if repo.description != 'unknown' else \
191 'auto description for %s' % name,
199 'auto description for %s' % name,
192 'private':False
200 'private':False
193 }
201 }
194 rm.create(form_data, user, just_db=True)
202 rm.create(form_data, user, just_db=True)
General Comments 0
You need to be logged in to leave comments. Login now