##// END OF EJS Templates
cleanup-repos: reintroduce paster command...
Mads Kiilerich -
r4560:b1679034 default
parent child Browse files
Show More
@@ -0,0 +1,155 b''
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
15 kallithea.lib.paster_commands.cleanup
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
18 cleanup-repos paster command for Kallithea
19
20
21 This file was forked by the Kallithea project in July 2014.
22 Original author and date, and relevant copyright and licensing information is below:
23 :created_on: Jul 14, 2012
24 :author: marcink
25 :copyright: (c) 2013 RhodeCode GmbH.
26 :license: GPLv3, see LICENSE.md for more details.
27 """
28
29 from __future__ import with_statement
30
31 import os
32 import sys
33 import re
34 import shutil
35 import logging
36 import datetime
37
38 from kallithea.lib.utils import BasePasterCommand, ask_ok, REMOVED_REPO_PAT
39 from kallithea.lib.utils2 import safe_str
40 from kallithea.model.db import Ui
41
42 # Add location of top level folder to sys.path
43 from os.path import dirname as dn
44 rc_path = dn(dn(dn(os.path.realpath(__file__))))
45 sys.path.append(rc_path)
46
47 log = logging.getLogger(__name__)
48
49
50 class Command(BasePasterCommand):
51
52 max_args = 1
53 min_args = 1
54
55 usage = "CONFIG_FILE"
56 group_name = "Kallithea"
57 takes_config_file = -1
58 parser = BasePasterCommand.standard_parser(verbose=True)
59 summary = "Cleanup deleted repos"
60
61 def _parse_older_than(self, val):
62 regex = re.compile(r'((?P<days>\d+?)d)?((?P<hours>\d+?)h)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')
63 parts = regex.match(val)
64 if not parts:
65 return
66 parts = parts.groupdict()
67 time_params = {}
68 for (name, param) in parts.iteritems():
69 if param:
70 time_params[name] = int(param)
71 return datetime.timedelta(**time_params)
72
73 def _extract_date(self, name):
74 """
75 Extract the date part from rm__<date> pattern of removed repos,
76 and convert it to datetime object
77
78 :param name:
79 """
80 date_part = name[4:19] # 4:19 since we don't parse milisecods
81 return datetime.datetime.strptime(date_part, '%Y%m%d_%H%M%S')
82
83 def command(self):
84 #get SqlAlchemy session
85 self._init_session()
86
87 repos_location = Ui.get_repos_location()
88 to_remove = []
89 for dn, dirs, f in os.walk(safe_str(repos_location)):
90 alldirs = list(dirs)
91 del dirs[:]
92 if ('.hg' in alldirs or
93 'objects' in alldirs and ('refs' in alldirs or 'packed-refs' in f)):
94 continue
95 for loc in alldirs:
96 if REMOVED_REPO_PAT.match(loc):
97 to_remove.append([os.path.join(dn, loc),
98 self._extract_date(loc)])
99 else:
100 dirs.append(loc)
101
102 #filter older than (if present)!
103 now = datetime.datetime.now()
104 older_than = self.options.older_than
105 if older_than:
106 to_remove_filtered = []
107 older_than_date = self._parse_older_than(older_than)
108 for name, date_ in to_remove:
109 repo_age = now - date_
110 if repo_age > older_than_date:
111 to_remove_filtered.append([name, date_])
112
113 to_remove = to_remove_filtered
114 print >> sys.stdout, 'removing %s deleted repos older than %s (%s)' \
115 % (len(to_remove), older_than, older_than_date)
116 else:
117 print >> sys.stdout, 'removing all [%s] deleted repos' \
118 % len(to_remove)
119 if self.options.dont_ask or not to_remove:
120 # don't ask just remove !
121 remove = True
122 else:
123 remove = ask_ok('the following repositories will be deleted completely:\n%s\n'
124 'are you sure you want to remove them [y/n]?'
125 % ', \n'.join(['%s removed on %s'
126 % (safe_str(x[0]), safe_str(x[1])) for x in to_remove]))
127
128 if remove:
129 for path, date_ in to_remove:
130 print >> sys.stdout, 'removing repository %s' % path
131 shutil.rmtree(path)
132 else:
133 print 'nothing done exiting...'
134 sys.exit(0)
135
136 def update_parser(self):
137 self.parser.add_option(
138 '--older-than',
139 action='store',
140 dest='older_than',
141 help=("only remove repos that have been removed "
142 "at least given time ago. "
143 "The default is to remove all removed repositories. "
144 "Possible suffixes: "
145 "d (days), h (hours), m (minutes), s (seconds). "
146 "For example --older-than=30d deletes repositories "
147 "removed more than 30 days ago.")
148 )
149
150 self.parser.add_option(
151 '--dont-ask',
152 action="store_true",
153 dest="dont_ask",
154 help="remove repositories without asking for confirmation."
155 )
@@ -1,185 +1,186 b''
1 #!/usr/bin/env python2
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
2 # -*- coding: utf-8 -*-
3 import os
3 import os
4 import sys
4 import sys
5 import platform
5 import platform
6
6
7 if sys.version_info < (2, 6):
7 if sys.version_info < (2, 6):
8 raise Exception('Kallithea requires python 2.6 or 2.7')
8 raise Exception('Kallithea requires python 2.6 or 2.7')
9
9
10
10
11 here = os.path.abspath(os.path.dirname(__file__))
11 here = os.path.abspath(os.path.dirname(__file__))
12
12
13
13
14 def _get_meta_var(name, data, callback_handler=None):
14 def _get_meta_var(name, data, callback_handler=None):
15 import re
15 import re
16 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
16 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
17 if matches:
17 if matches:
18 if not callable(callback_handler):
18 if not callable(callback_handler):
19 callback_handler = lambda v: v
19 callback_handler = lambda v: v
20
20
21 return callback_handler(eval(matches.groups()[0]))
21 return callback_handler(eval(matches.groups()[0]))
22
22
23 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'rb')
23 _meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'rb')
24 _metadata = _meta.read()
24 _metadata = _meta.read()
25 _meta.close()
25 _meta.close()
26
26
27 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
27 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
28 __version__ = _get_meta_var('VERSION', _metadata, callback)
28 __version__ = _get_meta_var('VERSION', _metadata, callback)
29 __license__ = _get_meta_var('__license__', _metadata)
29 __license__ = _get_meta_var('__license__', _metadata)
30 __author__ = _get_meta_var('__author__', _metadata)
30 __author__ = _get_meta_var('__author__', _metadata)
31 __url__ = _get_meta_var('__url__', _metadata)
31 __url__ = _get_meta_var('__url__', _metadata)
32 # defines current platform
32 # defines current platform
33 __platform__ = platform.system()
33 __platform__ = platform.system()
34
34
35 is_windows = __platform__ in ['Windows']
35 is_windows = __platform__ in ['Windows']
36
36
37 requirements = [
37 requirements = [
38 "waitress==0.8.8",
38 "waitress==0.8.8",
39 "webob==1.0.8",
39 "webob==1.0.8",
40 "webtest==1.4.3",
40 "webtest==1.4.3",
41 "Pylons==1.0.0",
41 "Pylons==1.0.0",
42 "Beaker==1.6.4",
42 "Beaker==1.6.4",
43 "WebHelpers==1.3",
43 "WebHelpers==1.3",
44 "formencode>=1.2.4,<=1.2.6",
44 "formencode>=1.2.4,<=1.2.6",
45 "SQLAlchemy==0.7.10",
45 "SQLAlchemy==0.7.10",
46 "Mako>=0.9.0,<=1.0.0",
46 "Mako>=0.9.0,<=1.0.0",
47 "pygments>=1.5",
47 "pygments>=1.5",
48 "whoosh>=2.4.0,<=2.5.7",
48 "whoosh>=2.4.0,<=2.5.7",
49 "celery>=2.2.5,<2.3",
49 "celery>=2.2.5,<2.3",
50 "babel>=0.9.6,<=1.3",
50 "babel>=0.9.6,<=1.3",
51 "python-dateutil>=1.5.0,<2.0.0",
51 "python-dateutil>=1.5.0,<2.0.0",
52 "markdown==2.2.1",
52 "markdown==2.2.1",
53 "docutils>=0.8.1,<=0.11",
53 "docutils>=0.8.1,<=0.11",
54 "simplejson==2.5.2",
54 "simplejson==2.5.2",
55 "mock",
55 "mock",
56 "pycrypto>=2.6.0,<=2.6.1",
56 "pycrypto>=2.6.0,<=2.6.1",
57 "URLObject==2.3.4",
57 "URLObject==2.3.4",
58 "Routes==1.13",
58 "Routes==1.13",
59 ]
59 ]
60
60
61 if sys.version_info < (2, 7):
61 if sys.version_info < (2, 7):
62 requirements.append("importlib==1.0.1")
62 requirements.append("importlib==1.0.1")
63 requirements.append("unittest2")
63 requirements.append("unittest2")
64 requirements.append("argparse")
64 requirements.append("argparse")
65
65
66 if is_windows:
66 if is_windows:
67 requirements.append("mercurial>=2.8.2,<3.2")
67 requirements.append("mercurial>=2.8.2,<3.2")
68 else:
68 else:
69 requirements.append("py-bcrypt>=0.3.0,<=0.4")
69 requirements.append("py-bcrypt>=0.3.0,<=0.4")
70 requirements.append("mercurial>=2.8.2,<3.2")
70 requirements.append("mercurial>=2.8.2,<3.2")
71
71
72 if sys.version_info < (2, 7):
72 if sys.version_info < (2, 7):
73 # Dulwich 0.9.6 and later do not support Python2.6.
73 # Dulwich 0.9.6 and later do not support Python2.6.
74 requirements.append("dulwich>=0.9.3,<=0.9.5")
74 requirements.append("dulwich>=0.9.3,<=0.9.5")
75 else:
75 else:
76 requirements.append("dulwich>=0.9.3,<=0.9.7")
76 requirements.append("dulwich>=0.9.3,<=0.9.7")
77
77
78 dependency_links = [
78 dependency_links = [
79 ]
79 ]
80
80
81 classifiers = [
81 classifiers = [
82 'Development Status :: 4 - Beta',
82 'Development Status :: 4 - Beta',
83 'Environment :: Web Environment',
83 'Environment :: Web Environment',
84 'Framework :: Pylons',
84 'Framework :: Pylons',
85 'Intended Audience :: Developers',
85 'Intended Audience :: Developers',
86 'License :: OSI Approved :: GNU General Public License (GPL)',
86 'License :: OSI Approved :: GNU General Public License (GPL)',
87 'Operating System :: OS Independent',
87 'Operating System :: OS Independent',
88 'Programming Language :: Python',
88 'Programming Language :: Python',
89 'Programming Language :: Python :: 2.6',
89 'Programming Language :: Python :: 2.6',
90 'Programming Language :: Python :: 2.7',
90 'Programming Language :: Python :: 2.7',
91 'Topic :: Software Development :: Version Control',
91 'Topic :: Software Development :: Version Control',
92 ]
92 ]
93
93
94
94
95 # additional files from project that goes somewhere in the filesystem
95 # additional files from project that goes somewhere in the filesystem
96 # relative to sys.prefix
96 # relative to sys.prefix
97 data_files = []
97 data_files = []
98
98
99 # additional files that goes into package itself
99 # additional files that goes into package itself
100 package_data = {'kallithea': ['i18n/*/LC_MESSAGES/*.mo', ], }
100 package_data = {'kallithea': ['i18n/*/LC_MESSAGES/*.mo', ], }
101
101
102 description = ('Kallithea is a fast and powerful management tool '
102 description = ('Kallithea is a fast and powerful management tool '
103 'for Mercurial and GIT with a built in push/pull server, '
103 'for Mercurial and GIT with a built in push/pull server, '
104 'full text search and code-review.')
104 'full text search and code-review.')
105
105
106 keywords = ' '.join([
106 keywords = ' '.join([
107 'kallithea', 'mercurial', 'git', 'code review',
107 'kallithea', 'mercurial', 'git', 'code review',
108 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
108 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
109 'hgwebdir', 'gitweb replacement', 'serving hgweb',
109 'hgwebdir', 'gitweb replacement', 'serving hgweb',
110 ])
110 ])
111
111
112 # long description
112 # long description
113 README_FILE = 'README.rst'
113 README_FILE = 'README.rst'
114 CHANGELOG_FILE = 'docs/changelog.rst'
114 CHANGELOG_FILE = 'docs/changelog.rst'
115 try:
115 try:
116 long_description = open(README_FILE).read() + '\n\n' + \
116 long_description = open(README_FILE).read() + '\n\n' + \
117 open(CHANGELOG_FILE).read()
117 open(CHANGELOG_FILE).read()
118
118
119 except IOError, err:
119 except IOError, err:
120 sys.stderr.write(
120 sys.stderr.write(
121 "[WARNING] Cannot find file specified as long_description (%s)\n or "
121 "[WARNING] Cannot find file specified as long_description (%s)\n or "
122 "changelog (%s) skipping that file" % (README_FILE, CHANGELOG_FILE)
122 "changelog (%s) skipping that file" % (README_FILE, CHANGELOG_FILE)
123 )
123 )
124 long_description = description
124 long_description = description
125
125
126 try:
126 try:
127 from setuptools import setup, find_packages
127 from setuptools import setup, find_packages
128 except ImportError:
128 except ImportError:
129 from ez_setup import use_setuptools
129 from ez_setup import use_setuptools
130 use_setuptools()
130 use_setuptools()
131 from setuptools import setup, find_packages
131 from setuptools import setup, find_packages
132 # packages
132 # packages
133 packages = find_packages(exclude=['ez_setup'])
133 packages = find_packages(exclude=['ez_setup'])
134
134
135 setup(
135 setup(
136 name='Kallithea',
136 name='Kallithea',
137 version=__version__,
137 version=__version__,
138 description=description,
138 description=description,
139 long_description=long_description,
139 long_description=long_description,
140 keywords=keywords,
140 keywords=keywords,
141 license=__license__,
141 license=__license__,
142 author=__author__,
142 author=__author__,
143 author_email='kallithea@sfconservancy.org',
143 author_email='kallithea@sfconservancy.org',
144 dependency_links=dependency_links,
144 dependency_links=dependency_links,
145 url=__url__,
145 url=__url__,
146 install_requires=requirements,
146 install_requires=requirements,
147 classifiers=classifiers,
147 classifiers=classifiers,
148 setup_requires=["PasteScript>=1.6.3"],
148 setup_requires=["PasteScript>=1.6.3"],
149 data_files=data_files,
149 data_files=data_files,
150 packages=packages,
150 packages=packages,
151 include_package_data=True,
151 include_package_data=True,
152 test_suite='nose.collector',
152 test_suite='nose.collector',
153 package_data=package_data,
153 package_data=package_data,
154 message_extractors={'kallithea': [
154 message_extractors={'kallithea': [
155 ('**.py', 'python', None),
155 ('**.py', 'python', None),
156 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
156 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
157 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
157 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
158 ('public/**', 'ignore', None)]},
158 ('public/**', 'ignore', None)]},
159 zip_safe=False,
159 zip_safe=False,
160 paster_plugins=['PasteScript', 'Pylons'],
160 paster_plugins=['PasteScript', 'Pylons'],
161 entry_points="""
161 entry_points="""
162 [console_scripts]
162 [console_scripts]
163 kallithea-api = kallithea.bin.kallithea_api:main
163 kallithea-api = kallithea.bin.kallithea_api:main
164 kallithea-gist = kallithea.bin.kallithea_gist:main
164 kallithea-gist = kallithea.bin.kallithea_gist:main
165 kallithea-config = kallithea.bin.kallithea_config:main
165 kallithea-config = kallithea.bin.kallithea_config:main
166
166
167 [paste.app_factory]
167 [paste.app_factory]
168 main = kallithea.config.middleware:make_app
168 main = kallithea.config.middleware:make_app
169
169
170 [paste.app_install]
170 [paste.app_install]
171 main = pylons.util:PylonsInstaller
171 main = pylons.util:PylonsInstaller
172
172
173 [paste.global_paster_command]
173 [paste.global_paster_command]
174 setup-db=kallithea.lib.paster_commands.setup_db:Command
174 setup-db=kallithea.lib.paster_commands.setup_db:Command
175 cleanup-repos=kallithea.lib.paster_commands.cleanup:Command
175 update-repoinfo=kallithea.lib.paster_commands.update_repoinfo:Command
176 update-repoinfo=kallithea.lib.paster_commands.update_repoinfo:Command
176 make-rcext=kallithea.lib.paster_commands.make_rcextensions:Command
177 make-rcext=kallithea.lib.paster_commands.make_rcextensions:Command
177 repo-scan=kallithea.lib.paster_commands.repo_scan:Command
178 repo-scan=kallithea.lib.paster_commands.repo_scan:Command
178 cache-keys=kallithea.lib.paster_commands.cache_keys:Command
179 cache-keys=kallithea.lib.paster_commands.cache_keys:Command
179 ishell=kallithea.lib.paster_commands.ishell:Command
180 ishell=kallithea.lib.paster_commands.ishell:Command
180 make-index=kallithea.lib.paster_commands.make_index:Command
181 make-index=kallithea.lib.paster_commands.make_index:Command
181 upgrade-db=kallithea.lib.dbmigrate:UpgradeDb
182 upgrade-db=kallithea.lib.dbmigrate:UpgradeDb
182 celeryd=kallithea.lib.celerypylons.commands:CeleryDaemonCommand
183 celeryd=kallithea.lib.celerypylons.commands:CeleryDaemonCommand
183 install-iis=kallithea.lib.paster_commands.install_iis:Command
184 install-iis=kallithea.lib.paster_commands.install_iis:Command
184 """,
185 """,
185 )
186 )
General Comments 0
You need to be logged in to leave comments. Login now