##// END OF EJS Templates
Updated readme
marcink -
r325:d8d471cf default
parent child Browse files
Show More
@@ -1,46 +1,56 b''
1 1 -------------------------------------
2 2 Pylons based replacement for hgwebdir
3 3 -------------------------------------
4 4
5 5 Fully customizable, with authentication, permissions. Based on vcs library.
6 6
7 7 **Overview**
8 8
9 9 - has it's own middleware to handle mercurial protocol request each request can
10 10 be logged and authenticated + threaded performance unlikely to hgweb
11 - full permissions per project read/write/admin access even on mercurial request
11 12 - mako templates let's you cusmotize look and feel of appplication.
12 13 - diffs annotations and source code all colored by pygments.
13 14 - mercurial branch graph
14 15 - admin interface for performing user/permission managments as well as repository
15 16 managment
17 - backup scripts can do backup of whole app and send it over scp to desired location
16 18 - setup project descriptions and info inside built in db for easy, non
17 19 file-system operations
18 20 - added cache with invalidation on push/repo managment for high performance and
19 21 always upto date data.
20 22 - rss /atom feed customizable
21 23 - future support for git
22 24 - based on pylons 1.0 / sqlalchemy 0.6
23 25
24 26 **Incoming**
25 27
26 - full permissions per project
28 - code review based on hg-review (when it's stable)
27 29 - git support (when vcs can handle it)
30 - other cools stuff that i can figure out
28 31
29 32 .. note::
30 33 This software is still in beta mode. I don't guarantee that it'll work.
31 34
32 35
33 36 -------------
34 37 Installation
35 38 -------------
39 .. note::
40 I recomend to install tip version of vcs while the app is in beta mode.
41
42
36 43 - create new virtualenv and activate it
37 - download hg app and run python setup.py install
38 - goto build/ directory
44 - download hg app from default (not demo) branch from bitbucket and run
45 'python setup.py install' this will install all required dependencies needed
39 46 - goto pylons_app/lib and run python db_manage.py it should create all
40 needed tables and an admin account.
47 needed tables and an admin account. You can play with this file if you wish to
48 use different db than sqlite
41 49 - edit file repositories.config and change the [paths] where you keep your
42 50 mercurial repositories, remember about permissions for accessing this dir by
43 51 hg app.
44 52 - run paster serve development.ini
45 53 the app should be available at the 127.0.0.1:5000
46 - use admin account you created to login. No newline at end of file
54 - use admin account you created to login.
55 - default permissions on each repository is read, and owner is admin. So remember
56 to update those.
@@ -1,158 +1,158 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 from sqlalchemy.orm import joinedload
6 5
7 6 # This program is free software; you can redistribute it and/or
8 7 # modify it under the terms of the GNU General Public License
9 8 # as published by the Free Software Foundation; version 2
10 9 # of the License or (at your opinion) any later version of the license.
11 10 #
12 11 # This program is distributed in the hope that it will be useful,
13 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 14 # GNU General Public License for more details.
16 15 #
17 16 # You should have received a copy of the GNU General Public License
18 17 # along with this program; if not, write to the Free Software
19 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 19 # MA 02110-1301, USA.
21 20
22 21 """
23 22 Created on April 9, 2010
24 23 Model for hg app
25 24 @author: marcink
26 25 """
27 26
28 27 from beaker.cache import cache_region
29 28 from mercurial import ui
30 29 from mercurial.hgweb.hgwebdir_mod import findrepos
31 30 from vcs.exceptions import RepositoryError, VCSError
32 31 from pylons_app.model.meta import Session
33 32 from pylons_app.model.db import Repository
33 from sqlalchemy.orm import joinedload
34 34 import logging
35 35 import os
36 36 import sys
37 37 log = logging.getLogger(__name__)
38 38
39 39 try:
40 40 from vcs.backends.hg import MercurialRepository
41 41 except ImportError:
42 42 sys.stderr.write('You have to import vcs module')
43 43 raise Exception('Unable to import vcs')
44 44
45 45 def _get_repos_cached_initial(app_globals):
46 46 """
47 47 return cached dict with repos
48 48 """
49 49 g = app_globals
50 50 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
51 51
52 52 @cache_region('long_term', 'cached_repo_list')
53 53 def _get_repos_cached():
54 54 """
55 55 return cached dict with repos
56 56 """
57 57 log.info('getting all repositories list')
58 58 from pylons import app_globals as g
59 59 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
60 60
61 61 @cache_region('long_term', 'full_changelog')
62 62 def _full_changelog_cached(repo_name):
63 63 log.info('getting full changelog for %s', repo_name)
64 64 return list(reversed(list(HgModel().get_repo(repo_name))))
65 65
66 66 class HgModel(object):
67 67 """
68 68 Mercurial Model
69 69 """
70 70
71 71 def __init__(self):
72 72 """
73 73 Constructor
74 74 """
75 75
76 76 @staticmethod
77 77 def repo_scan(repos_prefix, repos_path, baseui):
78 78 """
79 79 Listing of repositories in given path. This path should not be a
80 80 repository itself. Return a dictionary of repository objects
81 81 :param repos_path: path to directory it could take syntax with
82 82 * or ** for deep recursive displaying repositories
83 83 """
84 84 sa = Session()
85 85 def check_repo_dir(path):
86 86 """
87 87 Checks the repository
88 88 :param path:
89 89 """
90 90 repos_path = path.split('/')
91 91 if repos_path[-1] in ['*', '**']:
92 92 repos_path = repos_path[:-1]
93 93 if repos_path[0] != '/':
94 94 repos_path[0] = '/'
95 95 if not os.path.isdir(os.path.join(*repos_path)):
96 96 raise RepositoryError('Not a valid repository in %s' % path[0][1])
97 97 if not repos_path.endswith('*'):
98 98 raise VCSError('You need to specify * or ** at the end of path '
99 99 'for recursive scanning')
100 100
101 101 check_repo_dir(repos_path)
102 102 log.info('scanning for repositories in %s', repos_path)
103 103 repos = findrepos([(repos_prefix, repos_path)])
104 104 if not isinstance(baseui, ui.ui):
105 105 baseui = ui.ui()
106 106
107 107 repos_list = {}
108 108 for name, path in repos:
109 109 try:
110 110 #name = name.split('/')[-1]
111 111 if repos_list.has_key(name):
112 112 raise RepositoryError('Duplicate repository name %s found in'
113 113 ' %s' % (name, path))
114 114 else:
115 115
116 116 repos_list[name] = MercurialRepository(path, baseui=baseui)
117 117 repos_list[name].name = name
118 118 dbrepo = sa.query(Repository).get(name)
119 119 if dbrepo:
120 120 repos_list[name].dbrepo = dbrepo
121 121 repos_list[name].description = dbrepo.description
122 122 repos_list[name].contact = dbrepo.user.full_contact
123 123 except OSError:
124 124 continue
125 125 return repos_list
126 126
127 127 def get_repos(self):
128 128 for name, repo in _get_repos_cached().items():
129 129 if repo._get_hidden():
130 130 #skip hidden web repository
131 131 continue
132 132
133 133 last_change = repo.last_change
134 134 try:
135 135 tip = repo.get_changeset('tip')
136 136 except RepositoryError:
137 137 from pylons_app.lib.utils import EmptyChangeset
138 138 tip = EmptyChangeset()
139 139
140 140 tmp_d = {}
141 141 tmp_d['name'] = repo.name
142 142 tmp_d['name_sort'] = tmp_d['name'].lower()
143 143 tmp_d['description'] = repo.description
144 144 tmp_d['description_sort'] = tmp_d['description']
145 145 tmp_d['last_change'] = last_change
146 146 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
147 147 tmp_d['tip'] = tip.raw_id
148 148 tmp_d['tip_sort'] = tip.revision
149 149 tmp_d['rev'] = tip.revision
150 150 tmp_d['contact'] = repo.contact
151 151 tmp_d['contact_sort'] = tmp_d['contact']
152 152 tmp_d['repo_archives'] = list(repo._get_archives())
153 153 tmp_d['last_msg'] = tip.message
154 154 tmp_d['repo'] = repo
155 155 yield tmp_d
156 156
157 157 def get_repo(self, repo_name):
158 158 return _get_repos_cached()[repo_name]
@@ -1,43 +1,43 b''
1 1 from pylons_app import get_version
2 2 try:
3 3 from setuptools import setup, find_packages
4 4 except ImportError:
5 5 from ez_setup import use_setuptools
6 6 use_setuptools()
7 7 from setuptools import setup, find_packages
8 8
9 9 setup(
10 10 name='pylons_app',
11 11 version=get_version(),
12 12 description='',
13 13 author='marcin kuzminski',
14 14 author_email='marcin@python-works.com',
15 url='',
15 url='http://hg.python-works.com',
16 16 install_requires=[
17 17 "Pylons>=1.0.0",
18 18 "SQLAlchemy>=0.6",
19 19 "Mako>=0.3.2",
20 20 "vcs>=0.1.2",
21 21 "pygments>=1.3.0",
22 22 "mercurial>=1.5",
23 23 "pysqlite"
24 24 ],
25 25 setup_requires=["PasteScript>=1.6.3"],
26 26 packages=find_packages(exclude=['ez_setup']),
27 27 include_package_data=True,
28 28 test_suite='nose.collector',
29 29 package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']},
30 30 message_extractors={'pylons_app': [
31 31 ('**.py', 'python', None),
32 32 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
33 33 ('public/**', 'ignore', None)]},
34 34 zip_safe=False,
35 35 paster_plugins=['PasteScript', 'Pylons'],
36 36 entry_points="""
37 37 [paste.app_factory]
38 38 main = pylons_app.config.middleware:make_app
39 39
40 40 [paste.app_install]
41 41 main = pylons.util:PylonsInstaller
42 42 """,
43 43 )
General Comments 0
You need to be logged in to leave comments. Login now