##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r156:c4fd9054 merge stable
parent child Browse files
Show More
@@ -0,0 +1,18 b''
1
2 .PHONY: clean test test-clean test-only
3
4
5 clean:
6 make test-clean
7 find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' \) -exec rm '{}' ';'
8
9 test:
10 make test-clean
11 make test-only
12
13 test-clean:
14 rm -rf coverage.xml htmlcov junit.xml pylint.log result
15 find . -type d -name "__pycache__" -prune -exec rm -rf '{}' ';'
16
17 test-only:
18 PYTHONHASHSEED=random py.test -vv -r xw --cov=vcsserver --cov-report=term-missing --cov-report=html vcsserver
@@ -0,0 +1,3 b''
1 [pytest]
2 testpaths = ./vcsserver
3 addopts = -v
@@ -0,0 +1,15 b''
1 # test related requirements
2 pytest==3.0.5
3 py==1.4.31
4 pytest-cov==2.4.0
5 pytest-sugar==0.7.1
6 pytest-runner==2.9.0
7 pytest-catchlog==1.2.2
8 pytest-profiling==1.2.2
9 gprof2dot==2016.10.13
10 pytest-timeout==1.2.0
11
12 mock==1.0.1
13 WebTest==1.4.3
14 cov-core==1.15.0
15 coverage==3.7.1
@@ -0,0 +1,2 b''
1 [aliases]
2 test = pytest
@@ -0,0 +1,60 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18
19
20 import time
21 import logging
22
23
24 from vcsserver.utils import safe_str
25
26
27 log = logging.getLogger(__name__)
28
29
30 def get_access_path(request):
31 environ = request.environ
32 return environ.get('PATH_INFO')
33
34
35 class RequestWrapperTween(object):
36 def __init__(self, handler, registry):
37 self.handler = handler
38 self.registry = registry
39
40 # one-time configuration code goes here
41
42 def __call__(self, request):
43 start = time.time()
44 try:
45 response = self.handler(request)
46 finally:
47 end = time.time()
48
49 log.info('IP: %s Request to %s time: %.3fs' % (
50 '127.0.0.1',
51 safe_str(get_access_path(request)), end - start)
52 )
53
54 return response
55
56
57 def includeme(config):
58 config.add_tween(
59 'vcsserver.tweens.RequestWrapperTween',
60 )
@@ -1,5 +1,5 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.5.2
2 current_version = 4.6.0
3 message = release: Bump version {current_version} to {new_version}
3 message = release: Bump version {current_version} to {new_version}
4
4
5 [bumpversion:file:vcsserver/VERSION]
5 [bumpversion:file:vcsserver/VERSION]
@@ -2,9 +2,11 b' syntax: glob'
2 *.orig
2 *.orig
3 *.pyc
3 *.pyc
4 *.swp
4 *.swp
5 *.sqlite
5 *.tox
6 *.tox
6 *.egg-info
7 *.egg-info
7 *.egg
8 *.egg
9 *.eggs
8 *.idea
10 *.idea
9 .DS_Store*
11 .DS_Store*
10
12
@@ -5,12 +5,10 b' done = false'
5 done = true
5 done = true
6
6
7 [task:fixes_on_stable]
7 [task:fixes_on_stable]
8 done = true
9
8
10 [task:pip2nix_generated]
9 [task:pip2nix_generated]
11 done = true
12
10
13 [release]
11 [release]
14 state = prepared
12 state = in_progress
15 version = 4.5.2
13 version = 4.6.0
16
14
@@ -1,15 +1,13 b''
1 # top level files
1 # top level files
2 include test.ini
2 include *.rst
3 include MANIFEST.in
3 include *.txt
4 include README.rst
5 include CHANGES.rst
6 include LICENSE.txt
7
4
5 # package extras
8 include vcsserver/VERSION
6 include vcsserver/VERSION
9
7
10 # all config files
8 # all config files
11 recursive-include configs *
9 recursive-include configs *
12
10
13 # skip any tests files
11 # skip any tests files
14 recursive-exclude tests *
12 recursive-exclude vcsserver/tests *
15
13
@@ -14,6 +14,16 b' let pkgs_ = pkgs; in'
14
14
15 let
15 let
16 pkgs = pkgs_.overridePackages (self: super: {
16 pkgs = pkgs_.overridePackages (self: super: {
17 # bump GIT version
18 git = pkgs.lib.overrideDerivation pkgs_.git (oldAttrs: {
19 name = "git-2.9.3";
20 src = pkgs.fetchurl {
21 url = "https://www.kernel.org/pub/software/scm/git/git-2.9.3.tar.xz";
22 sha256 = "0qzs681a64k3shh5p0rg41l1z16fbk5sj0xga45k34hp1hsp654z";
23 };
24
25 });
26
17 # Override subversion derivation to
27 # Override subversion derivation to
18 # - activate python bindings
28 # - activate python bindings
19 subversion = let
29 subversion = let
@@ -22,17 +32,20 b' let'
22 pythonBindings = true;
32 pythonBindings = true;
23 python = self.python27Packages.python;
33 python = self.python27Packages.python;
24 };
34 };
25 in pkgs.lib.overrideDerivation subversionWithPython (oldAttrs: {
35
36 in
37
38 pkgs.lib.overrideDerivation subversionWithPython (oldAttrs: {
26 patches = (oldAttrs.patches or []) ++
39 patches = (oldAttrs.patches or []) ++
27 pkgs.lib.optionals pkgs.stdenv.isDarwin [
40 pkgs.lib.optionals pkgs.stdenv.isDarwin [
28 # johbo: "import svn.client" fails on darwin currently.
41 # johbo: "import svn.client" fails on darwin currently.
29 ./pkgs/subversion-1.9.4-darwin.patch
42 ./pkgs/subversion-1.9.4-darwin.patch
30 ];
43 ];
31 });
44 });
45
32 });
46 });
33
47
34 inherit (pkgs.lib) fix extends;
48 inherit (pkgs.lib) fix extends;
35
36 basePythonPackages = with builtins; if isAttrs pythonPackages
49 basePythonPackages = with builtins; if isAttrs pythonPackages
37 then pythonPackages
50 then pythonPackages
38 else getAttr pythonPackages pkgs;
51 else getAttr pythonPackages pkgs;
@@ -47,9 +60,8 b' let'
47 let
60 let
48 ext = last (splitString "." path);
61 ext = last (splitString "." path);
49 in
62 in
50 !elem (basename path) [
63 !elem (basename path) [".hg" ".git" "__pycache__" ".eggs"
51 ".git" ".hg" "__pycache__" ".eggs" "node_modules"
64 "node_modules" "build" "data" "tmp"] &&
52 "build" "data" "tmp"] &&
53 !elem ext ["egg-info" "pyc"] &&
65 !elem ext ["egg-info" "pyc"] &&
54 !startsWith "result" path;
66 !startsWith "result" path;
55
67
@@ -57,8 +69,7 b' let'
57
69
58 pythonGeneratedPackages = self: basePythonPackages.override (a: {
70 pythonGeneratedPackages = self: basePythonPackages.override (a: {
59 inherit self;
71 inherit self;
60 })
72 }) // (scopedImport {
61 // (scopedImport {
62 self = self;
73 self = self;
63 super = basePythonPackages;
74 super = basePythonPackages;
64 inherit pkgs;
75 inherit pkgs;
@@ -66,18 +77,15 b' let'
66 } ./pkgs/python-packages.nix);
77 } ./pkgs/python-packages.nix);
67
78
68 pythonOverrides = import ./pkgs/python-packages-overrides.nix {
79 pythonOverrides = import ./pkgs/python-packages-overrides.nix {
69 inherit
80 inherit basePythonPackages pkgs;
70 basePythonPackages
71 pkgs;
72 };
81 };
73
82
74 version = builtins.readFile ./vcsserver/VERSION;
83 version = builtins.readFile ./vcsserver/VERSION;
75
84
76 pythonLocalOverrides = self: super: {
85 pythonLocalOverrides = self: super: {
77 rhodecode-vcsserver = super.rhodecode-vcsserver.override (attrs: {
86 rhodecode-vcsserver = super.rhodecode-vcsserver.override (attrs: {
78 inherit
87 inherit doCheck version;
79 doCheck
88
80 version;
81 name = "rhodecode-vcsserver-${version}";
89 name = "rhodecode-vcsserver-${version}";
82 releaseName = "RhodeCodeVCSServer-${version}";
90 releaseName = "RhodeCodeVCSServer-${version}";
83 src = rhodecode-vcsserver-src;
91 src = rhodecode-vcsserver-src;
@@ -98,6 +106,13 b' let'
98 export PATH="$out/bin:$PATH"
106 export PATH="$out/bin:$PATH"
99 '';
107 '';
100
108
109 # put custom attrs here
110 checkPhase = ''
111 runHook preCheck
112 PYTHONHASHSEED=random py.test -p no:sugar -vv --cov-config=.coveragerc --cov=vcsserver --cov-report=term-missing vcsserver
113 runHook postCheck
114 '';
115
101 postInstall = ''
116 postInstall = ''
102 echo "Writing meta information for rccontrol to nix-support/rccontrol"
117 echo "Writing meta information for rccontrol to nix-support/rccontrol"
103 mkdir -p $out/nix-support/rccontrol
118 mkdir -p $out/nix-support/rccontrol
@@ -40,16 +40,6 b' self: super: {'
40 '';
40 '';
41 });
41 });
42
42
43 Pyro4 = super.Pyro4.override (attrs: {
44 # TODO: Was not able to generate this version, needs further
45 # investigation.
46 name = "Pyro4-4.35";
47 src = pkgs.fetchurl {
48 url = "https://pypi.python.org/packages/source/P/Pyro4/Pyro4-4.35.src.tar.gz";
49 md5 = "cbe6cb855f086a0f092ca075005855f3";
50 };
51 });
52
53 # Avoid that setuptools is replaced, this leads to trouble
43 # Avoid that setuptools is replaced, this leads to trouble
54 # with buildPythonPackage.
44 # with buildPythonPackage.
55 setuptools = basePythonPackages.setuptools;
45 setuptools = basePythonPackages.setuptools;
@@ -132,6 +132,32 b''
132 license = [ pkgs.lib.licenses.bsdOriginal ];
132 license = [ pkgs.lib.licenses.bsdOriginal ];
133 };
133 };
134 };
134 };
135 cov-core = super.buildPythonPackage {
136 name = "cov-core-1.15.0";
137 buildInputs = with self; [];
138 doCheck = false;
139 propagatedBuildInputs = with self; [coverage];
140 src = fetchurl {
141 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
142 md5 = "f519d4cb4c4e52856afb14af52919fe6";
143 };
144 meta = {
145 license = [ pkgs.lib.licenses.mit ];
146 };
147 };
148 coverage = super.buildPythonPackage {
149 name = "coverage-3.7.1";
150 buildInputs = with self; [];
151 doCheck = false;
152 propagatedBuildInputs = with self; [];
153 src = fetchurl {
154 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
155 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
156 };
157 meta = {
158 license = [ pkgs.lib.licenses.bsdOriginal ];
159 };
160 };
135 decorator = super.buildPythonPackage {
161 decorator = super.buildPythonPackage {
136 name = "decorator-4.0.10";
162 name = "decorator-4.0.10";
137 buildInputs = with self; [];
163 buildInputs = with self; [];
@@ -171,14 +197,40 b''
171 license = [ pkgs.lib.licenses.bsdOriginal ];
197 license = [ pkgs.lib.licenses.bsdOriginal ];
172 };
198 };
173 };
199 };
174 greenlet = super.buildPythonPackage {
200 gevent = super.buildPythonPackage {
175 name = "greenlet-0.4.7";
201 name = "gevent-1.1.2";
202 buildInputs = with self; [];
203 doCheck = false;
204 propagatedBuildInputs = with self; [greenlet];
205 src = fetchurl {
206 url = "https://pypi.python.org/packages/43/8f/cb3224a0e6ab663547f45c10d0651cfd52633fde4283bf68d627084df8cc/gevent-1.1.2.tar.gz";
207 md5 = "bb32a2f852a4997138014d5007215c6e";
208 };
209 meta = {
210 license = [ pkgs.lib.licenses.mit ];
211 };
212 };
213 gprof2dot = super.buildPythonPackage {
214 name = "gprof2dot-2016.10.13";
176 buildInputs = with self; [];
215 buildInputs = with self; [];
177 doCheck = false;
216 doCheck = false;
178 propagatedBuildInputs = with self; [];
217 propagatedBuildInputs = with self; [];
179 src = fetchurl {
218 src = fetchurl {
180 url = "https://pypi.python.org/packages/7a/9f/a1a0d9bdf3203ae1502c5a8434fe89d323599d78a106985bc327351a69d4/greenlet-0.4.7.zip";
219 url = "https://pypi.python.org/packages/a0/e0/73c71baed306f0402a00a94ffc7b2be94ad1296dfcb8b46912655b93154c/gprof2dot-2016.10.13.tar.gz";
181 md5 = "c2333a8ff30fa75c5d5ec0e67b461086";
220 md5 = "0125401f15fd2afe1df686a76c64a4fd";
221 };
222 meta = {
223 license = [ { fullName = "LGPL"; } ];
224 };
225 };
226 greenlet = super.buildPythonPackage {
227 name = "greenlet-0.4.10";
228 buildInputs = with self; [];
229 doCheck = false;
230 propagatedBuildInputs = with self; [];
231 src = fetchurl {
232 url = "https://pypi.python.org/packages/67/62/ca2a95648666eaa2ffeb6a9b3964f21d419ae27f82f2e66b53da5b943fc4/greenlet-0.4.10.zip";
233 md5 = "bed0c4b3b896702131f4d5c72f87c41d";
182 };
234 };
183 meta = {
235 meta = {
184 license = [ pkgs.lib.licenses.mit ];
236 license = [ pkgs.lib.licenses.mit ];
@@ -263,13 +315,13 b''
263 };
315 };
264 };
316 };
265 mercurial = super.buildPythonPackage {
317 mercurial = super.buildPythonPackage {
266 name = "mercurial-3.8.4";
318 name = "mercurial-4.0.2";
267 buildInputs = with self; [];
319 buildInputs = with self; [];
268 doCheck = false;
320 doCheck = false;
269 propagatedBuildInputs = with self; [];
321 propagatedBuildInputs = with self; [];
270 src = fetchurl {
322 src = fetchurl {
271 url = "https://pypi.python.org/packages/bc/16/b66eef0b70ee2b4ebb8e76622fe21bbed834606dd8c1bd30d6936ebf6f45/mercurial-3.8.4.tar.gz";
323 url = "https://pypi.python.org/packages/85/1b/0296aacd697228974a473d2508f013532f987ed6b1bacfe5abd6d5be6332/mercurial-4.0.2.tar.gz";
272 md5 = "cec2c3db688cb87142809089c6ae13e9";
324 md5 = "fa72a08e2723e4fa2a21c4e66437f3fa";
273 };
325 };
274 meta = {
326 meta = {
275 license = [ pkgs.lib.licenses.gpl1 pkgs.lib.licenses.gpl2Plus ];
327 license = [ pkgs.lib.licenses.gpl1 pkgs.lib.licenses.gpl2Plus ];
@@ -289,13 +341,13 b''
289 };
341 };
290 };
342 };
291 msgpack-python = super.buildPythonPackage {
343 msgpack-python = super.buildPythonPackage {
292 name = "msgpack-python-0.4.6";
344 name = "msgpack-python-0.4.8";
293 buildInputs = with self; [];
345 buildInputs = with self; [];
294 doCheck = false;
346 doCheck = false;
295 propagatedBuildInputs = with self; [];
347 propagatedBuildInputs = with self; [];
296 src = fetchurl {
348 src = fetchurl {
297 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
349 url = "https://pypi.python.org/packages/21/27/8a1d82041c7a2a51fcc73675875a5f9ea06c2663e02fcfeb708be1d081a0/msgpack-python-0.4.8.tar.gz";
298 md5 = "8b317669314cf1bc881716cccdaccb30";
350 md5 = "dcd854fb41ee7584ebbf35e049e6be98";
299 };
351 };
300 meta = {
352 meta = {
301 license = [ pkgs.lib.licenses.asl20 ];
353 license = [ pkgs.lib.licenses.asl20 ];
@@ -367,13 +419,13 b''
367 };
419 };
368 };
420 };
369 py = super.buildPythonPackage {
421 py = super.buildPythonPackage {
370 name = "py-1.4.29";
422 name = "py-1.4.31";
371 buildInputs = with self; [];
423 buildInputs = with self; [];
372 doCheck = false;
424 doCheck = false;
373 propagatedBuildInputs = with self; [];
425 propagatedBuildInputs = with self; [];
374 src = fetchurl {
426 src = fetchurl {
375 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
427 url = "https://pypi.python.org/packages/f4/9a/8dfda23f36600dd701c6722316ba8a3ab4b990261f83e7d3ffc6dfedf7ef/py-1.4.31.tar.gz";
376 md5 = "c28e0accba523a29b35a48bb703fb96c";
428 md5 = "5d2c63c56dc3f2115ec35c066ecd582b";
377 };
429 };
378 meta = {
430 meta = {
379 license = [ pkgs.lib.licenses.mit ];
431 license = [ pkgs.lib.licenses.mit ];
@@ -432,18 +484,96 b''
432 };
484 };
433 };
485 };
434 pytest = super.buildPythonPackage {
486 pytest = super.buildPythonPackage {
435 name = "pytest-2.8.5";
487 name = "pytest-3.0.5";
436 buildInputs = with self; [];
488 buildInputs = with self; [];
437 doCheck = false;
489 doCheck = false;
438 propagatedBuildInputs = with self; [py];
490 propagatedBuildInputs = with self; [py];
439 src = fetchurl {
491 src = fetchurl {
440 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
492 url = "https://pypi.python.org/packages/a8/87/b7ca49efe52d2b4169f2bfc49aa5e384173c4619ea8e635f123a0dac5b75/pytest-3.0.5.tar.gz";
441 md5 = "8493b06f700862f1294298d6c1b715a9";
493 md5 = "cefd527b59332688bf5db4a10aa8a7cb";
494 };
495 meta = {
496 license = [ pkgs.lib.licenses.mit ];
497 };
498 };
499 pytest-catchlog = super.buildPythonPackage {
500 name = "pytest-catchlog-1.2.2";
501 buildInputs = with self; [];
502 doCheck = false;
503 propagatedBuildInputs = with self; [py pytest];
504 src = fetchurl {
505 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
506 md5 = "09d890c54c7456c818102b7ff8c182c8";
442 };
507 };
443 meta = {
508 meta = {
444 license = [ pkgs.lib.licenses.mit ];
509 license = [ pkgs.lib.licenses.mit ];
445 };
510 };
446 };
511 };
512 pytest-cov = super.buildPythonPackage {
513 name = "pytest-cov-2.4.0";
514 buildInputs = with self; [];
515 doCheck = false;
516 propagatedBuildInputs = with self; [pytest coverage];
517 src = fetchurl {
518 url = "https://pypi.python.org/packages/00/c0/2bfd1fcdb9d407b8ac8185b1cb5ff458105c6b207a9a7f0e13032de9828f/pytest-cov-2.4.0.tar.gz";
519 md5 = "2fda09677d232acc99ec1b3c5831e33f";
520 };
521 meta = {
522 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
523 };
524 };
525 pytest-profiling = super.buildPythonPackage {
526 name = "pytest-profiling-1.2.2";
527 buildInputs = with self; [];
528 doCheck = false;
529 propagatedBuildInputs = with self; [six pytest gprof2dot];
530 src = fetchurl {
531 url = "https://pypi.python.org/packages/73/e8/804681323bac0bc45c520ec34185ba8469008942266d0074699b204835c1/pytest-profiling-1.2.2.tar.gz";
532 md5 = "0a16d7dda2d23b91e9730fa4558cf728";
533 };
534 meta = {
535 license = [ pkgs.lib.licenses.mit ];
536 };
537 };
538 pytest-runner = super.buildPythonPackage {
539 name = "pytest-runner-2.9";
540 buildInputs = with self; [];
541 doCheck = false;
542 propagatedBuildInputs = with self; [];
543 src = fetchurl {
544 url = "https://pypi.python.org/packages/11/d4/c335ddf94463e451109e3494e909765c3e5205787b772e3b25ee8601b86a/pytest-runner-2.9.tar.gz";
545 md5 = "2212a2e34404b0960b2fdc2c469247b2";
546 };
547 meta = {
548 license = [ pkgs.lib.licenses.mit ];
549 };
550 };
551 pytest-sugar = super.buildPythonPackage {
552 name = "pytest-sugar-0.7.1";
553 buildInputs = with self; [];
554 doCheck = false;
555 propagatedBuildInputs = with self; [pytest termcolor];
556 src = fetchurl {
557 url = "https://pypi.python.org/packages/03/97/05d988b4fa870e7373e8ee4582408543b9ca2bd35c3c67b569369c6f9c49/pytest-sugar-0.7.1.tar.gz";
558 md5 = "7400f7c11f3d572b2c2a3b60352d35fe";
559 };
560 meta = {
561 license = [ pkgs.lib.licenses.bsdOriginal ];
562 };
563 };
564 pytest-timeout = super.buildPythonPackage {
565 name = "pytest-timeout-1.2.0";
566 buildInputs = with self; [];
567 doCheck = false;
568 propagatedBuildInputs = with self; [pytest];
569 src = fetchurl {
570 url = "https://pypi.python.org/packages/cc/b7/b2a61365ea6b6d2e8881360ae7ed8dad0327ad2df89f2f0be4a02304deb2/pytest-timeout-1.2.0.tar.gz";
571 md5 = "83607d91aa163562c7ee835da57d061d";
572 };
573 meta = {
574 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
575 };
576 };
447 repoze.lru = super.buildPythonPackage {
577 repoze.lru = super.buildPythonPackage {
448 name = "repoze.lru-0.6";
578 name = "repoze.lru-0.6";
449 buildInputs = with self; [];
579 buildInputs = with self; [];
@@ -458,36 +588,36 b''
458 };
588 };
459 };
589 };
460 rhodecode-vcsserver = super.buildPythonPackage {
590 rhodecode-vcsserver = super.buildPythonPackage {
461 name = "rhodecode-vcsserver-4.5.2";
591 name = "rhodecode-vcsserver-4.6.0";
462 buildInputs = with self; [mock pytest pytest-sugar WebTest];
592 buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj];
463 doCheck = true;
593 doCheck = true;
464 propagatedBuildInputs = with self; [configobj dulwich hgsubversion infrae.cache mercurial msgpack-python pyramid Pyro4 simplejson subprocess32 waitress WebOb];
594 propagatedBuildInputs = with self; [Beaker configobj dulwich hgsubversion infrae.cache mercurial msgpack-python pyramid pyramid-jinja2 pyramid-mako repoze.lru simplejson subprocess32 subvertpy six translationstring WebOb wheel zope.deprecation zope.interface ipdb gevent greenlet gunicorn waitress Pyro4 serpent pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage];
465 src = ./.;
595 src = ./.;
466 meta = {
596 meta = {
467 license = [ pkgs.lib.licenses.gpl3 { fullName = "GNU General Public License v3 or later (GPLv3+)"; } ];
597 license = [ { fullName = "GPL V3"; } { fullName = "GNU General Public License v3 or later (GPLv3+)"; } ];
468 };
598 };
469 };
599 };
470 serpent = super.buildPythonPackage {
600 serpent = super.buildPythonPackage {
471 name = "serpent-1.12";
601 name = "serpent-1.15";
472 buildInputs = with self; [];
602 buildInputs = with self; [];
473 doCheck = false;
603 doCheck = false;
474 propagatedBuildInputs = with self; [];
604 propagatedBuildInputs = with self; [];
475 src = fetchurl {
605 src = fetchurl {
476 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
606 url = "https://pypi.python.org/packages/7b/38/b2b27673a882ff2ea5871bb3e3e6b496ebbaafd1612e51990ffb158b9254/serpent-1.15.tar.gz";
477 md5 = "05869ac7b062828b34f8f927f0457b65";
607 md5 = "e27b1aad5c218e16442f52abb7c7053a";
478 };
608 };
479 meta = {
609 meta = {
480 license = [ pkgs.lib.licenses.mit ];
610 license = [ pkgs.lib.licenses.mit ];
481 };
611 };
482 };
612 };
483 setuptools = super.buildPythonPackage {
613 setuptools = super.buildPythonPackage {
484 name = "setuptools-20.8.1";
614 name = "setuptools-30.1.0";
485 buildInputs = with self; [];
615 buildInputs = with self; [];
486 doCheck = false;
616 doCheck = false;
487 propagatedBuildInputs = with self; [];
617 propagatedBuildInputs = with self; [];
488 src = fetchurl {
618 src = fetchurl {
489 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
619 url = "https://pypi.python.org/packages/1e/43/002c8616db9a3e7be23c2556e39b90a32bb40ba0dc652de1999d5334d372/setuptools-30.1.0.tar.gz";
490 md5 = "fe58a5cac0df20bb83942b252a4b0543";
620 md5 = "cac497f42e5096ac8df29e38d3f81c3e";
491 };
621 };
492 meta = {
622 meta = {
493 license = [ pkgs.lib.licenses.mit ];
623 license = [ pkgs.lib.licenses.mit ];
@@ -551,13 +681,26 b''
551 doCheck = false;
681 doCheck = false;
552 propagatedBuildInputs = with self; [];
682 propagatedBuildInputs = with self; [];
553 src = fetchurl {
683 src = fetchurl {
554 url = "https://github.com/jelmer/subvertpy/archive/subvertpy-0.9.3.tar.gz";
684 url = "https://code.rhodecode.com/upstream/subvertpy/archive/subvertpy-0.9.3.tar.gz?md5=4e49da2fe07608239cc9a80a7bb8f33c";
555 md5 = "7b745a47128050ea5a73efcd913ec1cf";
685 md5 = "4e49da2fe07608239cc9a80a7bb8f33c";
556 };
686 };
557 meta = {
687 meta = {
558 license = [ pkgs.lib.licenses.lgpl21Plus ];
688 license = [ pkgs.lib.licenses.lgpl21Plus ];
559 };
689 };
560 };
690 };
691 termcolor = super.buildPythonPackage {
692 name = "termcolor-1.1.0";
693 buildInputs = with self; [];
694 doCheck = false;
695 propagatedBuildInputs = with self; [];
696 src = fetchurl {
697 url = "https://pypi.python.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
698 md5 = "043e89644f8909d462fbbfa511c768df";
699 };
700 meta = {
701 license = [ pkgs.lib.licenses.mit ];
702 };
703 };
561 traitlets = super.buildPythonPackage {
704 traitlets = super.buildPythonPackage {
562 name = "traitlets-4.3.1";
705 name = "traitlets-4.3.1";
563 buildInputs = with self; [];
706 buildInputs = with self; [];
@@ -598,13 +741,13 b''
598 };
741 };
599 };
742 };
600 waitress = super.buildPythonPackage {
743 waitress = super.buildPythonPackage {
601 name = "waitress-0.8.9";
744 name = "waitress-1.0.1";
602 buildInputs = with self; [];
745 buildInputs = with self; [];
603 doCheck = false;
746 doCheck = false;
604 propagatedBuildInputs = with self; [setuptools];
747 propagatedBuildInputs = with self; [];
605 src = fetchurl {
748 src = fetchurl {
606 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
749 url = "https://pypi.python.org/packages/78/7d/84d11b96c3f60164dec3bef4a859a03aeae0231aa93f57fbe0d05fa4ff36/waitress-1.0.1.tar.gz";
607 md5 = "da3f2e62b3676be5dd630703a68e2a04";
750 md5 = "dda92358a7569669086155923a46e57c";
608 };
751 };
609 meta = {
752 meta = {
610 license = [ pkgs.lib.licenses.zpt21 ];
753 license = [ pkgs.lib.licenses.zpt21 ];
@@ -637,13 +780,13 b''
637 };
780 };
638 };
781 };
639 zope.deprecation = super.buildPythonPackage {
782 zope.deprecation = super.buildPythonPackage {
640 name = "zope.deprecation-4.1.1";
783 name = "zope.deprecation-4.1.2";
641 buildInputs = with self; [];
784 buildInputs = with self; [];
642 doCheck = false;
785 doCheck = false;
643 propagatedBuildInputs = with self; [setuptools];
786 propagatedBuildInputs = with self; [setuptools];
644 src = fetchurl {
787 src = fetchurl {
645 url = "https://pypi.python.org/packages/c5/c9/e760f131fcde817da6c186a3f4952b8f206b7eeb269bb6f0836c715c5f20/zope.deprecation-4.1.1.tar.gz";
788 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
646 md5 = "ce261b9384066f7e13b63525778430cb";
789 md5 = "e9a663ded58f4f9f7881beb56cae2782";
647 };
790 };
648 meta = {
791 meta = {
649 license = [ pkgs.lib.licenses.zpt21 ];
792 license = [ pkgs.lib.licenses.zpt21 ];
@@ -665,30 +808,5 b''
665
808
666 ### Test requirements
809 ### Test requirements
667
810
668 pytest-sugar = super.buildPythonPackage {
811
669 name = "pytest-sugar-0.7.1";
670 buildInputs = with self; [];
671 doCheck = false;
672 propagatedBuildInputs = with self; [pytest termcolor];
673 src = fetchurl {
674 url = "https://pypi.python.org/packages/03/97/05d988b4fa870e7373e8ee4582408543b9ca2bd35c3c67b569369c6f9c49/pytest-sugar-0.7.1.tar.gz";
675 md5 = "7400f7c11f3d572b2c2a3b60352d35fe";
676 };
677 meta = {
678 license = [ pkgs.lib.licenses.bsdOriginal ];
679 };
680 };
681 termcolor = super.buildPythonPackage {
682 name = "termcolor-1.1.0";
683 buildInputs = with self; [];
684 doCheck = false;
685 propagatedBuildInputs = with self; [];
686 src = fetchurl {
687 url = "https://pypi.python.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
688 md5 = "043e89644f8909d462fbbfa511c768df";
689 };
690 meta = {
691 license = [ pkgs.lib.licenses.mit ];
692 };
693 };
694 }
812 }
@@ -1,35 +1,43 b''
1 # core
2 setuptools==30.1.0
3
1 Beaker==1.7.0
4 Beaker==1.7.0
2 configobj==5.0.6
5 configobj==5.0.6
3 dulwich==0.13.0
6 dulwich==0.13.0
4 hgsubversion==1.8.6
7 hgsubversion==1.8.6
5 infrae.cache==1.0.1
8 infrae.cache==1.0.1
6 ipdb==0.10.1
9 mercurial==4.0.2
7 mercurial==3.8.4
10 msgpack-python==0.4.8
8 msgpack-python==0.4.6
9 py==1.4.29
10 pyramid==1.6.1
11 pyramid==1.6.1
11 pyramid-jinja2==2.5
12 pyramid-jinja2==2.5
12 pyramid-mako==1.0.2
13 pyramid-mako==1.0.2
13 Pyro4==4.41
14 pytest==2.8.5
15 repoze.lru==0.6
14 repoze.lru==0.6
16 serpent==1.12
17 setuptools==20.8.1
18 simplejson==3.7.2
15 simplejson==3.7.2
19 subprocess32==3.2.6
16 subprocess32==3.2.6
20 # TODO: johbo: This version is not in source on PyPI currently,
17
21 # change back once this or a future version is available
18 # Custom subvertpy that is not available on pypi.
22 https://github.com/jelmer/subvertpy/archive/subvertpy-0.9.3.tar.gz#md5=7b745a47128050ea5a73efcd913ec1cf
19 https://code.rhodecode.com/upstream/subvertpy/archive/subvertpy-0.9.3.tar.gz?md5=4e49da2fe07608239cc9a80a7bb8f33c#egg=subvertpy==0.9.3
20
23 six==1.9.0
21 six==1.9.0
24 translationstring==1.3
22 translationstring==1.3
25 waitress==0.8.9
26 WebOb==1.3.1
23 WebOb==1.3.1
27 wheel==0.29.0
24 wheel==0.29.0
28 zope.deprecation==4.1.1
25 zope.deprecation==4.1.2
29 zope.interface==4.1.3
26 zope.interface==4.1.3
30 greenlet==0.4.7
27
28 ## debug
29 ipdb==0.10.1
30 ipython==5.1.0
31
32 # http servers
33 gevent==1.1.2
34 greenlet==0.4.10
31 gunicorn==19.6.0
35 gunicorn==19.6.0
36 waitress==1.0.1
32
37
33 # Test related requirements
38 # Pyro/Deprecated TODO(Marcink): remove in 4.7 release.
34 mock==1.0.1
39 Pyro4==4.41
35 WebTest==1.4.3
40 serpent==1.15
41
42 ## test related requirements
43 -r requirements_test.txt
@@ -1,5 +1,6 b''
1 # -*- coding: utf-8 -*-
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
3 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
4 #
4 # This program is free software; you can redistribute it and/or modify
5 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # it under the terms of the GNU General Public License as published by
@@ -15,18 +16,57 b''
15 # along with this program; if not, write to the Free Software Foundation,
16 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18
19 # Import early to make sure things are patched up properly
18 from setuptools import setup, find_packages
20 from setuptools import setup, find_packages
19 from setuptools.command.test import test as TestCommand
21
22 import os
23 import sys
24 import pkgutil
25 import platform
26
27 from pip.download import PipSession
28 from pip.req import parse_requirements
29
20 from codecs import open
30 from codecs import open
21 from os import path
22 import pkgutil
23 import sys
24
31
25
32
26 here = path.abspath(path.dirname(__file__))
33 if sys.version_info < (2, 7):
34 raise Exception('VCSServer requires Python 2.7 or later')
35
36 here = os.path.abspath(os.path.dirname(__file__))
37
38 # defines current platform
39 __platform__ = platform.system()
40 __license__ = 'GPL V3'
41 __author__ = 'RhodeCode GmbH'
42 __url__ = 'https://code.rhodecode.com'
43 is_windows = __platform__ in ('Windows',)
44
45
46 def _get_requirements(req_filename, exclude=None, extras=None):
47 extras = extras or []
48 exclude = exclude or []
27
49
28 with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
50 try:
29 long_description = f.read()
51 parsed = parse_requirements(
52 os.path.join(here, req_filename), session=PipSession())
53 except TypeError:
54 # try pip < 6.0.0, that doesn't support session
55 parsed = parse_requirements(os.path.join(here, req_filename))
56
57 requirements = []
58 for ir in parsed:
59 if ir.req and ir.name not in exclude:
60 requirements.append(str(ir.req))
61 return requirements + extras
62
63
64 # requirements extract
65 setup_requirements = ['pytest-runner']
66 install_requirements = _get_requirements(
67 'requirements.txt', exclude=['setuptools'])
68 test_requirements = _get_requirements(
69 'requirements_test.txt', extras=['configobj'])
30
70
31
71
32 def get_version():
72 def get_version():
@@ -34,66 +74,55 b' def get_version():'
34 return version.strip()
74 return version.strip()
35
75
36
76
37 class PyTest(TestCommand):
77 # additional files that goes into package itself
38 user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
78 package_data = {
79 '': ['*.txt', '*.rst'],
80 'configs': ['*.ini'],
81 'vcsserver': ['VERSION'],
82 }
39
83
40 def initialize_options(self):
84 description = 'Version Control System Server'
41 TestCommand.initialize_options(self)
85 keywords = ' '.join([
42 self.pytest_args = []
86 'CLI', 'RhodeCode', 'RhodeCode Enterprise', 'RhodeCode Tools'])
43
87
44 def finalize_options(self):
88 # README/DESCRIPTION generation
45 TestCommand.finalize_options(self)
89 readme_file = 'README.rst'
46 self.test_args = []
90 changelog_file = 'CHANGES.rst'
47 self.test_suite = True
91 try:
48
92 long_description = open(readme_file).read() + '\n\n' + \
49 def run_tests(self):
93 open(changelog_file).read()
50 # import here, cause outside the eggs aren't loaded
94 except IOError as err:
51 import pytest
95 sys.stderr.write(
52 errno = pytest.main(self.pytest_args)
96 "[WARNING] Cannot find file specified as long_description (%s)\n "
53 sys.exit(errno)
97 "or changelog (%s) skipping that file" % (readme_file, changelog_file))
98 long_description = description
54
99
55
100
56 setup(
101 setup(
57 name='rhodecode-vcsserver',
102 name='rhodecode-vcsserver',
58 version=get_version(),
103 version=get_version(),
59 description='Version Control System Server',
104 description=description,
60 long_description=long_description,
105 long_description=long_description,
61 url='http://www.rhodecode.com',
106 keywords=keywords,
62 author='RhodeCode GmbH',
107 license=__license__,
108 author=__author__,
63 author_email='marcin@rhodecode.com',
109 author_email='marcin@rhodecode.com',
64 cmdclass={'test': PyTest},
110 url=__url__,
65 license='GPLv3',
111 setup_requires=setup_requirements,
112 install_requires=install_requirements,
113 tests_require=test_requirements,
114 zip_safe=False,
115 packages=find_packages(exclude=["docs", "tests*"]),
116 package_data=package_data,
117 include_package_data=True,
66 classifiers=[
118 classifiers=[
67 'Development Status :: 5 - Production/Stable',
119 'Development Status :: 6 - Mature',
68 'Intended Audience :: Developers',
120 'Intended Audience :: Developers',
121 'Operating System :: OS Independent',
69 'Topic :: Software Development :: Version Control',
122 'Topic :: Software Development :: Version Control',
70 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
123 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
71 'Programming Language :: Python :: 2.7',
124 'Programming Language :: Python :: 2.7',
72 ],
125 ],
73 packages=find_packages(),
74 tests_require=[
75 'mock',
76 'pytest',
77 'pytest-sugar',
78 'WebTest',
79 ],
80 install_requires=[
81 'configobj',
82 'dulwich',
83 'hgsubversion',
84 'infrae.cache',
85 'mercurial',
86 'msgpack-python',
87 'pyramid',
88 'Pyro4',
89 'simplejson',
90 'subprocess32',
91 'waitress',
92 'WebOb',
93 ],
94 package_data={
95 'vcsserver': ['VERSION'],
96 },
97 entry_points={
126 entry_points={
98 'console_scripts': [
127 'console_scripts': [
99 'vcsserver=vcsserver.main:main',
128 'vcsserver=vcsserver.main:main',
@@ -1,1 +1,1 b''
1 4.5.2 No newline at end of file
1 4.6.0 No newline at end of file
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -389,14 +389,14 b' class HgRemote(object):'
389 repo = self._factory.repo(wire)
389 repo = self._factory.repo(wire)
390
390
391 if file_filter:
391 if file_filter:
392 filter = match(file_filter[0], '', [file_filter[1]])
392 match_filter = match(file_filter[0], '', [file_filter[1]])
393 else:
393 else:
394 filter = file_filter
394 match_filter = file_filter
395 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
395 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
396
396
397 try:
397 try:
398 return "".join(patch.diff(
398 return "".join(patch.diff(
399 repo, node1=rev1, node2=rev2, match=filter, opts=opts))
399 repo, node1=rev1, node2=rev2, match=match_filter, opts=opts))
400 except RepoLookupError:
400 except RepoLookupError:
401 raise exceptions.LookupException()
401 raise exceptions.LookupException()
402
402
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,7 +1,7 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # RhodeCode VCSServer provides access to different vcs backends via network.
3 # RhodeCode VCSServer provides access to different vcs backends via network.
4 # Copyright (C) 2014-2016 RodeCode GmbH
4 # Copyright (C) 2014-2017 RodeCode GmbH
5 #
5 #
6 # This program is free software; you can redistribute it and/or modify
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
7 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 b' import locale'
20 import logging
20 import logging
21 import uuid
21 import uuid
22 import wsgiref.util
22 import wsgiref.util
23 import traceback
23 from itertools import chain
24 from itertools import chain
24
25
25 import msgpack
26 import msgpack
@@ -154,7 +155,7 b' class HTTPApplication(object):'
154
155
155 def __init__(self, settings=None):
156 def __init__(self, settings=None):
156 self.config = Configurator(settings=settings)
157 self.config = Configurator(settings=settings)
157 locale = settings.get('', 'en_US.UTF-8')
158 locale = settings.get('locale', '') or 'en_US.UTF-8'
158 vcs = VCS(locale=locale, cache_config=settings)
159 vcs = VCS(locale=locale, cache_config=settings)
159 self._remotes = {
160 self._remotes = {
160 'hg': vcs._hg_remote,
161 'hg': vcs._hg_remote,
@@ -198,14 +199,27 b' class HTTPApplication(object):'
198 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
199 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
199 self.config.add_view(self.git_proxy(), route_name='git_proxy')
200 self.config.add_view(self.git_proxy(), route_name='git_proxy')
200 self.config.add_view(
201 self.config.add_view(
201 self.vcs_view, route_name='vcs', renderer='msgpack')
202 self.vcs_view, route_name='vcs', renderer='msgpack',
203 custom_predicates=[self.is_vcs_view])
202
204
203 self.config.add_view(self.hg_stream(), route_name='stream_hg')
205 self.config.add_view(self.hg_stream(), route_name='stream_hg')
204 self.config.add_view(self.git_stream(), route_name='stream_git')
206 self.config.add_view(self.git_stream(), route_name='stream_git')
207
208 def notfound(request):
209 return {'status': '404 NOT FOUND'}
210 self.config.add_notfound_view(notfound, renderer='json')
211
205 self.config.add_view(
212 self.config.add_view(
206 self.handle_vcs_exception, context=Exception,
213 self.handle_vcs_exception, context=Exception,
207 custom_predicates=[self.is_vcs_exception])
214 custom_predicates=[self.is_vcs_exception])
208
215
216 self.config.add_view(
217 self.general_error_handler, context=Exception)
218
219 self.config.add_tween(
220 'vcsserver.tweens.RequestWrapperTween',
221 )
222
209 def wsgi_app(self):
223 def wsgi_app(self):
210 return self.config.make_wsgi_app()
224 return self.config.make_wsgi_app()
211
225
@@ -224,9 +238,12 b' class HTTPApplication(object):'
224 pass
238 pass
225 args.insert(0, wire)
239 args.insert(0, wire)
226
240
241 log.debug('method called:%s with kwargs:%s', method, kwargs)
227 try:
242 try:
228 resp = getattr(remote, method)(*args, **kwargs)
243 resp = getattr(remote, method)(*args, **kwargs)
229 except Exception as e:
244 except Exception as e:
245 tb_info = traceback.format_exc()
246
230 type_ = e.__class__.__name__
247 type_ = e.__class__.__name__
231 if type_ not in self.ALLOWED_EXCEPTIONS:
248 if type_ not in self.ALLOWED_EXCEPTIONS:
232 type_ = None
249 type_ = None
@@ -235,6 +252,7 b' class HTTPApplication(object):'
235 'id': payload.get('id'),
252 'id': payload.get('id'),
236 'error': {
253 'error': {
237 'message': e.message,
254 'message': e.message,
255 'traceback': tb_info,
238 'type': type_
256 'type': type_
239 }
257 }
240 }
258 }
@@ -338,6 +356,14 b' class HTTPApplication(object):'
338 return app(environ, start_response)
356 return app(environ, start_response)
339 return _git_stream
357 return _git_stream
340
358
359 def is_vcs_view(self, context, request):
360 """
361 View predicate that returns true if given backend is supported by
362 defined remotes.
363 """
364 backend = request.matchdict.get('backend')
365 return backend in self._remotes
366
341 def is_vcs_exception(self, context, request):
367 def is_vcs_exception(self, context, request):
342 """
368 """
343 View predicate that returns true if the context object is a VCS
369 View predicate that returns true if the context object is a VCS
@@ -355,6 +381,12 b' class HTTPApplication(object):'
355 # Re-raise exception if we can not handle it.
381 # Re-raise exception if we can not handle it.
356 raise exception
382 raise exception
357
383
384 def general_error_handler(self, exception, request):
385 log.exception(
386 'error occurred handling this request for path: %s',
387 request.path)
388 raise exception
389
358
390
359 class ResponseFilter(object):
391 class ResponseFilter(object):
360
392
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -375,11 +375,18 b' class SvnRemote(object):'
375
375
376 def diff(self, wire, rev1, rev2, path1=None, path2=None,
376 def diff(self, wire, rev1, rev2, path1=None, path2=None,
377 ignore_whitespace=False, context=3):
377 ignore_whitespace=False, context=3):
378
378 wire.update(cache=False)
379 wire.update(cache=False)
379 repo = self._factory.repo(wire)
380 repo = self._factory.repo(wire)
380 diff_creator = SvnDiffer(
381 diff_creator = SvnDiffer(
381 repo, rev1, path1, rev2, path2, ignore_whitespace, context)
382 repo, rev1, path1, rev2, path2, ignore_whitespace, context)
382 return diff_creator.generate_diff()
383 try:
384 return diff_creator.generate_diff()
385 except svn.core.SubversionException as e:
386 log.exception(
387 "Error during diff operation operation. "
388 "Path might not exist %s, %s" % (path1, path2))
389 return ""
383
390
384
391
385 class SvnDiffer(object):
392 class SvnDiffer(object):
@@ -450,23 +457,30 b' class SvnDiffer(object):'
450 buf, change, path, self.tgt_path, path, self.src_path)
457 buf, change, path, self.tgt_path, path, self.src_path)
451
458
452 def _generate_file_diff(self, buf):
459 def _generate_file_diff(self, buf):
453 change = None
460 change = None
454 if self.src_kind == svn.core.svn_node_none:
461 if self.src_kind == svn.core.svn_node_none:
455 change = "add"
462 change = "add"
456 elif self.tgt_kind == svn.core.svn_node_none:
463 elif self.tgt_kind == svn.core.svn_node_none:
457 change = "delete"
464 change = "delete"
458 tgt_base, tgt_path = vcspath.split(self.tgt_path)
465 tgt_base, tgt_path = vcspath.split(self.tgt_path)
459 src_base, src_path = vcspath.split(self.src_path)
466 src_base, src_path = vcspath.split(self.src_path)
460 self._generate_node_diff(
467 self._generate_node_diff(
461 buf, change, tgt_path, tgt_base, src_path, src_base)
468 buf, change, tgt_path, tgt_base, src_path, src_base)
462
469
463 def _generate_node_diff(
470 def _generate_node_diff(
464 self, buf, change, tgt_path, tgt_base, src_path, src_base):
471 self, buf, change, tgt_path, tgt_base, src_path, src_base):
472
473 if self.src_rev == self.tgt_rev and tgt_base == src_base:
474 # makes consistent behaviour with git/hg to return empty diff if
475 # we compare same revisions
476 return
477
465 tgt_full_path = vcspath.join(tgt_base, tgt_path)
478 tgt_full_path = vcspath.join(tgt_base, tgt_path)
466 src_full_path = vcspath.join(src_base, src_path)
479 src_full_path = vcspath.join(src_base, src_path)
467
480
468 self.binary_content = False
481 self.binary_content = False
469 mime_type = self._get_mime_type(tgt_full_path)
482 mime_type = self._get_mime_type(tgt_full_path)
483
470 if mime_type and not mime_type.startswith('text'):
484 if mime_type and not mime_type.startswith('text'):
471 self.binary_content = True
485 self.binary_content = True
472 buf.write("=" * 67 + '\n')
486 buf.write("=" * 67 + '\n')
@@ -480,11 +494,21 b' class SvnDiffer(object):'
480 if change == 'add':
494 if change == 'add':
481 # TODO: johbo: SVN is missing a zero here compared to git
495 # TODO: johbo: SVN is missing a zero here compared to git
482 buf.write("new file mode 10644\n")
496 buf.write("new file mode 10644\n")
497
498 #TODO(marcink): intro to binary detection of svn patches
499 # if self.binary_content:
500 # buf.write('GIT binary patch\n')
501
483 buf.write("--- /dev/null\t(revision 0)\n")
502 buf.write("--- /dev/null\t(revision 0)\n")
484 src_lines = []
503 src_lines = []
485 else:
504 else:
486 if change == 'delete':
505 if change == 'delete':
487 buf.write("deleted file mode 10644\n")
506 buf.write("deleted file mode 10644\n")
507
508 #TODO(marcink): intro to binary detection of svn patches
509 # if self.binary_content:
510 # buf.write('GIT binary patch\n')
511
488 buf.write("--- a/%s\t(revision %s)\n" % (
512 buf.write("--- a/%s\t(revision %s)\n" % (
489 src_path, self.src_rev))
513 src_path, self.src_rev))
490 src_lines = self._svn_readlines(self.src_root, src_full_path)
514 src_lines = self._svn_readlines(self.src_root, src_full_path)
@@ -76,12 +76,14 b' def filter_ignorable_lines(hunks, fromli'
76 elif tag == 'replace' and (ignore_case or ignore_space_changes):
76 elif tag == 'replace' and (ignore_case or ignore_space_changes):
77 if len(fromlines) != len(tolines):
77 if len(fromlines) != len(tolines):
78 return False
78 return False
79 def f(str):
79
80 def f(input_str):
80 if ignore_case:
81 if ignore_case:
81 str = str.lower()
82 input_str = input_str.lower()
82 if ignore_space_changes:
83 if ignore_space_changes:
83 str = ' '.join(str.split())
84 input_str = ' '.join(input_str.split())
84 return str
85 return input_str
86
85 for i in range(len(fromlines)):
87 for i in range(len(fromlines)):
86 if f(fromlines[i]) != f(tolines[i]):
88 if f(fromlines[i]) != f(tolines[i]):
87 return False
89 return False
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 b' import tempfile'
22 import configobj
22 import configobj
23
23
24
24
25 class TestINI(object):
25 class ContextINI(object):
26 """
26 """
27 Allows to create a new test.ini file as a copy of existing one with edited
27 Allows to create a new test.ini file as a copy of existing one with edited
28 data. If existing file is not present, it creates a new one. Example usage::
28 data. If existing file is not present, it creates a new one. Example usage::
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
1 NO CONTENT: file renamed from tests/test_http_performance.py to vcsserver/tests/test_http_performance.py
NO CONTENT: file renamed from tests/test_http_performance.py to vcsserver/tests/test_http_performance.py
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -40,7 +40,7 b" INVALID_CERTIFICATE_STDERR = '\\n'.join(["
40 @pytest.mark.parametrize('stderr,expected_reason', [
40 @pytest.mark.parametrize('stderr,expected_reason', [
41 (INVALID_CERTIFICATE_STDERR, 'INVALID_CERTIFICATE'),
41 (INVALID_CERTIFICATE_STDERR, 'INVALID_CERTIFICATE'),
42 ('svnrdump: E123456', 'UNKNOWN'),
42 ('svnrdump: E123456', 'UNKNOWN'),
43 ])
43 ], ids=['invalid-cert-stderr', 'svnrdump-err-123456'])
44 @pytest.mark.xfail(sys.platform == "cygwin",
44 @pytest.mark.xfail(sys.platform == "cygwin",
45 reason="SVN not packaged for Cygwin")
45 reason="SVN not packaged for Cygwin")
46 def test_import_remote_repository_certificate_error(stderr, expected_reason):
46 def test_import_remote_repository_certificate_error(stderr, expected_reason):
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 b' import time'
21
21
22 import pytest
22 import pytest
23
23
24 from fixture import TestINI
24 from fixture import ContextINI
25
25
26
26
27 @pytest.mark.parametrize("arguments, expected_texts", [
27 @pytest.mark.parametrize("arguments, expected_texts", [
@@ -66,7 +66,7 b' def test_vcsserver_with_config(vcsserver'
66 {'DEFAULT': {'port': vcsserver_port}},
66 {'DEFAULT': {'port': vcsserver_port}},
67 ]
67 ]
68
68
69 with TestINI('test.ini', ini_def) as new_test_ini_path:
69 with ContextINI('test.ini', ini_def) as new_test_ini_path:
70 output = call_vcs_server_with_arguments(
70 output = call_vcs_server_with_arguments(
71 ['--config=' + new_test_ini_path])
71 ['--config=' + new_test_ini_path])
72
72
@@ -85,7 +85,7 b' def test_vcsserver_with_config_cli_overw'
85 {'DEFAULT': {'threadpool_size': '111'}},
85 {'DEFAULT': {'threadpool_size': '111'}},
86 {'DEFAULT': {'timeout': '0'}},
86 {'DEFAULT': {'timeout': '0'}},
87 ]
87 ]
88 with TestINI('test.ini', ini_def) as new_test_ini_path:
88 with ContextINI('test.ini', ini_def) as new_test_ini_path:
89 output = call_vcs_server_with_arguments([
89 output = call_vcs_server_with_arguments([
90 '--config=' + new_test_ini_path,
90 '--config=' + new_test_ini_path,
91 '--host=128.0.0.1',
91 '--host=128.0.0.1',
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2016 RodeCode GmbH
2 # Copyright (C) 2014-2017 RodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
General Comments 0
You need to be logged in to leave comments. Login now