##// END OF EJS Templates
merge with beta
marcink -
r2361:948c16bb merge codereview
parent child Browse files
Show More
@@ -0,0 +1,244
1 .. _installation_win:
2
3
4 Step by step Installation for Windows
5 =====================================
6
7
8 RhodeCode step-by-step install Guide for Windows
9
10 Target OS: Windows XP SP3 English (Clean installation)
11 + All Windows Updates until 24-may-2012
12
13 Step1 - Install Visual Studio 2008 Express
14 ------------------------------------------
15
16
17 Optional: You can also install MingW, but VS2008 installation is easier
18
19 Download "Visual C++ 2008 Express Edition with SP1" from:
20 http://www.microsoft.com/visualstudio/en-us/products/2008-editions/express
21 (if not found or relocated, google for "visual studio 2008 express" for
22 updated link)
23
24 You can also download full ISO file for offline installation, just
25 choose "All - Offline Install ISO image file" in the previous page and
26 choose "Visual C++ 2008 Express" when installing.
27
28
29 .. note::
30
31 Silverlight Runtime and SQL Server 2008 Express Edition are not
32 required, you can uncheck them
33
34
35 Step2 - Install Python
36 ----------------------
37
38 Install Python 2.x.y (x >= 5) x86 version (32bit). DO NOT USE A 3.x version.
39 Download Python 2.x.y from:
40 http://www.python.org/download/
41
42 Choose "Windows Installer" (32bit version) not "Windows X86-64
43 Installer". While writing this guide, the latest version was v2.7.3.
44 Remember the specific major and minor version installed, because it will
45 be needed in the next step. In this case, it is "2.7".
46
47
48 Step3 - Install Win32py extensions
49 ----------------------------------
50
51 Download pywin32 from:
52 http://sourceforge.net/projects/pywin32/files/
53
54 - Click on "pywin32" folder
55 - Click on the first folder (in this case, Build 217, maybe newer when you try)
56 - Choose the file ending with ".win32-py2.x.exe" -> x being the minor
57 version of Python you installed (in this case, 7)
58 When writing this guide, the file was:
59 http://sourceforge.net/projects/pywin32/files/pywin32/Build%20217/pywin32-217.win32-py2.7.exe/download
60
61
62 Step4 - Python BIN
63 ------------------
64
65 Add Python BIN folder to the path
66
67 You have to add the Python folder to the path, you can do it manually
68 (editing "PATH" environment variable) or using Windows Support Tools
69 that came preinstalled in Vista/7 and can be installed in Windows XP.
70
71 - Using support tools on WINDOWS XP:
72 If you use Windows XP you can install them using Windows XP CD and
73 navigating to \SUPPORT\TOOLS. There, execute Setup.EXE (not MSI).
74 Afterwards, open a CMD and type::
75
76 SETX PATH "%PATH%;[your-python-path]" -M
77
78 Close CMD (the path variable will be updated then)
79
80 - Using support tools on WINDOWS Vista/7:
81
82 Open a CMD and type::
83
84 SETX PATH "%PATH%;[your-python-path]" /M
85
86 Please substitute [your-python-path] with your Python installation path.
87 Typically: C:\\Python27
88
89
90 Step5 - RhodeCode folder structure
91 ----------------------------------
92
93 Create a RhodeCode folder structure
94
95 This is only a example to install RhodeCode, you can of course change
96 it. However, this guide will follow the proposed structure, so please
97 later adapt the paths if you change them. My recommendation is to use
98 folders with NO SPACES. But you can try if you are brave...
99
100 Create the following folder structure::
101
102 C:\RhodeCode
103 C:\RhodeCode\Bin
104 C:\RhodeCode\Env
105 C:\RhodeCode\Repos
106
107
108 Step6 - Install virtualenv
109 ---------------------------
110
111 Install Virtual Env for Python
112
113 Navigate to: http://www.virtualenv.org/en/latest/index.html#installation
114 Right click on "virtualenv.py" file and choose "Save link as...".
115 Download to C:\\RhodeCode (or whatever you want)
116 (the file is located at
117 https://raw.github.com/pypa/virtualenv/master/virtualenv.py)
118
119 Create a virtual Python environment in C:\\RhodeCode\\Env (or similar). To
120 do so, open a CMD (Python Path should be included in Step3), navigate
121 where you downloaded "virtualenv.py", and write::
122
123 python virtualenv.py C:\RhodeCode\Env
124
125 (--no-site-packages is now the default behaviour of virtualenv, no need
126 to include it)
127
128
129 Step7 - Install RhodeCode
130 -------------------------
131
132 Finally, install RhodeCode
133
134 Close previously opened command prompt/s, and open a Visual Studio 2008
135 Command Prompt (**IMPORTANT!!**). To do so, go to Start Menu, and then open
136 "Microsoft Visual C++ 2008 Express Edition" -> "Visual Studio Tools" ->
137 "Visual Studio 2008 Command Prompt"
138
139 In that CMD (loaded with VS2008 PATHs) type::
140
141 cd C:\RhodeCode\Env\Scripts (or similar)
142 activate
143
144 The prompt will change into "(Env) C:\\RhodeCode\\Env\\Scripts" or similar
145 (depending of your folder structure). Then type::
146
147 pip install rhodecode
148
149 (long step, please wait until fully complete)
150
151 Some warnings will appear, don't worry as they are normal.
152
153
154 Step8 - Configuring RhodeCode
155 -----------------------------
156
157
158 steps taken from http://packages.python.org/RhodeCode/setup.html
159
160 You have to use the same Visual Studio 2008 command prompt as Step7, so
161 if you closed it reopen it following the same commands (including the
162 "activate" one). When ready, just type::
163
164 cd C:\RhodeCode\Bin
165 paster make-config RhodeCode production.ini
166
167 Then, you must edit production.ini to fit your needs (ip address, ip
168 port, mail settings, database, whatever). I recommend using NotePad++
169 (free) or similar text editor, as it handles well the EndOfLine
170 character differences between Unix and Windows
171 (http://notepad-plus-plus.org/)
172
173 For the sake of simplicity lets run it with the default settings. After
174 your edits (if any), in the previous Command Prompt, type::
175
176 paster setup-rhodecode production.ini
177
178 (this time a NEW database will be installed, you must follow a different
179 step to later UPGRADE to a newer RhodeCode version)
180
181 The script will ask you for confirmation about creating a NEW database,
182 answer yes (y)
183 The script will ask you for repository path, answer C:\\RhodeCode\\Repos
184 (or similar)
185 The script will ask you for admin username and password, answer "admin"
186 + "123456" (or whatever you want)
187 The script will ask you for admin mail, answer "admin@xxxx.com" (or
188 whatever you want)
189
190 If you make some mistake and the script does not end, don't worry, start
191 it again.
192
193
194 Step9 - Running RhodeCode
195 -------------------------
196
197
198 In the previous command prompt, being in the C:\\RhodeCode\\Bin folder,
199 just type::
200
201 paster serve production.ini
202
203 Open yout web server, and go to http://127.0.0.1:5000
204
205 It works!! :-)
206
207 Remark:
208 If it does not work first time, just Ctrl-C the CMD process and start it
209 again. Don't forget the "http://" in Internet Explorer
210
211
212
213 What this Guide does not cover:
214
215 - Installing Celery
216 - Running RhodeCode as Windows Service. You can investigate here:
217
218 - http://pypi.python.org/pypi/wsgisvc
219 - http://ryrobes.com/python/running-python-scripts-as-a-windows-service/
220 - http://wiki.pylonshq.com/display/pylonscookbook/How+to+run+Pylons+as+a+Windows+service
221
222 - Using Apache. You can investigate here:
223
224 - https://groups.google.com/group/rhodecode/msg/c433074e813ffdc4
225
226
227 Upgrading
228 =========
229
230 Stop running RhodeCode
231 Open a CommandPrompt like in Step7 (VS2008 path + activate) and type::
232
233 easy_install -U rhodecode
234 cd \RhodeCode\Bin
235
236 { backup your production.ini file now} ::
237
238 paster make-config RhodeCode production.ini
239
240 (check changes and update your production.ini accordingly) ::
241
242 paster upgrade-db production.ini (update database)
243
244 Full steps in http://packages.python.org/RhodeCode/upgrade.html No newline at end of file
@@ -1,124 +1,129
1 1 .. _installation:
2 2
3 3 ============
4 4 Installation
5 5 ============
6 6
7 7 ``RhodeCode`` is written entirely in Python. Before posting any issues make
8 8 sure, your not missing any system libraries and using right version of
9 9 libraries required by RhodeCode. There's also restriction in terms of mercurial
10 10 clients. Minimal version of hg client known working fine with RhodeCode is
11 11 **1.6**. If you're using older client, please upgrade.
12 12
13 13
14 14 Installing RhodeCode from Cheese Shop
15 15 -------------------------------------
16 16
17 17 Rhodecode requires python version 2.5 or higher.
18 18
19 19 The easiest way to install ``rhodecode`` is to run::
20 20
21 21 easy_install rhodecode
22 22
23 23 Or::
24 24
25 25 pip install rhodecode
26 26
27 27 If you prefer to install RhodeCode manually simply grab latest release from
28 28 http://pypi.python.org/pypi/RhodeCode, decompress the archive and run::
29 29
30 30 python setup.py install
31 31
32 Step by step installation example for Windows
33 ---------------------------------------------
32 34
33 Step by step installation example
34 ---------------------------------
35 :ref:`installation_win`
36
37
38 Step by step installation example for Linux
39 -------------------------------------------
35 40
36 41
37 42 For installing RhodeCode i highly recommend using separate virtualenv_. This
38 43 way many required by RhodeCode libraries will remain sandboxed from your main
39 44 python and making things less problematic when doing system python updates.
40 45
41 46 - Assuming you have installed virtualenv_ create a new virtual environment
42 47 using virtualenv command::
43 48
44 49 virtualenv --no-site-packages /var/www/rhodecode-venv
45 50
46 51
47 52 .. note:: Using ``--no-site-packages`` when generating your
48 53 virtualenv is **very important**. This flag provides the necessary
49 54 isolation for running the set of packages required by
50 55 RhodeCode. If you do not specify ``--no-site-packages``,
51 56 it's possible that RhodeCode will not install properly into
52 57 the virtualenv, or, even if it does, may not run properly,
53 58 depending on the packages you've already got installed into your
54 59 Python's "main" site-packages dir.
55 60
56 61
57 62 - this will install new virtualenv_ into `/var/www/rhodecode-venv`.
58 63 - Activate the virtualenv_ by running::
59 64
60 65 source /var/www/rhodecode-venv/bin/activate
61 66
62 67 .. note:: If you're using UNIX, *do not* use ``sudo`` to run the
63 68 ``virtualenv`` script. It's perfectly acceptable (and desirable)
64 69 to create a virtualenv as a normal user.
65 70
66 71 - Make a folder for rhodecode data files, and configuration somewhere on the
67 72 filesystem. For example::
68 73
69 74 mkdir /var/www/rhodecode
70 75
71 76
72 77 - Go into the created directory run this command to install rhodecode::
73 78
74 79 easy_install rhodecode
75 80
76 81 or::
77 82
78 83 pip install rhodecode
79 84
80 85 - This will install rhodecode together with pylons and all other required
81 86 python libraries into activated virtualenv
82 87
83 88 Requirements for Celery (optional)
84 89 ----------------------------------
85 90
86 91 In order to gain maximum performance
87 92 there are some third-party you must install. When RhodeCode is used
88 93 together with celery you have to install some kind of message broker,
89 94 recommended one is rabbitmq_ to make the async tasks work.
90 95
91 96 Of course RhodeCode works in sync mode also and then you do not have to install
92 97 any third party applications. However, using Celery_ will give you a large
93 98 speed improvement when using many big repositories. If you plan to use
94 99 RhodeCode for say 7 to 10 repositories, RhodeCode will perform perfectly well
95 100 without celery running.
96 101
97 102 If you make the decision to run RhodeCode with celery make sure you run
98 103 celeryd using paster and message broker together with the application.
99 104
100 105 .. note::
101 106 Installing message broker and using celery is optional, RhodeCode will
102 107 work perfectly fine without them.
103 108
104 109
105 110 **Message Broker**
106 111
107 112 - preferred is `RabbitMq <http://www.rabbitmq.com/>`_
108 113 - A possible alternative is `Redis <http://code.google.com/p/redis/>`_
109 114
110 115 For installation instructions you can visit:
111 116 http://ask.github.com/celery/getting-started/index.html.
112 117 This is a very nice tutorial on how to start using celery_ with rabbitmq_
113 118
114 119
115 120 You can now proceed to :ref:`setup`
116 121 -----------------------------------
117 122
118 123
119 124
120 125 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
121 126 .. _python: http://www.python.org/
122 127 .. _mercurial: http://mercurial.selenic.com/
123 128 .. _celery: http://celeryproject.org/
124 129 .. _rabbitmq: http://www.rabbitmq.com/ No newline at end of file
@@ -1,732 +1,737
1 1 .. _setup:
2 2
3 3 =====
4 4 Setup
5 5 =====
6 6
7 7
8 8 Setting up RhodeCode
9 9 --------------------
10 10
11 11 First, you will need to create a RhodeCode configuration file. Run the
12 12 following command to do this::
13 13
14 14 paster make-config RhodeCode production.ini
15 15
16 16 - This will create the file `production.ini` in the current directory. This
17 17 configuration file contains the various settings for RhodeCode, e.g proxy
18 18 port, email settings, usage of static files, cache, celery settings and
19 19 logging.
20 20
21 21
22 22 Next, you need to create the databases used by RhodeCode. I recommend that you
23 23 use postgresql or sqlite (default). If you choose a database other than the
24 24 default ensure you properly adjust the db url in your production.ini
25 25 configuration file to use this other database. RhodeCode currently supports
26 26 postgresql, sqlite and mysql databases. Create the database by running
27 27 the following command::
28 28
29 29 paster setup-rhodecode production.ini
30 30
31 31 This will prompt you for a "root" path. This "root" path is the location where
32 32 RhodeCode will store all of its repositories on the current machine. After
33 33 entering this "root" path ``setup-rhodecode`` will also prompt you for a username
34 34 and password for the initial admin account which ``setup-rhodecode`` sets
35 35 up for you.
36 36
37 setup process can be fully automated, example for lazy::
38
39 paster setup-rhodecode production.ini --user=marcink --password=secret --email=marcin@rhodecode.org --repos=/home/marcink/my_repos
40
41
37 42 - The ``setup-rhodecode`` command will create all of the needed tables and an
38 43 admin account. When choosing a root path you can either use a new empty
39 44 location, or a location which already contains existing repositories. If you
40 45 choose a location which contains existing repositories RhodeCode will simply
41 46 add all of the repositories at the chosen location to it's database.
42 47 (Note: make sure you specify the correct path to the root).
43 48 - Note: the given path for mercurial_ repositories **must** be write accessible
44 49 for the application. It's very important since the RhodeCode web interface
45 50 will work without write access, but when trying to do a push it will
46 51 eventually fail with permission denied errors unless it has write access.
47 52
48 53 You are now ready to use RhodeCode, to run it simply execute::
49 54
50 55 paster serve production.ini
51 56
52 57 - This command runs the RhodeCode server. The web app should be available at the
53 58 127.0.0.1:5000. This ip and port is configurable via the production.ini
54 59 file created in previous step
55 60 - Use the admin account you created above when running ``setup-rhodecode``
56 61 to login to the web app.
57 62 - The default permissions on each repository is read, and the owner is admin.
58 63 Remember to update these if needed.
59 64 - In the admin panel you can toggle ldap, anonymous, permissions settings. As
60 65 well as edit more advanced options on users and repositories
61 66
62 67 Optionally users can create `rcextensions` package that extends RhodeCode
63 68 functionality. To do this simply execute::
64 69
65 70 paster make-rcext production.ini
66 71
67 72 This will create `rcextensions` package in the same place that your `ini` file
68 73 lives. With `rcextensions` it's possible to add additional mapping for whoosh,
69 74 stats and add additional code into the push/pull/create repo hooks. For example
70 75 for sending signals to build-bots such as jenkins.
71 76 Please see the `__init__.py` file inside `rcextensions` package
72 77 for more details.
73 78
74 79
75 80 Using RhodeCode with SSH
76 81 ------------------------
77 82
78 83 RhodeCode currently only hosts repositories using http and https. (The addition
79 84 of ssh hosting is a planned future feature.) However you can easily use ssh in
80 85 parallel with RhodeCode. (Repository access via ssh is a standard "out of
81 86 the box" feature of mercurial_ and you can use this to access any of the
82 87 repositories that RhodeCode is hosting. See PublishingRepositories_)
83 88
84 89 RhodeCode repository structures are kept in directories with the same name
85 90 as the project. When using repository groups, each group is a subdirectory.
86 91 This allows you to easily use ssh for accessing repositories.
87 92
88 93 In order to use ssh you need to make sure that your web-server and the users
89 94 login accounts have the correct permissions set on the appropriate directories.
90 95 (Note that these permissions are independent of any permissions you have set up
91 96 using the RhodeCode web interface.)
92 97
93 98 If your main directory (the same as set in RhodeCode settings) is for example
94 99 set to **/home/hg** and the repository you are using is named `rhodecode`, then
95 100 to clone via ssh you should run::
96 101
97 102 hg clone ssh://user@server.com/home/hg/rhodecode
98 103
99 104 Using other external tools such as mercurial-server_ or using ssh key based
100 105 authentication is fully supported.
101 106
102 107 Note: In an advanced setup, in order for your ssh access to use the same
103 108 permissions as set up via the RhodeCode web interface, you can create an
104 109 authentication hook to connect to the rhodecode db and runs check functions for
105 110 permissions against that.
106 111
107 112 Setting up Whoosh full text search
108 113 ----------------------------------
109 114
110 115 Starting from version 1.1 the whoosh index can be build by using the paster
111 116 command ``make-index``. To use ``make-index`` you must specify the configuration
112 117 file that stores the location of the index. You may specify the location of the
113 118 repositories (`--repo-location`). If not specified, this value is retrieved
114 119 from the RhodeCode database. This was required prior to 1.2. Starting from
115 120 version 1.2 it is also possible to specify a comma separated list of
116 121 repositories (`--index-only`) to build index only on chooses repositories
117 122 skipping any other found in repos location
118 123
119 124 You may optionally pass the option `-f` to enable a full index rebuild. Without
120 125 the `-f` option, indexing will run always in "incremental" mode.
121 126
122 127 For an incremental index build use::
123 128
124 129 paster make-index production.ini
125 130
126 131 For a full index rebuild use::
127 132
128 133 paster make-index production.ini -f
129 134
130 135
131 136 building index just for chosen repositories is possible with such command::
132 137
133 138 paster make-index production.ini --index-only=vcs,rhodecode
134 139
135 140
136 141 In order to do periodical index builds and keep your index always up to date.
137 142 It's recommended to do a crontab entry for incremental indexing.
138 143 An example entry might look like this::
139 144
140 145 /path/to/python/bin/paster make-index /path/to/rhodecode/production.ini
141 146
142 147 When using incremental mode (the default) whoosh will check the last
143 148 modification date of each file and add it to be reindexed if a newer file is
144 149 available. The indexing daemon checks for any removed files and removes them
145 150 from index.
146 151
147 152 If you want to rebuild index from scratch, you can use the `-f` flag as above,
148 153 or in the admin panel you can check `build from scratch` flag.
149 154
150 155
151 156 Setting up LDAP support
152 157 -----------------------
153 158
154 159 RhodeCode starting from version 1.1 supports ldap authentication. In order
155 160 to use LDAP, you have to install the python-ldap_ package. This package is
156 161 available via pypi, so you can install it by running
157 162
158 163 using easy_install::
159 164
160 165 easy_install python-ldap
161 166
162 167 using pip::
163 168
164 169 pip install python-ldap
165 170
166 171 .. note::
167 172 python-ldap requires some certain libs on your system, so before installing
168 173 it check that you have at least `openldap`, and `sasl` libraries.
169 174
170 175 LDAP settings are located in admin->ldap section,
171 176
172 177 Here's a typical ldap setup::
173 178
174 179 Connection settings
175 180 Enable LDAP = checked
176 181 Host = host.example.org
177 182 Port = 389
178 183 Account = <account>
179 184 Password = <password>
180 185 Connection Security = LDAPS connection
181 186 Certificate Checks = DEMAND
182 187
183 188 Search settings
184 189 Base DN = CN=users,DC=host,DC=example,DC=org
185 190 LDAP Filter = (&(objectClass=user)(!(objectClass=computer)))
186 191 LDAP Search Scope = SUBTREE
187 192
188 193 Attribute mappings
189 194 Login Attribute = uid
190 195 First Name Attribute = firstName
191 196 Last Name Attribute = lastName
192 197 E-mail Attribute = mail
193 198
194 199 .. _enable_ldap:
195 200
196 201 Enable LDAP : required
197 202 Whether to use LDAP for authenticating users.
198 203
199 204 .. _ldap_host:
200 205
201 206 Host : required
202 207 LDAP server hostname or IP address.
203 208
204 209 .. _Port:
205 210
206 211 Port : required
207 212 389 for un-encrypted LDAP, 636 for SSL-encrypted LDAP.
208 213
209 214 .. _ldap_account:
210 215
211 216 Account : optional
212 217 Only required if the LDAP server does not allow anonymous browsing of
213 218 records. This should be a special account for record browsing. This
214 219 will require `LDAP Password`_ below.
215 220
216 221 .. _LDAP Password:
217 222
218 223 Password : optional
219 224 Only required if the LDAP server does not allow anonymous browsing of
220 225 records.
221 226
222 227 .. _Enable LDAPS:
223 228
224 229 Connection Security : required
225 230 Defines the connection to LDAP server
226 231
227 232 No encryption
228 233 Plain non encrypted connection
229 234
230 235 LDAPS connection
231 236 Enable ldaps connection. It will likely require `Port`_ to be set to
232 237 a different value (standard LDAPS port is 636). When LDAPS is enabled
233 238 then `Certificate Checks`_ is required.
234 239
235 240 START_TLS on LDAP connection
236 241 START TLS connection
237 242
238 243 .. _Certificate Checks:
239 244
240 245 Certificate Checks : optional
241 246 How SSL certificates verification is handled - this is only useful when
242 247 `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security
243 248 while the other options are susceptible to man-in-the-middle attacks. SSL
244 249 certificates can be installed to /etc/openldap/cacerts so that the
245 250 DEMAND or HARD options can be used with self-signed certificates or
246 251 certificates that do not have traceable certificates of authority.
247 252
248 253 NEVER
249 254 A serve certificate will never be requested or checked.
250 255
251 256 ALLOW
252 257 A server certificate is requested. Failure to provide a
253 258 certificate or providing a bad certificate will not terminate the
254 259 session.
255 260
256 261 TRY
257 262 A server certificate is requested. Failure to provide a
258 263 certificate does not halt the session; providing a bad certificate
259 264 halts the session.
260 265
261 266 DEMAND
262 267 A server certificate is requested and must be provided and
263 268 authenticated for the session to proceed.
264 269
265 270 HARD
266 271 The same as DEMAND.
267 272
268 273 .. _Base DN:
269 274
270 275 Base DN : required
271 276 The Distinguished Name (DN) where searches for users will be performed.
272 277 Searches can be controlled by `LDAP Filter`_ and `LDAP Search Scope`_.
273 278
274 279 .. _LDAP Filter:
275 280
276 281 LDAP Filter : optional
277 282 A LDAP filter defined by RFC 2254. This is more useful when `LDAP
278 283 Search Scope`_ is set to SUBTREE. The filter is useful for limiting
279 284 which LDAP objects are identified as representing Users for
280 285 authentication. The filter is augmented by `Login Attribute`_ below.
281 286 This can commonly be left blank.
282 287
283 288 .. _LDAP Search Scope:
284 289
285 290 LDAP Search Scope : required
286 291 This limits how far LDAP will search for a matching object.
287 292
288 293 BASE
289 294 Only allows searching of `Base DN`_ and is usually not what you
290 295 want.
291 296
292 297 ONELEVEL
293 298 Searches all entries under `Base DN`_, but not Base DN itself.
294 299
295 300 SUBTREE
296 301 Searches all entries below `Base DN`_, but not Base DN itself.
297 302 When using SUBTREE `LDAP Filter`_ is useful to limit object
298 303 location.
299 304
300 305 .. _Login Attribute:
301 306
302 307 Login Attribute : required
303 308 The LDAP record attribute that will be matched as the USERNAME or
304 309 ACCOUNT used to connect to RhodeCode. This will be added to `LDAP
305 310 Filter`_ for locating the User object. If `LDAP Filter`_ is specified as
306 311 "LDAPFILTER", `Login Attribute`_ is specified as "uid" and the user has
307 312 connected as "jsmith" then the `LDAP Filter`_ will be augmented as below
308 313 ::
309 314
310 315 (&(LDAPFILTER)(uid=jsmith))
311 316
312 317 .. _ldap_attr_firstname:
313 318
314 319 First Name Attribute : required
315 320 The LDAP record attribute which represents the user's first name.
316 321
317 322 .. _ldap_attr_lastname:
318 323
319 324 Last Name Attribute : required
320 325 The LDAP record attribute which represents the user's last name.
321 326
322 327 .. _ldap_attr_email:
323 328
324 329 Email Attribute : required
325 330 The LDAP record attribute which represents the user's email address.
326 331
327 332 If all data are entered correctly, and python-ldap_ is properly installed
328 333 users should be granted access to RhodeCode with ldap accounts. At this
329 334 time user information is copied from LDAP into the RhodeCode user database.
330 335 This means that updates of an LDAP user object may not be reflected as a
331 336 user update in RhodeCode.
332 337
333 338 If You have problems with LDAP access and believe You entered correct
334 339 information check out the RhodeCode logs, any error messages sent from LDAP
335 340 will be saved there.
336 341
337 342 Active Directory
338 343 ''''''''''''''''
339 344
340 345 RhodeCode can use Microsoft Active Directory for user authentication. This
341 346 is done through an LDAP or LDAPS connection to Active Directory. The
342 347 following LDAP configuration settings are typical for using Active
343 348 Directory ::
344 349
345 350 Base DN = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
346 351 Login Attribute = sAMAccountName
347 352 First Name Attribute = givenName
348 353 Last Name Attribute = sn
349 354 E-mail Attribute = mail
350 355
351 356 All other LDAP settings will likely be site-specific and should be
352 357 appropriately configured.
353 358
354 359
355 360 Authentication by container or reverse-proxy
356 361 --------------------------------------------
357 362
358 363 Starting with version 1.3, RhodeCode supports delegating the authentication
359 364 of users to its WSGI container, or to a reverse-proxy server through which all
360 365 clients access the application.
361 366
362 367 When these authentication methods are enabled in RhodeCode, it uses the
363 368 username that the container/proxy (Apache/Nginx/etc) authenticated and doesn't
364 369 perform the authentication itself. The authorization, however, is still done by
365 370 RhodeCode according to its settings.
366 371
367 372 When a user logs in for the first time using these authentication methods,
368 373 a matching user account is created in RhodeCode with default permissions. An
369 374 administrator can then modify it using RhodeCode's admin interface.
370 375 It's also possible for an administrator to create accounts and configure their
371 376 permissions before the user logs in for the first time.
372 377
373 378 Container-based authentication
374 379 ''''''''''''''''''''''''''''''
375 380
376 381 In a container-based authentication setup, RhodeCode reads the user name from
377 382 the ``REMOTE_USER`` server variable provided by the WSGI container.
378 383
379 384 After setting up your container (see `Apache's WSGI config`_), you'd need
380 385 to configure it to require authentication on the location configured for
381 386 RhodeCode.
382 387
383 388 In order for RhodeCode to start using the provided username, you should set the
384 389 following in the [app:main] section of your .ini file::
385 390
386 391 container_auth_enabled = true
387 392
388 393
389 394 Proxy pass-through authentication
390 395 '''''''''''''''''''''''''''''''''
391 396
392 397 In a proxy pass-through authentication setup, RhodeCode reads the user name
393 398 from the ``X-Forwarded-User`` request header, which should be configured to be
394 399 sent by the reverse-proxy server.
395 400
396 401 After setting up your proxy solution (see `Apache virtual host reverse proxy example`_,
397 402 `Apache as subdirectory`_ or `Nginx virtual host example`_), you'd need to
398 403 configure the authentication and add the username in a request header named
399 404 ``X-Forwarded-User``.
400 405
401 406 For example, the following config section for Apache sets a subdirectory in a
402 407 reverse-proxy setup with basic auth::
403 408
404 409 <Location /<someprefix> >
405 410 ProxyPass http://127.0.0.1:5000/<someprefix>
406 411 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
407 412 SetEnvIf X-Url-Scheme https HTTPS=1
408 413
409 414 AuthType Basic
410 415 AuthName "RhodeCode authentication"
411 416 AuthUserFile /home/web/rhodecode/.htpasswd
412 417 require valid-user
413 418
414 419 RequestHeader unset X-Forwarded-User
415 420
416 421 RewriteEngine On
417 422 RewriteCond %{LA-U:REMOTE_USER} (.+)
418 423 RewriteRule .* - [E=RU:%1]
419 424 RequestHeader set X-Forwarded-User %{RU}e
420 425 </Location>
421 426
422 427 In order for RhodeCode to start using the forwarded username, you should set
423 428 the following in the [app:main] section of your .ini file::
424 429
425 430 proxypass_auth_enabled = true
426 431
427 432 .. note::
428 433 If you enable proxy pass-through authentication, make sure your server is
429 434 only accessible through the proxy. Otherwise, any client would be able to
430 435 forge the authentication header and could effectively become authenticated
431 436 using any account of their liking.
432 437
433 438 Integration with Issue trackers
434 439 -------------------------------
435 440
436 441 RhodeCode provides a simple integration with issue trackers. It's possible
437 442 to define a regular expression that will fetch issue id stored in commit
438 443 messages and replace that with an url to this issue. To enable this simply
439 444 uncomment following variables in the ini file::
440 445
441 446 url_pat = (?:^#|\s#)(\w+)
442 447 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
443 448 issue_prefix = #
444 449
445 450 `url_pat` is the regular expression that will fetch issues from commit messages.
446 451 Default regex will match issues in format of #<number> eg. #300.
447 452
448 453 Matched issues will be replace with the link specified as `issue_server_link`
449 454 {id} will be replaced with issue id, and {repo} with repository name.
450 455 Since the # is striped `issue_prefix` is added as a prefix to url.
451 456 `issue_prefix` can be something different than # if you pass
452 457 ISSUE- as issue prefix this will generate an url in format::
453 458
454 459 <a href="https://myissueserver.com/example_repo/issue/300">ISSUE-300</a>
455 460
456 461 Hook management
457 462 ---------------
458 463
459 464 Hooks can be managed in similar way to this used in .hgrc files.
460 465 To access hooks setting click `advanced setup` on Hooks section of Mercurial
461 466 Settings in Admin.
462 467
463 468 There are 4 built in hooks that cannot be changed (only enable/disable by
464 469 checkboxes on previos section).
465 470 To add another custom hook simply fill in first section with
466 471 <name>.<hook_type> and the second one with hook path. Example hooks
467 472 can be found at *rhodecode.lib.hooks*.
468 473
469 474
470 475 Changing default encoding
471 476 -------------------------
472 477
473 478 By default RhodeCode uses utf8 encoding, starting from 1.3 series this
474 479 can be changed, simply edit default_encoding in .ini file to desired one.
475 480 This affects many parts in rhodecode including commiters names, filenames,
476 481 encoding of commit messages. In addition RhodeCode can detect if `chardet`
477 482 library is installed. If `chardet` is detected RhodeCode will fallback to it
478 483 when there are encode/decode errors.
479 484
480 485
481 486 Setting Up Celery
482 487 -----------------
483 488
484 489 Since version 1.1 celery is configured by the rhodecode ini configuration files.
485 490 Simply set use_celery=true in the ini file then add / change the configuration
486 491 variables inside the ini file.
487 492
488 493 Remember that the ini files use the format with '.' not with '_' like celery.
489 494 So for example setting `BROKER_HOST` in celery means setting `broker.host` in
490 495 the config file.
491 496
492 497 In order to start using celery run::
493 498
494 499 paster celeryd <configfile.ini>
495 500
496 501
497 502 .. note::
498 503 Make sure you run this command from the same virtualenv, and with the same
499 504 user that rhodecode runs.
500 505
501 506 HTTPS support
502 507 -------------
503 508
504 509 There are two ways to enable https:
505 510
506 511 - Set HTTP_X_URL_SCHEME in your http server headers, than rhodecode will
507 512 recognize this headers and make proper https redirections
508 513 - Alternatively, change the `force_https = true` flag in the ini configuration
509 514 to force using https, no headers are needed than to enable https
510 515
511 516
512 517 Nginx virtual host example
513 518 --------------------------
514 519
515 520 Sample config for nginx using proxy::
516 521
517 522 upstream rc {
518 523 server 127.0.0.1:5000;
519 524 # add more instances for load balancing
520 525 #server 127.0.0.1:5001;
521 526 #server 127.0.0.1:5002;
522 527 }
523 528
524 529 server {
525 530 listen 80;
526 531 server_name hg.myserver.com;
527 532 access_log /var/log/nginx/rhodecode.access.log;
528 533 error_log /var/log/nginx/rhodecode.error.log;
529 534
530 535 location / {
531 536 try_files $uri @rhode;
532 537 }
533 538
534 539 location @rhode {
535 540 proxy_pass http://rc;
536 541 include /etc/nginx/proxy.conf;
537 542 }
538 543
539 544 }
540 545
541 546 Here's the proxy.conf. It's tuned so it will not timeout on long
542 547 pushes or large pushes::
543 548
544 549 proxy_redirect off;
545 550 proxy_set_header Host $host;
546 551 proxy_set_header X-Url-Scheme $scheme;
547 552 proxy_set_header X-Host $http_host;
548 553 proxy_set_header X-Real-IP $remote_addr;
549 554 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
550 555 proxy_set_header Proxy-host $proxy_host;
551 556 client_max_body_size 400m;
552 557 client_body_buffer_size 128k;
553 558 proxy_buffering off;
554 559 proxy_connect_timeout 7200;
555 560 proxy_send_timeout 7200;
556 561 proxy_read_timeout 7200;
557 562 proxy_buffers 8 32k;
558 563
559 564 Also, when using root path with nginx you might set the static files to false
560 565 in the production.ini file::
561 566
562 567 [app:main]
563 568 use = egg:rhodecode
564 569 full_stack = true
565 570 static_files = false
566 571 lang=en
567 572 cache_dir = %(here)s/data
568 573
569 574 In order to not have the statics served by the application. This improves speed.
570 575
571 576
572 577 Apache virtual host reverse proxy example
573 578 -----------------------------------------
574 579
575 580 Here is a sample configuration file for apache using proxy::
576 581
577 582 <VirtualHost *:80>
578 583 ServerName hg.myserver.com
579 584 ServerAlias hg.myserver.com
580 585
581 586 <Proxy *>
582 587 Order allow,deny
583 588 Allow from all
584 589 </Proxy>
585 590
586 591 #important !
587 592 #Directive to properly generate url (clone url) for pylons
588 593 ProxyPreserveHost On
589 594
590 595 #rhodecode instance
591 596 ProxyPass / http://127.0.0.1:5000/
592 597 ProxyPassReverse / http://127.0.0.1:5000/
593 598
594 599 #to enable https use line below
595 600 #SetEnvIf X-Url-Scheme https HTTPS=1
596 601
597 602 </VirtualHost>
598 603
599 604
600 605 Additional tutorial
601 606 http://wiki.pylonshq.com/display/pylonscookbook/Apache+as+a+reverse+proxy+for+Pylons
602 607
603 608
604 609 Apache as subdirectory
605 610 ----------------------
606 611
607 612 Apache subdirectory part::
608 613
609 614 <Location /<someprefix> >
610 615 ProxyPass http://127.0.0.1:5000/<someprefix>
611 616 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
612 617 SetEnvIf X-Url-Scheme https HTTPS=1
613 618 </Location>
614 619
615 620 Besides the regular apache setup you will need to add the following line
616 621 into [app:main] section of your .ini file::
617 622
618 623 filter-with = proxy-prefix
619 624
620 625 Add the following at the end of the .ini file::
621 626
622 627 [filter:proxy-prefix]
623 628 use = egg:PasteDeploy#prefix
624 629 prefix = /<someprefix>
625 630
626 631
627 632 then change <someprefix> into your choosen prefix
628 633
629 634 Apache's WSGI config
630 635 --------------------
631 636
632 637 Alternatively, RhodeCode can be set up with Apache under mod_wsgi. For
633 638 that, you'll need to:
634 639
635 640 - Install mod_wsgi. If using a Debian-based distro, you can install
636 641 the package libapache2-mod-wsgi::
637 642
638 643 aptitude install libapache2-mod-wsgi
639 644
640 645 - Enable mod_wsgi::
641 646
642 647 a2enmod wsgi
643 648
644 649 - Create a wsgi dispatch script, like the one below. Make sure you
645 650 check the paths correctly point to where you installed RhodeCode
646 651 and its Python Virtual Environment.
647 652 - Enable the WSGIScriptAlias directive for the wsgi dispatch script,
648 653 as in the following example. Once again, check the paths are
649 654 correctly specified.
650 655
651 656 Here is a sample excerpt from an Apache Virtual Host configuration file::
652 657
653 658 WSGIDaemonProcess pylons user=www-data group=www-data processes=1 \
654 659 threads=4 \
655 660 python-path=/home/web/rhodecode/pyenv/lib/python2.6/site-packages
656 661 WSGIScriptAlias / /home/web/rhodecode/dispatch.wsgi
657 662 WSGIPassAuthorization On
658 663
659 664 Example wsgi dispatch script::
660 665
661 666 import os
662 667 os.environ["HGENCODING"] = "UTF-8"
663 668 os.environ['PYTHON_EGG_CACHE'] = '/home/web/rhodecode/.egg-cache'
664 669
665 670 # sometimes it's needed to set the curent dir
666 671 os.chdir('/home/web/rhodecode/')
667 672
668 673 import site
669 674 site.addsitedir("/home/web/rhodecode/pyenv/lib/python2.6/site-packages")
670 675
671 676 from paste.deploy import loadapp
672 677 from paste.script.util.logging_config import fileConfig
673 678
674 679 fileConfig('/home/web/rhodecode/production.ini')
675 680 application = loadapp('config:/home/web/rhodecode/production.ini')
676 681
677 682 Note: when using mod_wsgi you'll need to install the same version of
678 683 Mercurial that's inside RhodeCode's virtualenv also on the system's Python
679 684 environment.
680 685
681 686
682 687 Other configuration files
683 688 -------------------------
684 689
685 690 Some example init.d scripts can be found here, for debian and gentoo:
686 691
687 692 https://rhodecode.org/rhodecode/files/tip/init.d
688 693
689 694
690 695 Troubleshooting
691 696 ---------------
692 697
693 698 :Q: **Missing static files?**
694 699 :A: Make sure either to set the `static_files = true` in the .ini file or
695 700 double check the root path for your http setup. It should point to
696 701 for example:
697 702 /home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
698 703
699 704 |
700 705
701 706 :Q: **Can't install celery/rabbitmq**
702 707 :A: Don't worry RhodeCode works without them too. No extra setup is required.
703 708
704 709 |
705 710
706 711 :Q: **Long lasting push timeouts?**
707 712 :A: Make sure you set a longer timeouts in your proxy/fcgi settings, timeouts
708 713 are caused by https server and not RhodeCode.
709 714
710 715 |
711 716
712 717 :Q: **Large pushes timeouts?**
713 718 :A: Make sure you set a proper max_body_size for the http server.
714 719
715 720 |
716 721
717 722 :Q: **Apache doesn't pass basicAuth on pull/push?**
718 723 :A: Make sure you added `WSGIPassAuthorization true`.
719 724
720 725 For further questions search the `Issues tracker`_, or post a message in the
721 726 `google group rhodecode`_
722 727
723 728 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
724 729 .. _python: http://www.python.org/
725 730 .. _mercurial: http://mercurial.selenic.com/
726 731 .. _celery: http://celeryproject.org/
727 732 .. _rabbitmq: http://www.rabbitmq.com/
728 733 .. _python-ldap: http://www.python-ldap.org/
729 734 .. _mercurial-server: http://www.lshift.net/mercurial-server.html
730 735 .. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
731 736 .. _Issues tracker: https://bitbucket.org/marcinkuzminski/rhodecode/issues
732 737 .. _google group rhodecode: http://groups.google.com/group/rhodecode
@@ -1,609 +1,614
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.diffs
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Set of diffing helpers, previously part of vcs
7 7
8 8
9 9 :created_on: Dec 4, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :original copyright: 2007-2008 by Armin Ronacher
13 13 :license: GPLv3, see COPYING for more details.
14 14 """
15 15 # This program is free software: you can redistribute it and/or modify
16 16 # it under the terms of the GNU General Public License as published by
17 17 # the Free Software Foundation, either version 3 of the License, or
18 18 # (at your option) any later version.
19 19 #
20 20 # This program is distributed in the hope that it will be useful,
21 21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 23 # GNU General Public License for more details.
24 24 #
25 25 # You should have received a copy of the GNU General Public License
26 26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 27
28 28 import re
29 29 import io
30 30 import difflib
31 31 import markupsafe
32 32
33 33 from itertools import tee, imap
34 34
35 35 from mercurial import patch
36 36 from mercurial.mdiff import diffopts
37 37 from mercurial.bundlerepo import bundlerepository
38 38 from mercurial import localrepo
39 39
40 40 from pylons.i18n.translation import _
41 41
42 42 from rhodecode.lib.vcs.exceptions import VCSError
43 43 from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
44 44 from rhodecode.lib.helpers import escape
45 45 from rhodecode.lib.utils import EmptyChangeset, make_ui
46 46
47 47
48 48 def wrap_to_table(str_):
49 49 return '''<table class="code-difftable">
50 50 <tr class="line no-comment">
51 51 <td class="lineno new"></td>
52 52 <td class="code no-comment"><pre>%s</pre></td>
53 53 </tr>
54 54 </table>''' % str_
55 55
56 56
57 57 def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
58 58 ignore_whitespace=True, line_context=3,
59 59 enable_comments=False):
60 60 """
61 61 returns a wrapped diff into a table, checks for cut_off_limit and presents
62 62 proper message
63 63 """
64 64
65 65 if filenode_old is None:
66 66 filenode_old = FileNode(filenode_new.path, '', EmptyChangeset())
67 67
68 68 if filenode_old.is_binary or filenode_new.is_binary:
69 69 diff = wrap_to_table(_('binary file'))
70 70 stats = (0, 0)
71 71 size = 0
72 72
73 73 elif cut_off_limit != -1 and (cut_off_limit is None or
74 74 (filenode_old.size < cut_off_limit and filenode_new.size < cut_off_limit)):
75 75
76 76 f_gitdiff = get_gitdiff(filenode_old, filenode_new,
77 77 ignore_whitespace=ignore_whitespace,
78 78 context=line_context)
79 79 diff_processor = DiffProcessor(f_gitdiff, format='gitdiff')
80 80
81 81 diff = diff_processor.as_html(enable_comments=enable_comments)
82 82 stats = diff_processor.stat()
83 83 size = len(diff or '')
84 84 else:
85 85 diff = wrap_to_table(_('Changeset was too big and was cut off, use '
86 86 'diff menu to display this diff'))
87 87 stats = (0, 0)
88 88 size = 0
89 89 if not diff:
90 90 submodules = filter(lambda o: isinstance(o, SubModuleNode),
91 91 [filenode_new, filenode_old])
92 92 if submodules:
93 93 diff = wrap_to_table(escape('Submodule %r' % submodules[0]))
94 94 else:
95 95 diff = wrap_to_table(_('No changes detected'))
96 96
97 97 cs1 = filenode_old.changeset.raw_id
98 98 cs2 = filenode_new.changeset.raw_id
99 99
100 100 return size, cs1, cs2, diff, stats
101 101
102 102
103 103 def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3):
104 104 """
105 105 Returns git style diff between given ``filenode_old`` and ``filenode_new``.
106 106
107 107 :param ignore_whitespace: ignore whitespaces in diff
108 108 """
109 109 # make sure we pass in default context
110 110 context = context or 3
111 111 submodules = filter(lambda o: isinstance(o, SubModuleNode),
112 112 [filenode_new, filenode_old])
113 113 if submodules:
114 114 return ''
115 115
116 116 for filenode in (filenode_old, filenode_new):
117 117 if not isinstance(filenode, FileNode):
118 118 raise VCSError("Given object should be FileNode object, not %s"
119 119 % filenode.__class__)
120 120
121 121 repo = filenode_new.changeset.repository
122 122 old_raw_id = getattr(filenode_old.changeset, 'raw_id', repo.EMPTY_CHANGESET)
123 123 new_raw_id = getattr(filenode_new.changeset, 'raw_id', repo.EMPTY_CHANGESET)
124 124
125 125 vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path,
126 126 ignore_whitespace, context)
127 127 return vcs_gitdiff
128 128
129 129
130 130 class DiffProcessor(object):
131 131 """
132 132 Give it a unified diff and it returns a list of the files that were
133 133 mentioned in the diff together with a dict of meta information that
134 134 can be used to render it in a HTML template.
135 135 """
136 136 _chunk_re = re.compile(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
137 137
138 138 def __init__(self, diff, differ='diff', format='udiff'):
139 139 """
140 140 :param diff: a text in diff format or generator
141 141 :param format: format of diff passed, `udiff` or `gitdiff`
142 142 """
143 143 if isinstance(diff, basestring):
144 144 diff = [diff]
145 145
146 146 self.__udiff = diff
147 147 self.__format = format
148 148 self.adds = 0
149 149 self.removes = 0
150 150
151 151 if isinstance(self.__udiff, basestring):
152 152 self.lines = iter(self.__udiff.splitlines(1))
153 153
154 154 elif self.__format == 'gitdiff':
155 155 udiff_copy = self.copy_iterator()
156 156 self.lines = imap(self.escaper, self._parse_gitdiff(udiff_copy))
157 157 else:
158 158 udiff_copy = self.copy_iterator()
159 159 self.lines = imap(self.escaper, udiff_copy)
160 160
161 161 # Select a differ.
162 162 if differ == 'difflib':
163 163 self.differ = self._highlight_line_difflib
164 164 else:
165 165 self.differ = self._highlight_line_udiff
166 166
167 167 def escaper(self, string):
168 168 return markupsafe.escape(string)
169 169
170 170 def copy_iterator(self):
171 171 """
172 172 make a fresh copy of generator, we should not iterate thru
173 173 an original as it's needed for repeating operations on
174 174 this instance of DiffProcessor
175 175 """
176 176 self.__udiff, iterator_copy = tee(self.__udiff)
177 177 return iterator_copy
178 178
179 179 def _extract_rev(self, line1, line2):
180 180 """
181 181 Extract the operation (A/M/D), filename and revision hint from a line.
182 182 """
183 183
184 184 try:
185 185 if line1.startswith('--- ') and line2.startswith('+++ '):
186 186 l1 = line1[4:].split(None, 1)
187 187 old_filename = (l1[0].replace('a/', '', 1)
188 188 if len(l1) >= 1 else None)
189 189 old_rev = l1[1] if len(l1) == 2 else 'old'
190 190
191 191 l2 = line2[4:].split(None, 1)
192 192 new_filename = (l2[0].replace('b/', '', 1)
193 193 if len(l1) >= 1 else None)
194 194 new_rev = l2[1] if len(l2) == 2 else 'new'
195 195
196 196 filename = (old_filename
197 197 if old_filename != '/dev/null' else new_filename)
198 198
199 199 operation = 'D' if new_filename == '/dev/null' else None
200 200 if not operation:
201 201 operation = 'M' if old_filename != '/dev/null' else 'A'
202 202
203 203 return operation, filename, new_rev, old_rev
204 204 except (ValueError, IndexError):
205 205 pass
206 206
207 207 return None, None, None, None
208 208
209 209 def _parse_gitdiff(self, diffiterator):
210 210 def line_decoder(l):
211 211 if l.startswith('+') and not l.startswith('+++'):
212 212 self.adds += 1
213 213 elif l.startswith('-') and not l.startswith('---'):
214 214 self.removes += 1
215 215 return l.decode('utf8', 'replace')
216 216
217 217 output = list(diffiterator)
218 218 size = len(output)
219 219
220 220 if size == 2:
221 221 l = []
222 222 l.extend([output[0]])
223 223 l.extend(output[1].splitlines(1))
224 224 return map(line_decoder, l)
225 225 elif size == 1:
226 226 return map(line_decoder, output[0].splitlines(1))
227 227 elif size == 0:
228 228 return []
229 229
230 230 raise Exception('wrong size of diff %s' % size)
231 231
232 232 def _highlight_line_difflib(self, line, next_):
233 233 """
234 234 Highlight inline changes in both lines.
235 235 """
236 236
237 237 if line['action'] == 'del':
238 238 old, new = line, next_
239 239 else:
240 240 old, new = next_, line
241 241
242 242 oldwords = re.split(r'(\W)', old['line'])
243 243 newwords = re.split(r'(\W)', new['line'])
244 244
245 245 sequence = difflib.SequenceMatcher(None, oldwords, newwords)
246 246
247 247 oldfragments, newfragments = [], []
248 248 for tag, i1, i2, j1, j2 in sequence.get_opcodes():
249 249 oldfrag = ''.join(oldwords[i1:i2])
250 250 newfrag = ''.join(newwords[j1:j2])
251 251 if tag != 'equal':
252 252 if oldfrag:
253 253 oldfrag = '<del>%s</del>' % oldfrag
254 254 if newfrag:
255 255 newfrag = '<ins>%s</ins>' % newfrag
256 256 oldfragments.append(oldfrag)
257 257 newfragments.append(newfrag)
258 258
259 259 old['line'] = "".join(oldfragments)
260 260 new['line'] = "".join(newfragments)
261 261
262 262 def _highlight_line_udiff(self, line, next_):
263 263 """
264 264 Highlight inline changes in both lines.
265 265 """
266 266 start = 0
267 267 limit = min(len(line['line']), len(next_['line']))
268 268 while start < limit and line['line'][start] == next_['line'][start]:
269 269 start += 1
270 270 end = -1
271 271 limit -= start
272 272 while -end <= limit and line['line'][end] == next_['line'][end]:
273 273 end -= 1
274 274 end += 1
275 275 if start or end:
276 276 def do(l):
277 277 last = end + len(l['line'])
278 278 if l['action'] == 'add':
279 279 tag = 'ins'
280 280 else:
281 281 tag = 'del'
282 282 l['line'] = '%s<%s>%s</%s>%s' % (
283 283 l['line'][:start],
284 284 tag,
285 285 l['line'][start:last],
286 286 tag,
287 287 l['line'][last:]
288 288 )
289
290 289 do(line)
291 290 do(next_)
292 291
293 292 def _parse_udiff(self):
294 293 """
295 294 Parse the diff an return data for the template.
296 295 """
297 296 lineiter = self.lines
298 297 files = []
299 298 try:
300 299 line = lineiter.next()
301 # skip first context
302 skipfirst = True
303
304 300 while 1:
305 301 # continue until we found the old file
306 302 if not line.startswith('--- '):
307 303 line = lineiter.next()
308 304 continue
309 305
310 306 chunks = []
311 307 stats = [0, 0]
312 308 operation, filename, old_rev, new_rev = \
313 309 self._extract_rev(line, lineiter.next())
314 310 files.append({
315 311 'filename': filename,
316 312 'old_revision': old_rev,
317 313 'new_revision': new_rev,
318 314 'chunks': chunks,
319 315 'operation': operation,
320 316 'stats': stats,
321 317 })
322 318
323 319 line = lineiter.next()
324 320 while line:
325
326 321 match = self._chunk_re.match(line)
327 322 if not match:
328 323 break
329 324
330 325 lines = []
331 326 chunks.append(lines)
332 327
333 328 old_line, old_end, new_line, new_end = \
334 329 [int(x or 1) for x in match.groups()[:-1]]
335 330 old_line -= 1
336 331 new_line -= 1
337 context = len(match.groups()) == 5
332 gr = match.groups()
333 context = len(gr) == 5
338 334 old_end += old_line
339 335 new_end += new_line
340 336
341 337 if context:
342 if not skipfirst:
338 # skip context only if it's first line
339 if int(gr[0]) > 1:
343 340 lines.append({
344 341 'old_lineno': '...',
345 342 'new_lineno': '...',
346 343 'action': 'context',
347 344 'line': line,
348 345 })
349 else:
350 skipfirst = False
351 346
352 347 line = lineiter.next()
353 348 while old_line < old_end or new_line < new_end:
354 349 if line:
355 350 command, line = line[0], line[1:]
356 351 else:
357 352 command = ' '
358 353 affects_old = affects_new = False
359 354
360 355 # ignore those if we don't expect them
361 356 if command in '#@':
362 357 continue
363 358 elif command == '+':
364 359 affects_new = True
365 360 action = 'add'
366 361 stats[0] += 1
367 362 elif command == '-':
368 363 affects_old = True
369 364 action = 'del'
370 365 stats[1] += 1
371 366 else:
372 367 affects_old = affects_new = True
373 368 action = 'unmod'
374 369
370 if line.find('No newline at end of file') != -1:
371 lines.append({
372 'old_lineno': '...',
373 'new_lineno': '...',
374 'action': 'context',
375 'line': line
376 })
377
378 else:
375 379 old_line += affects_old
376 380 new_line += affects_new
377 381 lines.append({
378 382 'old_lineno': affects_old and old_line or '',
379 383 'new_lineno': affects_new and new_line or '',
380 384 'action': action,
381 385 'line': line
382 386 })
387
383 388 line = lineiter.next()
384 389 except StopIteration:
385 390 pass
386 391
387 392 # highlight inline changes
388 393 for diff_data in files:
389 394 for chunk in diff_data['chunks']:
390 395 lineiter = iter(chunk)
391 396 try:
392 397 while 1:
393 398 line = lineiter.next()
394 399 if line['action'] != 'unmod':
395 400 nextline = lineiter.next()
396 if nextline['action'] == 'unmod' or \
401 if nextline['action'] in ['unmod', 'context'] or \
397 402 nextline['action'] == line['action']:
398 403 continue
399 404 self.differ(line, nextline)
400 405 except StopIteration:
401 406 pass
402 407 return files
403 408
404 409 def prepare(self):
405 410 """
406 411 Prepare the passed udiff for HTML rendering. It'l return a list
407 412 of dicts
408 413 """
409 414 return self._parse_udiff()
410 415
411 416 def _safe_id(self, idstring):
412 417 """Make a string safe for including in an id attribute.
413 418
414 419 The HTML spec says that id attributes 'must begin with
415 420 a letter ([A-Za-z]) and may be followed by any number
416 421 of letters, digits ([0-9]), hyphens ("-"), underscores
417 422 ("_"), colons (":"), and periods (".")'. These regexps
418 423 are slightly over-zealous, in that they remove colons
419 424 and periods unnecessarily.
420 425
421 426 Whitespace is transformed into underscores, and then
422 427 anything which is not a hyphen or a character that
423 428 matches \w (alphanumerics and underscore) is removed.
424 429
425 430 """
426 431 # Transform all whitespace to underscore
427 432 idstring = re.sub(r'\s', "_", '%s' % idstring)
428 433 # Remove everything that is not a hyphen or a member of \w
429 434 idstring = re.sub(r'(?!-)\W', "", idstring).lower()
430 435 return idstring
431 436
432 437 def raw_diff(self):
433 438 """
434 439 Returns raw string as udiff
435 440 """
436 441 udiff_copy = self.copy_iterator()
437 442 if self.__format == 'gitdiff':
438 443 udiff_copy = self._parse_gitdiff(udiff_copy)
439 444 return u''.join(udiff_copy)
440 445
441 446 def as_html(self, table_class='code-difftable', line_class='line',
442 447 new_lineno_class='lineno old', old_lineno_class='lineno new',
443 448 code_class='code', enable_comments=False, diff_lines=None):
444 449 """
445 450 Return given diff as html table with customized css classes
446 451 """
447 452 def _link_to_if(condition, label, url):
448 453 """
449 454 Generates a link if condition is meet or just the label if not.
450 455 """
451 456
452 457 if condition:
453 458 return '''<a href="%(url)s">%(label)s</a>''' % {
454 459 'url': url,
455 460 'label': label
456 461 }
457 462 else:
458 463 return label
459 464 if diff_lines is None:
460 465 diff_lines = self.prepare()
461 466 _html_empty = True
462 467 _html = []
463 468 _html.append('''<table class="%(table_class)s">\n''' % {
464 469 'table_class': table_class
465 470 })
466 471 for diff in diff_lines:
467 472 for line in diff['chunks']:
468 473 _html_empty = False
469 474 for change in line:
470 475 _html.append('''<tr class="%(lc)s %(action)s">\n''' % {
471 476 'lc': line_class,
472 477 'action': change['action']
473 478 })
474 479 anchor_old_id = ''
475 480 anchor_new_id = ''
476 481 anchor_old = "%(filename)s_o%(oldline_no)s" % {
477 482 'filename': self._safe_id(diff['filename']),
478 483 'oldline_no': change['old_lineno']
479 484 }
480 485 anchor_new = "%(filename)s_n%(oldline_no)s" % {
481 486 'filename': self._safe_id(diff['filename']),
482 487 'oldline_no': change['new_lineno']
483 488 }
484 489 cond_old = (change['old_lineno'] != '...' and
485 490 change['old_lineno'])
486 491 cond_new = (change['new_lineno'] != '...' and
487 492 change['new_lineno'])
488 493 if cond_old:
489 494 anchor_old_id = 'id="%s"' % anchor_old
490 495 if cond_new:
491 496 anchor_new_id = 'id="%s"' % anchor_new
492 497 ###########################################################
493 498 # OLD LINE NUMBER
494 499 ###########################################################
495 500 _html.append('''\t<td %(a_id)s class="%(olc)s">''' % {
496 501 'a_id': anchor_old_id,
497 502 'olc': old_lineno_class
498 503 })
499 504
500 505 _html.append('''%(link)s''' % {
501 506 'link': _link_to_if(True, change['old_lineno'],
502 507 '#%s' % anchor_old)
503 508 })
504 509 _html.append('''</td>\n''')
505 510 ###########################################################
506 511 # NEW LINE NUMBER
507 512 ###########################################################
508 513
509 514 _html.append('''\t<td %(a_id)s class="%(nlc)s">''' % {
510 515 'a_id': anchor_new_id,
511 516 'nlc': new_lineno_class
512 517 })
513 518
514 519 _html.append('''%(link)s''' % {
515 520 'link': _link_to_if(True, change['new_lineno'],
516 521 '#%s' % anchor_new)
517 522 })
518 523 _html.append('''</td>\n''')
519 524 ###########################################################
520 525 # CODE
521 526 ###########################################################
522 527 comments = '' if enable_comments else 'no-comment'
523 528 _html.append('''\t<td class="%(cc)s %(inc)s">''' % {
524 529 'cc': code_class,
525 530 'inc': comments
526 531 })
527 532 _html.append('''\n\t\t<pre>%(code)s</pre>\n''' % {
528 533 'code': change['line']
529 534 })
530 535 _html.append('''\t</td>''')
531 536 _html.append('''\n</tr>\n''')
532 537 _html.append('''</table>''')
533 538 if _html_empty:
534 539 return None
535 540 return ''.join(_html)
536 541
537 542 def stat(self):
538 543 """
539 544 Returns tuple of added, and removed lines for this instance
540 545 """
541 546 return self.adds, self.removes
542 547
543 548
544 549 def differ(org_repo, org_ref, other_repo, other_ref, discovery_data=None):
545 550 """
546 551 General differ between branches, bookmarks or separate but releated
547 552 repositories
548 553
549 554 :param org_repo:
550 555 :type org_repo:
551 556 :param org_ref:
552 557 :type org_ref:
553 558 :param other_repo:
554 559 :type other_repo:
555 560 :param other_ref:
556 561 :type other_ref:
557 562 """
558 563
559 564 ignore_whitespace = False
560 565 context = 3
561 566 org_repo = org_repo.scm_instance._repo
562 567 other_repo = other_repo.scm_instance._repo
563 568 opts = diffopts(git=True, ignorews=ignore_whitespace, context=context)
564 569 org_ref = org_ref[1]
565 570 other_ref = other_ref[1]
566 571
567 572 if org_repo != other_repo:
568 573
569 574 common, incoming, rheads = discovery_data
570 575 # create a bundle (uncompressed if other repo is not local)
571 576 if other_repo.capable('getbundle'):
572 577 # disable repo hooks here since it's just bundle !
573 578 # patch and reset hooks section of UI config to not run any
574 579 # hooks on fetching archives with subrepos
575 580 for k, _ in other_repo.ui.configitems('hooks'):
576 581 other_repo.ui.setconfig('hooks', k, None)
577 582
578 583 unbundle = other_repo.getbundle('incoming', common=common,
579 584 heads=rheads)
580 585
581 586 buf = io.BytesIO()
582 587 while True:
583 588 chunk = unbundle._stream.read(1024*4)
584 589 if not chunk:
585 590 break
586 591 buf.write(chunk)
587 592
588 593 buf.seek(0)
589 594 unbundle._stream = buf
590 595
591 596 class InMemoryBundleRepo(bundlerepository):
592 597 def __init__(self, ui, path, bundlestream):
593 598 self._tempparent = None
594 599 localrepo.localrepository.__init__(self, ui, path)
595 600 self.ui.setconfig('phases', 'publish', False)
596 601
597 602 self.bundle = bundlestream
598 603
599 604 # dict with the mapping 'filename' -> position in the bundle
600 605 self.bundlefilespos = {}
601 606
602 607 ui = make_ui('db')
603 608 bundlerepo = InMemoryBundleRepo(ui, path=other_repo.root,
604 609 bundlestream=unbundle)
605 610 return ''.join(patch.diff(bundlerepo, node1=org_ref, node2=other_ref,
606 611 opts=opts))
607 612 else:
608 613 return ''.join(patch.diff(org_repo, node1=org_ref, node2=other_ref,
609 614 opts=opts))
General Comments 0
You need to be logged in to leave comments. Login now