diff --git a/.bumpversion.cfg b/.bumpversion.cfg --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.5.2 +current_version = 4.6.0 message = release: Bump version {current_version} to {new_version} [bumpversion:file:vcsserver/VERSION] diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -2,9 +2,11 @@ syntax: glob *.orig *.pyc *.swp +*.sqlite *.tox *.egg-info *.egg +*.eggs *.idea .DS_Store* diff --git a/.release.cfg b/.release.cfg --- a/.release.cfg +++ b/.release.cfg @@ -5,12 +5,10 @@ done = false done = true [task:fixes_on_stable] -done = true [task:pip2nix_generated] -done = true [release] -state = prepared -version = 4.5.2 +state = in_progress +version = 4.6.0 diff --git a/MANIFEST.in b/MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,15 +1,13 @@ # top level files -include test.ini -include MANIFEST.in -include README.rst -include CHANGES.rst -include LICENSE.txt +include *.rst +include *.txt +# package extras include vcsserver/VERSION # all config files recursive-include configs * # skip any tests files -recursive-exclude tests * +recursive-exclude vcsserver/tests * diff --git a/Makefile b/Makefile new file mode 100644 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ + +.PHONY: clean test test-clean test-only + + +clean: + make test-clean + find . -type f \( -iname '*.c' -o -iname '*.pyc' -o -iname '*.so' \) -exec rm '{}' ';' + +test: + make test-clean + make test-only + +test-clean: + rm -rf coverage.xml htmlcov junit.xml pylint.log result + find . -type d -name "__pycache__" -prune -exec rm -rf '{}' ';' + +test-only: + PYTHONHASHSEED=random py.test -vv -r xw --cov=vcsserver --cov-report=term-missing --cov-report=html vcsserver diff --git a/default.nix b/default.nix --- a/default.nix +++ b/default.nix @@ -14,6 +14,16 @@ let pkgs_ = pkgs; in let pkgs = pkgs_.overridePackages (self: super: { + # bump GIT version + git = pkgs.lib.overrideDerivation pkgs_.git (oldAttrs: { + name = "git-2.9.3"; + src = pkgs.fetchurl { + url = "https://www.kernel.org/pub/software/scm/git/git-2.9.3.tar.xz"; + sha256 = "0qzs681a64k3shh5p0rg41l1z16fbk5sj0xga45k34hp1hsp654z"; + }; + + }); + # Override subversion derivation to # - activate python bindings subversion = let @@ -22,17 +32,20 @@ let pythonBindings = true; python = self.python27Packages.python; }; - in pkgs.lib.overrideDerivation subversionWithPython (oldAttrs: { + + in + + pkgs.lib.overrideDerivation subversionWithPython (oldAttrs: { patches = (oldAttrs.patches or []) ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ # johbo: "import svn.client" fails on darwin currently. ./pkgs/subversion-1.9.4-darwin.patch ]; }); + }); inherit (pkgs.lib) fix extends; - basePythonPackages = with builtins; if isAttrs pythonPackages then pythonPackages else getAttr pythonPackages pkgs; @@ -47,9 +60,8 @@ let let ext = last (splitString "." path); in - !elem (basename path) [ - ".git" ".hg" "__pycache__" ".eggs" "node_modules" - "build" "data" "tmp"] && + !elem (basename path) [".hg" ".git" "__pycache__" ".eggs" + "node_modules" "build" "data" "tmp"] && !elem ext ["egg-info" "pyc"] && !startsWith "result" path; @@ -57,8 +69,7 @@ let pythonGeneratedPackages = self: basePythonPackages.override (a: { inherit self; - }) - // (scopedImport { + }) // (scopedImport { self = self; super = basePythonPackages; inherit pkgs; @@ -66,18 +77,15 @@ let } ./pkgs/python-packages.nix); pythonOverrides = import ./pkgs/python-packages-overrides.nix { - inherit - basePythonPackages - pkgs; + inherit basePythonPackages pkgs; }; version = builtins.readFile ./vcsserver/VERSION; pythonLocalOverrides = self: super: { rhodecode-vcsserver = super.rhodecode-vcsserver.override (attrs: { - inherit - doCheck - version; + inherit doCheck version; + name = "rhodecode-vcsserver-${version}"; releaseName = "RhodeCodeVCSServer-${version}"; src = rhodecode-vcsserver-src; @@ -98,6 +106,13 @@ let export PATH="$out/bin:$PATH" ''; + # put custom attrs here + checkPhase = '' + runHook preCheck + PYTHONHASHSEED=random py.test -p no:sugar -vv --cov-config=.coveragerc --cov=vcsserver --cov-report=term-missing vcsserver + runHook postCheck + ''; + postInstall = '' echo "Writing meta information for rccontrol to nix-support/rccontrol" mkdir -p $out/nix-support/rccontrol diff --git a/pkgs/python-packages-overrides.nix b/pkgs/python-packages-overrides.nix --- a/pkgs/python-packages-overrides.nix +++ b/pkgs/python-packages-overrides.nix @@ -40,16 +40,6 @@ self: super: { ''; }); - Pyro4 = super.Pyro4.override (attrs: { - # TODO: Was not able to generate this version, needs further - # investigation. - name = "Pyro4-4.35"; - src = pkgs.fetchurl { - url = "https://pypi.python.org/packages/source/P/Pyro4/Pyro4-4.35.src.tar.gz"; - md5 = "cbe6cb855f086a0f092ca075005855f3"; - }; - }); - # Avoid that setuptools is replaced, this leads to trouble # with buildPythonPackage. setuptools = basePythonPackages.setuptools; diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -132,6 +132,32 @@ license = [ pkgs.lib.licenses.bsdOriginal ]; }; }; + cov-core = super.buildPythonPackage { + name = "cov-core-1.15.0"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [coverage]; + src = fetchurl { + url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz"; + md5 = "f519d4cb4c4e52856afb14af52919fe6"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; + coverage = super.buildPythonPackage { + name = "coverage-3.7.1"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; []; + src = fetchurl { + url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz"; + md5 = "c47b36ceb17eaff3ecfab3bcd347d0df"; + }; + meta = { + license = [ pkgs.lib.licenses.bsdOriginal ]; + }; + }; decorator = super.buildPythonPackage { name = "decorator-4.0.10"; buildInputs = with self; []; @@ -171,14 +197,40 @@ license = [ pkgs.lib.licenses.bsdOriginal ]; }; }; - greenlet = super.buildPythonPackage { - name = "greenlet-0.4.7"; + gevent = super.buildPythonPackage { + name = "gevent-1.1.2"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [greenlet]; + src = fetchurl { + url = "https://pypi.python.org/packages/43/8f/cb3224a0e6ab663547f45c10d0651cfd52633fde4283bf68d627084df8cc/gevent-1.1.2.tar.gz"; + md5 = "bb32a2f852a4997138014d5007215c6e"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; + gprof2dot = super.buildPythonPackage { + name = "gprof2dot-2016.10.13"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/7a/9f/a1a0d9bdf3203ae1502c5a8434fe89d323599d78a106985bc327351a69d4/greenlet-0.4.7.zip"; - md5 = "c2333a8ff30fa75c5d5ec0e67b461086"; + url = "https://pypi.python.org/packages/a0/e0/73c71baed306f0402a00a94ffc7b2be94ad1296dfcb8b46912655b93154c/gprof2dot-2016.10.13.tar.gz"; + md5 = "0125401f15fd2afe1df686a76c64a4fd"; + }; + meta = { + license = [ { fullName = "LGPL"; } ]; + }; + }; + greenlet = super.buildPythonPackage { + name = "greenlet-0.4.10"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; []; + src = fetchurl { + url = "https://pypi.python.org/packages/67/62/ca2a95648666eaa2ffeb6a9b3964f21d419ae27f82f2e66b53da5b943fc4/greenlet-0.4.10.zip"; + md5 = "bed0c4b3b896702131f4d5c72f87c41d"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -263,13 +315,13 @@ }; }; mercurial = super.buildPythonPackage { - name = "mercurial-3.8.4"; + name = "mercurial-4.0.2"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/bc/16/b66eef0b70ee2b4ebb8e76622fe21bbed834606dd8c1bd30d6936ebf6f45/mercurial-3.8.4.tar.gz"; - md5 = "cec2c3db688cb87142809089c6ae13e9"; + url = "https://pypi.python.org/packages/85/1b/0296aacd697228974a473d2508f013532f987ed6b1bacfe5abd6d5be6332/mercurial-4.0.2.tar.gz"; + md5 = "fa72a08e2723e4fa2a21c4e66437f3fa"; }; meta = { license = [ pkgs.lib.licenses.gpl1 pkgs.lib.licenses.gpl2Plus ]; @@ -289,13 +341,13 @@ }; }; msgpack-python = super.buildPythonPackage { - name = "msgpack-python-0.4.6"; + name = "msgpack-python-0.4.8"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz"; - md5 = "8b317669314cf1bc881716cccdaccb30"; + url = "https://pypi.python.org/packages/21/27/8a1d82041c7a2a51fcc73675875a5f9ea06c2663e02fcfeb708be1d081a0/msgpack-python-0.4.8.tar.gz"; + md5 = "dcd854fb41ee7584ebbf35e049e6be98"; }; meta = { license = [ pkgs.lib.licenses.asl20 ]; @@ -367,13 +419,13 @@ }; }; py = super.buildPythonPackage { - name = "py-1.4.29"; + name = "py-1.4.31"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz"; - md5 = "c28e0accba523a29b35a48bb703fb96c"; + url = "https://pypi.python.org/packages/f4/9a/8dfda23f36600dd701c6722316ba8a3ab4b990261f83e7d3ffc6dfedf7ef/py-1.4.31.tar.gz"; + md5 = "5d2c63c56dc3f2115ec35c066ecd582b"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -432,18 +484,96 @@ }; }; pytest = super.buildPythonPackage { - name = "pytest-2.8.5"; + name = "pytest-3.0.5"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; [py]; src = fetchurl { - url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip"; - md5 = "8493b06f700862f1294298d6c1b715a9"; + url = "https://pypi.python.org/packages/a8/87/b7ca49efe52d2b4169f2bfc49aa5e384173c4619ea8e635f123a0dac5b75/pytest-3.0.5.tar.gz"; + md5 = "cefd527b59332688bf5db4a10aa8a7cb"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; + pytest-catchlog = super.buildPythonPackage { + name = "pytest-catchlog-1.2.2"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [py pytest]; + src = fetchurl { + url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip"; + md5 = "09d890c54c7456c818102b7ff8c182c8"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; + pytest-cov = super.buildPythonPackage { + name = "pytest-cov-2.4.0"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [pytest coverage]; + src = fetchurl { + url = "https://pypi.python.org/packages/00/c0/2bfd1fcdb9d407b8ac8185b1cb5ff458105c6b207a9a7f0e13032de9828f/pytest-cov-2.4.0.tar.gz"; + md5 = "2fda09677d232acc99ec1b3c5831e33f"; + }; + meta = { + license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ]; + }; + }; + pytest-profiling = super.buildPythonPackage { + name = "pytest-profiling-1.2.2"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [six pytest gprof2dot]; + src = fetchurl { + url = "https://pypi.python.org/packages/73/e8/804681323bac0bc45c520ec34185ba8469008942266d0074699b204835c1/pytest-profiling-1.2.2.tar.gz"; + md5 = "0a16d7dda2d23b91e9730fa4558cf728"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; + pytest-runner = super.buildPythonPackage { + name = "pytest-runner-2.9"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; []; + src = fetchurl { + url = "https://pypi.python.org/packages/11/d4/c335ddf94463e451109e3494e909765c3e5205787b772e3b25ee8601b86a/pytest-runner-2.9.tar.gz"; + md5 = "2212a2e34404b0960b2fdc2c469247b2"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; + pytest-sugar = super.buildPythonPackage { + name = "pytest-sugar-0.7.1"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [pytest termcolor]; + src = fetchurl { + url = "https://pypi.python.org/packages/03/97/05d988b4fa870e7373e8ee4582408543b9ca2bd35c3c67b569369c6f9c49/pytest-sugar-0.7.1.tar.gz"; + md5 = "7400f7c11f3d572b2c2a3b60352d35fe"; + }; + meta = { + license = [ pkgs.lib.licenses.bsdOriginal ]; + }; + }; + pytest-timeout = super.buildPythonPackage { + name = "pytest-timeout-1.2.0"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; [pytest]; + src = fetchurl { + url = "https://pypi.python.org/packages/cc/b7/b2a61365ea6b6d2e8881360ae7ed8dad0327ad2df89f2f0be4a02304deb2/pytest-timeout-1.2.0.tar.gz"; + md5 = "83607d91aa163562c7ee835da57d061d"; + }; + meta = { + license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ]; + }; + }; repoze.lru = super.buildPythonPackage { name = "repoze.lru-0.6"; buildInputs = with self; []; @@ -458,36 +588,36 @@ }; }; rhodecode-vcsserver = super.buildPythonPackage { - name = "rhodecode-vcsserver-4.5.2"; - buildInputs = with self; [mock pytest pytest-sugar WebTest]; + name = "rhodecode-vcsserver-4.6.0"; + buildInputs = with self; [pytest py pytest-cov pytest-sugar pytest-runner pytest-catchlog pytest-profiling gprof2dot pytest-timeout mock WebTest cov-core coverage configobj]; doCheck = true; - propagatedBuildInputs = with self; [configobj dulwich hgsubversion infrae.cache mercurial msgpack-python pyramid Pyro4 simplejson subprocess32 waitress WebOb]; + 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]; src = ./.; meta = { - license = [ pkgs.lib.licenses.gpl3 { fullName = "GNU General Public License v3 or later (GPLv3+)"; } ]; + license = [ { fullName = "GPL V3"; } { fullName = "GNU General Public License v3 or later (GPLv3+)"; } ]; }; }; serpent = super.buildPythonPackage { - name = "serpent-1.12"; + name = "serpent-1.15"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz"; - md5 = "05869ac7b062828b34f8f927f0457b65"; + url = "https://pypi.python.org/packages/7b/38/b2b27673a882ff2ea5871bb3e3e6b496ebbaafd1612e51990ffb158b9254/serpent-1.15.tar.gz"; + md5 = "e27b1aad5c218e16442f52abb7c7053a"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; setuptools = super.buildPythonPackage { - name = "setuptools-20.8.1"; + name = "setuptools-30.1.0"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip"; - md5 = "fe58a5cac0df20bb83942b252a4b0543"; + url = "https://pypi.python.org/packages/1e/43/002c8616db9a3e7be23c2556e39b90a32bb40ba0dc652de1999d5334d372/setuptools-30.1.0.tar.gz"; + md5 = "cac497f42e5096ac8df29e38d3f81c3e"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -551,13 +681,26 @@ doCheck = false; propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://github.com/jelmer/subvertpy/archive/subvertpy-0.9.3.tar.gz"; - md5 = "7b745a47128050ea5a73efcd913ec1cf"; + url = "https://code.rhodecode.com/upstream/subvertpy/archive/subvertpy-0.9.3.tar.gz?md5=4e49da2fe07608239cc9a80a7bb8f33c"; + md5 = "4e49da2fe07608239cc9a80a7bb8f33c"; }; meta = { license = [ pkgs.lib.licenses.lgpl21Plus ]; }; }; + termcolor = super.buildPythonPackage { + name = "termcolor-1.1.0"; + buildInputs = with self; []; + doCheck = false; + propagatedBuildInputs = with self; []; + src = fetchurl { + url = "https://pypi.python.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz"; + md5 = "043e89644f8909d462fbbfa511c768df"; + }; + meta = { + license = [ pkgs.lib.licenses.mit ]; + }; + }; traitlets = super.buildPythonPackage { name = "traitlets-4.3.1"; buildInputs = with self; []; @@ -598,13 +741,13 @@ }; }; waitress = super.buildPythonPackage { - name = "waitress-0.8.9"; + name = "waitress-1.0.1"; buildInputs = with self; []; doCheck = false; - propagatedBuildInputs = with self; [setuptools]; + propagatedBuildInputs = with self; []; src = fetchurl { - url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz"; - md5 = "da3f2e62b3676be5dd630703a68e2a04"; + url = "https://pypi.python.org/packages/78/7d/84d11b96c3f60164dec3bef4a859a03aeae0231aa93f57fbe0d05fa4ff36/waitress-1.0.1.tar.gz"; + md5 = "dda92358a7569669086155923a46e57c"; }; meta = { license = [ pkgs.lib.licenses.zpt21 ]; @@ -637,13 +780,13 @@ }; }; zope.deprecation = super.buildPythonPackage { - name = "zope.deprecation-4.1.1"; + name = "zope.deprecation-4.1.2"; buildInputs = with self; []; doCheck = false; propagatedBuildInputs = with self; [setuptools]; src = fetchurl { - url = "https://pypi.python.org/packages/c5/c9/e760f131fcde817da6c186a3f4952b8f206b7eeb269bb6f0836c715c5f20/zope.deprecation-4.1.1.tar.gz"; - md5 = "ce261b9384066f7e13b63525778430cb"; + url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz"; + md5 = "e9a663ded58f4f9f7881beb56cae2782"; }; meta = { license = [ pkgs.lib.licenses.zpt21 ]; @@ -665,30 +808,5 @@ ### Test requirements - pytest-sugar = super.buildPythonPackage { - name = "pytest-sugar-0.7.1"; - buildInputs = with self; []; - doCheck = false; - propagatedBuildInputs = with self; [pytest termcolor]; - src = fetchurl { - url = "https://pypi.python.org/packages/03/97/05d988b4fa870e7373e8ee4582408543b9ca2bd35c3c67b569369c6f9c49/pytest-sugar-0.7.1.tar.gz"; - md5 = "7400f7c11f3d572b2c2a3b60352d35fe"; - }; - meta = { - license = [ pkgs.lib.licenses.bsdOriginal ]; - }; - }; - termcolor = super.buildPythonPackage { - name = "termcolor-1.1.0"; - buildInputs = with self; []; - doCheck = false; - propagatedBuildInputs = with self; []; - src = fetchurl { - url = "https://pypi.python.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz"; - md5 = "043e89644f8909d462fbbfa511c768df"; - }; - meta = { - license = [ pkgs.lib.licenses.mit ]; - }; - }; + } diff --git a/pytest.ini b/pytest.ini new file mode 100644 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +testpaths = ./vcsserver +addopts = -v diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -1,35 +1,43 @@ +# core +setuptools==30.1.0 + Beaker==1.7.0 configobj==5.0.6 dulwich==0.13.0 hgsubversion==1.8.6 infrae.cache==1.0.1 -ipdb==0.10.1 -mercurial==3.8.4 -msgpack-python==0.4.6 -py==1.4.29 +mercurial==4.0.2 +msgpack-python==0.4.8 pyramid==1.6.1 pyramid-jinja2==2.5 pyramid-mako==1.0.2 -Pyro4==4.41 -pytest==2.8.5 repoze.lru==0.6 -serpent==1.12 -setuptools==20.8.1 simplejson==3.7.2 subprocess32==3.2.6 -# TODO: johbo: This version is not in source on PyPI currently, -# change back once this or a future version is available -https://github.com/jelmer/subvertpy/archive/subvertpy-0.9.3.tar.gz#md5=7b745a47128050ea5a73efcd913ec1cf + +# Custom subvertpy that is not available on pypi. +https://code.rhodecode.com/upstream/subvertpy/archive/subvertpy-0.9.3.tar.gz?md5=4e49da2fe07608239cc9a80a7bb8f33c#egg=subvertpy==0.9.3 + six==1.9.0 translationstring==1.3 -waitress==0.8.9 WebOb==1.3.1 wheel==0.29.0 -zope.deprecation==4.1.1 +zope.deprecation==4.1.2 zope.interface==4.1.3 -greenlet==0.4.7 + +## debug +ipdb==0.10.1 +ipython==5.1.0 + +# http servers +gevent==1.1.2 +greenlet==0.4.10 gunicorn==19.6.0 +waitress==1.0.1 -# Test related requirements -mock==1.0.1 -WebTest==1.4.3 +# Pyro/Deprecated TODO(Marcink): remove in 4.7 release. +Pyro4==4.41 +serpent==1.15 + +## test related requirements +-r requirements_test.txt diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100644 --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,15 @@ +# test related requirements +pytest==3.0.5 +py==1.4.31 +pytest-cov==2.4.0 +pytest-sugar==0.7.1 +pytest-runner==2.9.0 +pytest-catchlog==1.2.2 +pytest-profiling==1.2.2 +gprof2dot==2016.10.13 +pytest-timeout==1.2.0 + +mock==1.0.1 +WebTest==1.4.3 +cov-core==1.15.0 +coverage==3.7.1 diff --git a/setup.cfg b/setup.cfg new file mode 100644 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[aliases] +test = pytest diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ +# -*- coding: utf-8 -*- # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,18 +16,57 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# Import early to make sure things are patched up properly from setuptools import setup, find_packages -from setuptools.command.test import test as TestCommand + +import os +import sys +import pkgutil +import platform + +from pip.download import PipSession +from pip.req import parse_requirements + from codecs import open -from os import path -import pkgutil -import sys -here = path.abspath(path.dirname(__file__)) +if sys.version_info < (2, 7): + raise Exception('VCSServer requires Python 2.7 or later') + +here = os.path.abspath(os.path.dirname(__file__)) + +# defines current platform +__platform__ = platform.system() +__license__ = 'GPL V3' +__author__ = 'RhodeCode GmbH' +__url__ = 'https://code.rhodecode.com' +is_windows = __platform__ in ('Windows',) + + +def _get_requirements(req_filename, exclude=None, extras=None): + extras = extras or [] + exclude = exclude or [] -with open(path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() + try: + parsed = parse_requirements( + os.path.join(here, req_filename), session=PipSession()) + except TypeError: + # try pip < 6.0.0, that doesn't support session + parsed = parse_requirements(os.path.join(here, req_filename)) + + requirements = [] + for ir in parsed: + if ir.req and ir.name not in exclude: + requirements.append(str(ir.req)) + return requirements + extras + + +# requirements extract +setup_requirements = ['pytest-runner'] +install_requirements = _get_requirements( + 'requirements.txt', exclude=['setuptools']) +test_requirements = _get_requirements( + 'requirements_test.txt', extras=['configobj']) def get_version(): @@ -34,66 +74,55 @@ def get_version(): return version.strip() -class PyTest(TestCommand): - user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")] +# additional files that goes into package itself +package_data = { + '': ['*.txt', '*.rst'], + 'configs': ['*.ini'], + 'vcsserver': ['VERSION'], +} - def initialize_options(self): - TestCommand.initialize_options(self) - self.pytest_args = [] +description = 'Version Control System Server' +keywords = ' '.join([ + 'CLI', 'RhodeCode', 'RhodeCode Enterprise', 'RhodeCode Tools']) - def finalize_options(self): - TestCommand.finalize_options(self) - self.test_args = [] - self.test_suite = True - - def run_tests(self): - # import here, cause outside the eggs aren't loaded - import pytest - errno = pytest.main(self.pytest_args) - sys.exit(errno) +# README/DESCRIPTION generation +readme_file = 'README.rst' +changelog_file = 'CHANGES.rst' +try: + long_description = open(readme_file).read() + '\n\n' + \ + open(changelog_file).read() +except IOError as err: + sys.stderr.write( + "[WARNING] Cannot find file specified as long_description (%s)\n " + "or changelog (%s) skipping that file" % (readme_file, changelog_file)) + long_description = description setup( name='rhodecode-vcsserver', version=get_version(), - description='Version Control System Server', + description=description, long_description=long_description, - url='http://www.rhodecode.com', - author='RhodeCode GmbH', + keywords=keywords, + license=__license__, + author=__author__, author_email='marcin@rhodecode.com', - cmdclass={'test': PyTest}, - license='GPLv3', + url=__url__, + setup_requires=setup_requirements, + install_requires=install_requirements, + tests_require=test_requirements, + zip_safe=False, + packages=find_packages(exclude=["docs", "tests*"]), + package_data=package_data, + include_package_data=True, classifiers=[ - 'Development Status :: 5 - Production/Stable', + 'Development Status :: 6 - Mature', 'Intended Audience :: Developers', + 'Operating System :: OS Independent', 'Topic :: Software Development :: Version Control', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 'Programming Language :: Python :: 2.7', ], - packages=find_packages(), - tests_require=[ - 'mock', - 'pytest', - 'pytest-sugar', - 'WebTest', - ], - install_requires=[ - 'configobj', - 'dulwich', - 'hgsubversion', - 'infrae.cache', - 'mercurial', - 'msgpack-python', - 'pyramid', - 'Pyro4', - 'simplejson', - 'subprocess32', - 'waitress', - 'WebOb', - ], - package_data={ - 'vcsserver': ['VERSION'], - }, entry_points={ 'console_scripts': [ 'vcsserver=vcsserver.main:main', diff --git a/vcsserver/VERSION b/vcsserver/VERSION --- a/vcsserver/VERSION +++ b/vcsserver/VERSION @@ -1,1 +1,1 @@ -4.5.2 \ No newline at end of file +4.6.0 \ No newline at end of file diff --git a/vcsserver/__init__.py b/vcsserver/__init__.py --- a/vcsserver/__init__.py +++ b/vcsserver/__init__.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/base.py b/vcsserver/base.py --- a/vcsserver/base.py +++ b/vcsserver/base.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/exceptions.py b/vcsserver/exceptions.py --- a/vcsserver/exceptions.py +++ b/vcsserver/exceptions.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/git.py b/vcsserver/git.py --- a/vcsserver/git.py +++ b/vcsserver/git.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/hg.py b/vcsserver/hg.py --- a/vcsserver/hg.py +++ b/vcsserver/hg.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -389,14 +389,14 @@ class HgRemote(object): repo = self._factory.repo(wire) if file_filter: - filter = match(file_filter[0], '', [file_filter[1]]) + match_filter = match(file_filter[0], '', [file_filter[1]]) else: - filter = file_filter + match_filter = file_filter opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context) try: return "".join(patch.diff( - repo, node1=rev1, node2=rev2, match=filter, opts=opts)) + repo, node1=rev1, node2=rev2, match=match_filter, opts=opts)) except RepoLookupError: raise exceptions.LookupException() diff --git a/vcsserver/hgcompat.py b/vcsserver/hgcompat.py --- a/vcsserver/hgcompat.py +++ b/vcsserver/hgcompat.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/hgpatches.py b/vcsserver/hgpatches.py --- a/vcsserver/hgpatches.py +++ b/vcsserver/hgpatches.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/hooks.py b/vcsserver/hooks.py --- a/vcsserver/hooks.py +++ b/vcsserver/hooks.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/http_main.py b/vcsserver/http_main.py --- a/vcsserver/http_main.py +++ b/vcsserver/http_main.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ import locale import logging import uuid import wsgiref.util +import traceback from itertools import chain import msgpack @@ -154,7 +155,7 @@ class HTTPApplication(object): def __init__(self, settings=None): self.config = Configurator(settings=settings) - locale = settings.get('', 'en_US.UTF-8') + locale = settings.get('locale', '') or 'en_US.UTF-8' vcs = VCS(locale=locale, cache_config=settings) self._remotes = { 'hg': vcs._hg_remote, @@ -198,14 +199,27 @@ class HTTPApplication(object): self.config.add_view(self.hg_proxy(), route_name='hg_proxy') self.config.add_view(self.git_proxy(), route_name='git_proxy') self.config.add_view( - self.vcs_view, route_name='vcs', renderer='msgpack') + self.vcs_view, route_name='vcs', renderer='msgpack', + custom_predicates=[self.is_vcs_view]) self.config.add_view(self.hg_stream(), route_name='stream_hg') self.config.add_view(self.git_stream(), route_name='stream_git') + + def notfound(request): + return {'status': '404 NOT FOUND'} + self.config.add_notfound_view(notfound, renderer='json') + self.config.add_view( self.handle_vcs_exception, context=Exception, custom_predicates=[self.is_vcs_exception]) + self.config.add_view( + self.general_error_handler, context=Exception) + + self.config.add_tween( + 'vcsserver.tweens.RequestWrapperTween', + ) + def wsgi_app(self): return self.config.make_wsgi_app() @@ -224,9 +238,12 @@ class HTTPApplication(object): pass args.insert(0, wire) + log.debug('method called:%s with kwargs:%s', method, kwargs) try: resp = getattr(remote, method)(*args, **kwargs) except Exception as e: + tb_info = traceback.format_exc() + type_ = e.__class__.__name__ if type_ not in self.ALLOWED_EXCEPTIONS: type_ = None @@ -235,6 +252,7 @@ class HTTPApplication(object): 'id': payload.get('id'), 'error': { 'message': e.message, + 'traceback': tb_info, 'type': type_ } } @@ -338,6 +356,14 @@ class HTTPApplication(object): return app(environ, start_response) return _git_stream + def is_vcs_view(self, context, request): + """ + View predicate that returns true if given backend is supported by + defined remotes. + """ + backend = request.matchdict.get('backend') + return backend in self._remotes + def is_vcs_exception(self, context, request): """ View predicate that returns true if the context object is a VCS @@ -355,6 +381,12 @@ class HTTPApplication(object): # Re-raise exception if we can not handle it. raise exception + def general_error_handler(self, exception, request): + log.exception( + 'error occurred handling this request for path: %s', + request.path) + raise exception + class ResponseFilter(object): diff --git a/vcsserver/main.py b/vcsserver/main.py --- a/vcsserver/main.py +++ b/vcsserver/main.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/pygrack.py b/vcsserver/pygrack.py --- a/vcsserver/pygrack.py +++ b/vcsserver/pygrack.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/remote_wsgi.py b/vcsserver/remote_wsgi.py --- a/vcsserver/remote_wsgi.py +++ b/vcsserver/remote_wsgi.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/scm_app.py b/vcsserver/scm_app.py --- a/vcsserver/scm_app.py +++ b/vcsserver/scm_app.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/server.py b/vcsserver/server.py --- a/vcsserver/server.py +++ b/vcsserver/server.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/settings.py b/vcsserver/settings.py --- a/vcsserver/settings.py +++ b/vcsserver/settings.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/svn.py b/vcsserver/svn.py --- a/vcsserver/svn.py +++ b/vcsserver/svn.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -375,11 +375,18 @@ class SvnRemote(object): def diff(self, wire, rev1, rev2, path1=None, path2=None, ignore_whitespace=False, context=3): + wire.update(cache=False) repo = self._factory.repo(wire) diff_creator = SvnDiffer( repo, rev1, path1, rev2, path2, ignore_whitespace, context) - return diff_creator.generate_diff() + try: + return diff_creator.generate_diff() + except svn.core.SubversionException as e: + log.exception( + "Error during diff operation operation. " + "Path might not exist %s, %s" % (path1, path2)) + return "" class SvnDiffer(object): @@ -450,23 +457,30 @@ class SvnDiffer(object): buf, change, path, self.tgt_path, path, self.src_path) def _generate_file_diff(self, buf): - change = None - if self.src_kind == svn.core.svn_node_none: - change = "add" - elif self.tgt_kind == svn.core.svn_node_none: - change = "delete" - tgt_base, tgt_path = vcspath.split(self.tgt_path) - src_base, src_path = vcspath.split(self.src_path) - self._generate_node_diff( - buf, change, tgt_path, tgt_base, src_path, src_base) + change = None + if self.src_kind == svn.core.svn_node_none: + change = "add" + elif self.tgt_kind == svn.core.svn_node_none: + change = "delete" + tgt_base, tgt_path = vcspath.split(self.tgt_path) + src_base, src_path = vcspath.split(self.src_path) + self._generate_node_diff( + buf, change, tgt_path, tgt_base, src_path, src_base) def _generate_node_diff( self, buf, change, tgt_path, tgt_base, src_path, src_base): + + if self.src_rev == self.tgt_rev and tgt_base == src_base: + # makes consistent behaviour with git/hg to return empty diff if + # we compare same revisions + return + tgt_full_path = vcspath.join(tgt_base, tgt_path) src_full_path = vcspath.join(src_base, src_path) self.binary_content = False mime_type = self._get_mime_type(tgt_full_path) + if mime_type and not mime_type.startswith('text'): self.binary_content = True buf.write("=" * 67 + '\n') @@ -480,11 +494,21 @@ class SvnDiffer(object): if change == 'add': # TODO: johbo: SVN is missing a zero here compared to git buf.write("new file mode 10644\n") + + #TODO(marcink): intro to binary detection of svn patches + # if self.binary_content: + # buf.write('GIT binary patch\n') + buf.write("--- /dev/null\t(revision 0)\n") src_lines = [] else: if change == 'delete': buf.write("deleted file mode 10644\n") + + #TODO(marcink): intro to binary detection of svn patches + # if self.binary_content: + # buf.write('GIT binary patch\n') + buf.write("--- a/%s\t(revision %s)\n" % ( src_path, self.src_rev)) src_lines = self._svn_readlines(self.src_root, src_full_path) diff --git a/vcsserver/svn_diff.py b/vcsserver/svn_diff.py --- a/vcsserver/svn_diff.py +++ b/vcsserver/svn_diff.py @@ -76,12 +76,14 @@ def filter_ignorable_lines(hunks, fromli elif tag == 'replace' and (ignore_case or ignore_space_changes): if len(fromlines) != len(tolines): return False - def f(str): + + def f(input_str): if ignore_case: - str = str.lower() + input_str = input_str.lower() if ignore_space_changes: - str = ' '.join(str.split()) - return str + input_str = ' '.join(input_str.split()) + return input_str + for i in range(len(fromlines)): if f(fromlines[i]) != f(tolines[i]): return False diff --git a/tests/conftest.py b/vcsserver/tests/conftest.py rename from tests/conftest.py rename to vcsserver/tests/conftest.py --- a/tests/conftest.py +++ b/vcsserver/tests/conftest.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/fixture.py b/vcsserver/tests/fixture.py rename from tests/fixture.py rename to vcsserver/tests/fixture.py --- a/tests/fixture.py +++ b/vcsserver/tests/fixture.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,7 +22,7 @@ import tempfile import configobj -class TestINI(object): +class ContextINI(object): """ Allows to create a new test.ini file as a copy of existing one with edited data. If existing file is not present, it creates a new one. Example usage:: diff --git a/tests/test_git.py b/vcsserver/tests/test_git.py rename from tests/test_git.py rename to vcsserver/tests/test_git.py --- a/tests/test_git.py +++ b/vcsserver/tests/test_git.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_hg.py b/vcsserver/tests/test_hg.py rename from tests/test_hg.py rename to vcsserver/tests/test_hg.py --- a/tests/test_hg.py +++ b/vcsserver/tests/test_hg.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_hgpatches.py b/vcsserver/tests/test_hgpatches.py rename from tests/test_hgpatches.py rename to vcsserver/tests/test_hgpatches.py --- a/tests/test_hgpatches.py +++ b/vcsserver/tests/test_hgpatches.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_hooks.py b/vcsserver/tests/test_hooks.py rename from tests/test_hooks.py rename to vcsserver/tests/test_hooks.py --- a/tests/test_hooks.py +++ b/vcsserver/tests/test_hooks.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_http_performance.py b/vcsserver/tests/test_http_performance.py rename from tests/test_http_performance.py rename to vcsserver/tests/test_http_performance.py diff --git a/tests/test_main.py b/vcsserver/tests/test_main.py rename from tests/test_main.py rename to vcsserver/tests/test_main.py --- a/tests/test_main.py +++ b/vcsserver/tests/test_main.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_pygrack.py b/vcsserver/tests/test_pygrack.py rename from tests/test_pygrack.py rename to vcsserver/tests/test_pygrack.py --- a/tests/test_pygrack.py +++ b/vcsserver/tests/test_pygrack.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_scm_app.py b/vcsserver/tests/test_scm_app.py rename from tests/test_scm_app.py rename to vcsserver/tests/test_scm_app.py --- a/tests/test_scm_app.py +++ b/vcsserver/tests/test_scm_app.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_server.py b/vcsserver/tests/test_server.py rename from tests/test_server.py rename to vcsserver/tests/test_server.py --- a/tests/test_server.py +++ b/vcsserver/tests/test_server.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_subprocessio.py b/vcsserver/tests/test_subprocessio.py rename from tests/test_subprocessio.py rename to vcsserver/tests/test_subprocessio.py --- a/tests/test_subprocessio.py +++ b/vcsserver/tests/test_subprocessio.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/tests/test_svn.py b/vcsserver/tests/test_svn.py rename from tests/test_svn.py rename to vcsserver/tests/test_svn.py --- a/tests/test_svn.py +++ b/vcsserver/tests/test_svn.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -40,7 +40,7 @@ INVALID_CERTIFICATE_STDERR = '\n'.join([ @pytest.mark.parametrize('stderr,expected_reason', [ (INVALID_CERTIFICATE_STDERR, 'INVALID_CERTIFICATE'), ('svnrdump: E123456', 'UNKNOWN'), -]) +], ids=['invalid-cert-stderr', 'svnrdump-err-123456']) @pytest.mark.xfail(sys.platform == "cygwin", reason="SVN not packaged for Cygwin") def test_import_remote_repository_certificate_error(stderr, expected_reason): diff --git a/tests/test_vcsserver.py b/vcsserver/tests/test_vcsserver.py rename from tests/test_vcsserver.py rename to vcsserver/tests/test_vcsserver.py --- a/tests/test_vcsserver.py +++ b/vcsserver/tests/test_vcsserver.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ import time import pytest -from fixture import TestINI +from fixture import ContextINI @pytest.mark.parametrize("arguments, expected_texts", [ @@ -66,7 +66,7 @@ def test_vcsserver_with_config(vcsserver {'DEFAULT': {'port': vcsserver_port}}, ] - with TestINI('test.ini', ini_def) as new_test_ini_path: + with ContextINI('test.ini', ini_def) as new_test_ini_path: output = call_vcs_server_with_arguments( ['--config=' + new_test_ini_path]) @@ -85,7 +85,7 @@ def test_vcsserver_with_config_cli_overw {'DEFAULT': {'threadpool_size': '111'}}, {'DEFAULT': {'timeout': '0'}}, ] - with TestINI('test.ini', ini_def) as new_test_ini_path: + with ContextINI('test.ini', ini_def) as new_test_ini_path: output = call_vcs_server_with_arguments([ '--config=' + new_test_ini_path, '--host=128.0.0.1', diff --git a/tests/test_wsgi_app_caller.py b/vcsserver/tests/test_wsgi_app_caller.py rename from tests/test_wsgi_app_caller.py rename to vcsserver/tests/test_wsgi_app_caller.py --- a/tests/test_wsgi_app_caller.py +++ b/vcsserver/tests/test_wsgi_app_caller.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/tweens.py b/vcsserver/tweens.py new file mode 100644 --- /dev/null +++ b/vcsserver/tweens.py @@ -0,0 +1,60 @@ +# RhodeCode VCSServer provides access to different vcs backends via network. +# Copyright (C) 2014-2017 RodeCode GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + + +import time +import logging + + +from vcsserver.utils import safe_str + + +log = logging.getLogger(__name__) + + +def get_access_path(request): + environ = request.environ + return environ.get('PATH_INFO') + + +class RequestWrapperTween(object): + def __init__(self, handler, registry): + self.handler = handler + self.registry = registry + + # one-time configuration code goes here + + def __call__(self, request): + start = time.time() + try: + response = self.handler(request) + finally: + end = time.time() + + log.info('IP: %s Request to %s time: %.3fs' % ( + '127.0.0.1', + safe_str(get_access_path(request)), end - start) + ) + + return response + + +def includeme(config): + config.add_tween( + 'vcsserver.tweens.RequestWrapperTween', + ) diff --git a/vcsserver/utils.py b/vcsserver/utils.py --- a/vcsserver/utils.py +++ b/vcsserver/utils.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/vcsserver/wsgi_app_caller.py b/vcsserver/wsgi_app_caller.py --- a/vcsserver/wsgi_app_caller.py +++ b/vcsserver/wsgi_app_caller.py @@ -1,5 +1,5 @@ # RhodeCode VCSServer provides access to different vcs backends via network. -# Copyright (C) 2014-2016 RodeCode GmbH +# Copyright (C) 2014-2017 RodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by