diff --git a/.bumpversion.cfg b/.bumpversion.cfg --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.16.2 +current_version = 4.17.0 message = release: Bump version {current_version} to {new_version} [bumpversion:file:vcsserver/VERSION] 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.16.2 +state = in_progress +version = 4.17.0 diff --git a/configs/gunicorn_config.py b/configs/gunicorn_config.py --- a/configs/gunicorn_config.py +++ b/configs/gunicorn_config.py @@ -30,10 +30,12 @@ loglevel = 'debug' # SECURITY # The maximum size of HTTP request line in bytes. -limit_request_line = 4094 +# 0 for unlimited +limit_request_line = 0 # Limit the number of HTTP headers fields in a request. -limit_request_fields = 1024 +# By default this value is 100 and can’t be larger than 32768. +limit_request_fields = 10240 # Limit the allowed size of an HTTP request header field. # Value is a positive number or 0. diff --git a/pip2nix.ini b/pip2nix.ini --- a/pip2nix.ini +++ b/pip2nix.ini @@ -1,3 +1,3 @@ [pip2nix] -requirements = ., -r ./requirements.txt +requirements = ., -r ./requirements.txt, -r ./requirements_pinned.txt output = ./pkgs/python-packages.nix diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix --- a/pkgs/python-packages.nix +++ b/pkgs/python-packages.nix @@ -55,8 +55,8 @@ self: super: { self."six" ]; src = fetchurl { - url = "https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c"; - sha256 = "1hhcxirwvg58grlfr177b3awhbq8hlx1l3lh69ifl1ki7lfd1s1x"; + url = "https://code.rhodecode.com/upstream/configobj/artifacts/download/0-012de99a-b1e1-4f64-a5c0-07a98a41b324.tar.gz?md5=6a513f51fe04b2c18cf84c1395a7c626"; + sha256 = "0kqfrdfr14mw8yd8qwq14dv2xghpkjmd3yjsy8dfcbvpcc17xnxp"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; @@ -77,11 +77,11 @@ self: super: { }; }; "coverage" = super.buildPythonPackage { - name = "coverage-4.5.1"; + name = "coverage-4.5.3"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/35/fe/e7df7289d717426093c68d156e0fd9117c8f4872b6588e8a8928a0f68424/coverage-4.5.1.tar.gz"; - sha256 = "1wbrzpxka3xd4nmmkc6q0ir343d91kymwsm8pbmwa0d2a7q4ir2n"; + url = "https://files.pythonhosted.org/packages/82/70/2280b5b29a0352519bb95ab0ef1ea942d40466ca71c53a2085bdeff7b0eb/coverage-4.5.3.tar.gz"; + sha256 = "02f6m073qdispn96rc616hg0rnmw1pgqzw3bgxwiwza4zf9hirlx"; }; meta = { license = [ pkgs.lib.licenses.asl20 ]; @@ -204,11 +204,11 @@ self: super: { }; }; "hg-evolve" = super.buildPythonPackage { - name = "hg-evolve-8.0.1"; + name = "hg-evolve-8.5.1"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/06/1a/c5c12d8f117426f05285a820ee5a23121882f5381104e86276b72598934f/hg-evolve-8.0.1.tar.gz"; - sha256 = "1brafifb42k71gl7qssb5m3ijnm7y30lfvm90z8xxcr2fgz19p29"; + url = "https://files.pythonhosted.org/packages/e3/ce/6594aa403e3464831d4daf20e45fd2e3ef553d968ac13d2c7fa791d4eedd/hg-evolve-8.5.1.tar.gz"; + sha256 = "09avqn7c1biz97vb1zw91q6nfzydpcqv43mgpfrj7ywp0fscfgf3"; }; meta = { license = [ { fullName = "GPLv2+"; } ]; @@ -230,26 +230,26 @@ self: super: { }; }; "hupper" = super.buildPythonPackage { - name = "hupper-1.4.2"; + name = "hupper-1.6.1"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/f1/75/1915dc7650b4867fa3049256e24ca8eddb5989998fcec788cf52b9812dfc/hupper-1.4.2.tar.gz"; - sha256 = "16vb9fkiaakdpcp6pn56h3w0dwvm67bxq2k2dv4i382qhqwphdzb"; + url = "https://files.pythonhosted.org/packages/85/d9/e005d357b11249c5d70ddf5b7adab2e4c0da4e8b0531ff146917a04fe6c0/hupper-1.6.1.tar.gz"; + sha256 = "0d3cvkc8ssgwk54wvhbifj56ry97qi10pfzwfk8vwzzcikbfp3zy"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; "ipdb" = super.buildPythonPackage { - name = "ipdb-0.11"; + name = "ipdb-0.12"; doCheck = false; propagatedBuildInputs = [ self."setuptools" self."ipython" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/80/fe/4564de08f174f3846364b3add8426d14cebee228f741c27e702b2877e85b/ipdb-0.11.tar.gz"; - sha256 = "02m0l8wrhhd3z7dg3czn5ys1g5pxib516hpshdzp7rxzsxgcd0bh"; + url = "https://files.pythonhosted.org/packages/6d/43/c3c2e866a8803e196d6209595020a4a6db1a3c5d07c01455669497ae23d0/ipdb-0.12.tar.gz"; + sha256 = "1khr2n7xfy8hg65kj1bsrjq9g7656pp0ybfa8abpbzpdawji3qnw"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; @@ -316,11 +316,11 @@ self: super: { }; }; "mercurial" = super.buildPythonPackage { - name = "mercurial-4.6.2"; + name = "mercurial-4.9.1"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/d9/fb/c7ecf2b7fd349878dbf45b8390b8db735cef73d49dd9ce8a364b4ca3a846/mercurial-4.6.2.tar.gz"; - sha256 = "1bv6wgcdx8glihjjfg22khhc52mclsn4kwfqvzbzlg0b42h4xl0w"; + url = "https://files.pythonhosted.org/packages/60/58/a1c52d5f5c0b755e231faf7c4f507dc51fe26d979d36346bc9d28f4f8a75/mercurial-4.9.1.tar.gz"; + sha256 = "0iybbkd9add066729zg01kwz5hhc1s6lhp9rrnsmzq6ihyxj3p8v"; }; meta = { license = [ pkgs.lib.licenses.gpl1 pkgs.lib.licenses.gpl2Plus ]; @@ -374,29 +374,29 @@ self: super: { }; }; "pathlib2" = super.buildPythonPackage { - name = "pathlib2-2.3.3"; + name = "pathlib2-2.3.4"; doCheck = false; propagatedBuildInputs = [ self."six" self."scandir" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/bf/d7/a2568f4596b75d2c6e2b4094a7e64f620decc7887f69a1f2811931ea15b9/pathlib2-2.3.3.tar.gz"; - sha256 = "0hpp92vqqgcd8h92msm9slv161b1q160igjwnkf2ag6cx0c96695"; + url = "https://files.pythonhosted.org/packages/b5/f4/9c7cc726ece2498b6c8b62d3262aa43f59039b953fe23c9964ac5e18d40b/pathlib2-2.3.4.tar.gz"; + sha256 = "1y0f9rkm1924zrc5dn4bwxlhgdkbml82lkcc28l5rgmr7d918q24"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; "pexpect" = super.buildPythonPackage { - name = "pexpect-4.6.0"; + name = "pexpect-4.7.0"; doCheck = false; propagatedBuildInputs = [ self."ptyprocess" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/89/43/07d07654ee3e25235d8cea4164cdee0ec39d1fda8e9203156ebe403ffda4/pexpect-4.6.0.tar.gz"; - sha256 = "1fla85g47iaxxpjhp9vkxdnv4pgc7rplfy6ja491smrrk0jqi3ia"; + url = "https://files.pythonhosted.org/packages/1c/b1/362a0d4235496cb42c33d1d8732b5e2c607b0129ad5fdd76f5a583b9fcb3/pexpect-4.7.0.tar.gz"; + sha256 = "1sv2rri15zwhds85a4kamwh9pj49qcxv7m4miyr4jfpfwv81yb4y"; }; meta = { license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ]; @@ -431,52 +431,52 @@ self: super: { }; }; "plaster-pastedeploy" = super.buildPythonPackage { - name = "plaster-pastedeploy-0.6"; + name = "plaster-pastedeploy-0.7"; doCheck = false; propagatedBuildInputs = [ self."pastedeploy" self."plaster" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/3f/e7/6a6833158d2038ec40085433308a1e164fd1dac595513f6dd556d5669bb8/plaster_pastedeploy-0.6.tar.gz"; - sha256 = "1bkggk18f4z2bmsmxyxabvf62znvjwbivzh880419r3ap0616cf2"; + url = "https://files.pythonhosted.org/packages/99/69/2d3bc33091249266a1bd3cf24499e40ab31d54dffb4a7d76fe647950b98c/plaster_pastedeploy-0.7.tar.gz"; + sha256 = "1zg7gcsvc1kzay1ry5p699rg2qavfsxqwl17mqxzr0gzw6j9679r"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; "pluggy" = super.buildPythonPackage { - name = "pluggy-0.8.1"; + name = "pluggy-0.11.0"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/38/e1/83b10c17688af7b2998fa5342fec58ecbd2a5a7499f31e606ae6640b71ac/pluggy-0.8.1.tar.gz"; - sha256 = "05l6g42p9ilmabw0hlbiyxy6gyzjri41m5l11a8dzgvi77q35p4d"; + url = "https://files.pythonhosted.org/packages/0d/a1/862ab336e8128fde20981d2c1aa8506693412daf5083b1911d539412676b/pluggy-0.11.0.tar.gz"; + sha256 = "10511a54dvafw1jrk75mrhml53c7b7w4yaw7241696lc2hfvr895"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; "prompt-toolkit" = super.buildPythonPackage { - name = "prompt-toolkit-1.0.15"; + name = "prompt-toolkit-1.0.16"; doCheck = false; propagatedBuildInputs = [ self."six" self."wcwidth" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz"; - sha256 = "05v9h5nydljwpj5nm8n804ms0glajwfy1zagrzqrg91wk3qqi1c5"; + url = "https://files.pythonhosted.org/packages/f1/03/bb36771dc9fa7553ac4bdc639a9ecdf6fda0ff4176faf940d97e3c16e41d/prompt_toolkit-1.0.16.tar.gz"; + sha256 = "1d65hm6nf0cbq0q0121m60zzy4s1fpg9fn761s1yxf08dridvkn1"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; }; }; "psutil" = super.buildPythonPackage { - name = "psutil-5.4.8"; + name = "psutil-5.5.1"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/e3/58/0eae6e4466e5abf779d7e2b71fac7fba5f59e00ea36ddb3ed690419ccb0f/psutil-5.4.8.tar.gz"; - sha256 = "1hyna338sml2cl1mfb2gs89np18z27mvyhmq4ifh22x07n7mq9kf"; + url = "https://files.pythonhosted.org/packages/c7/01/7c30b247cdc5ba29623faa5c8cf1f1bbf7e041783c340414b0ed7e067c64/psutil-5.5.1.tar.gz"; + sha256 = "045qaqvn6k90bj5bcy259yrwcd2afgznaav3sfhphy9b8ambzkkj"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; @@ -505,18 +505,18 @@ self: super: { }; }; "pygments" = super.buildPythonPackage { - name = "pygments-2.3.1"; + name = "pygments-2.4.2"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/64/69/413708eaf3a64a6abb8972644e0f20891a55e621c6759e2c3f3891e05d63/Pygments-2.3.1.tar.gz"; - sha256 = "0ji87g09jph8jqcvclgb02qvxasdnr9pzvk90rl66d90yqcxmyjz"; + url = "https://files.pythonhosted.org/packages/7e/ae/26808275fc76bf2832deb10d3a3ed3107bc4de01b85dcccbe525f2cd6d1e/Pygments-2.4.2.tar.gz"; + sha256 = "15v2sqm5g12bqa0c7wikfh9ck2nl97ayizy1hpqhmws5gqalq748"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal ]; }; }; "pyramid" = super.buildPythonPackage { - name = "pyramid-1.10.1"; + name = "pyramid-1.10.4"; doCheck = false; propagatedBuildInputs = [ self."hupper" @@ -531,8 +531,8 @@ self: super: { self."repoze.lru" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/0a/3e/22e3ac9be1b70a01139adba8906ee4b8f628bb469fea3c52f6c97b73063c/pyramid-1.10.1.tar.gz"; - sha256 = "1h5105nfh6rsrfjiyw20aavyibj36la3hajy6vh1fa77xb4y3hrp"; + url = "https://files.pythonhosted.org/packages/c2/43/1ae701c9c6bb3a434358e678a5e72c96e8aa55cf4cb1d2fa2041b5dd38b7/pyramid-1.10.4.tar.gz"; + sha256 = "0rkxs1ajycg2zh1c94xlmls56mx5m161sn8112skj0amza6cn36q"; }; meta = { license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ]; @@ -658,7 +658,7 @@ self: super: { }; }; "rhodecode-vcsserver" = super.buildPythonPackage { - name = "rhodecode-vcsserver-4.16.2"; + name = "rhodecode-vcsserver-4.17.0"; buildInputs = [ self."pytest" self."py" @@ -678,8 +678,6 @@ self: super: { doCheck = true; propagatedBuildInputs = [ self."configobj" - self."atomicwrites" - self."attrs" self."dogpile.cache" self."dogpile.core" self."decorator" @@ -691,11 +689,8 @@ self: super: { self."mercurial" self."msgpack-python" self."pastedeploy" - self."psutil" self."pyramid" self."pyramid-mako" - self."pygments" - self."pathlib2" self."repoze.lru" self."simplejson" self."subprocess32" @@ -705,12 +700,10 @@ self: super: { self."webob" self."zope.deprecation" self."zope.interface" - self."venusian" self."gevent" self."greenlet" self."gunicorn" self."waitress" - self."setproctitle" self."ipdb" self."ipython" self."pytest" @@ -733,11 +726,11 @@ self: super: { }; }; "scandir" = super.buildPythonPackage { - name = "scandir-1.9.0"; + name = "scandir-1.10.0"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/16/2a/557af1181e6b4e30254d5a6163b18f5053791ca66e251e77ab08887e8fe3/scandir-1.9.0.tar.gz"; - sha256 = "0r3hvf1a9jm1rkqgx40gxkmccknkaiqjavs8lccgq9s8khh5x5s4"; + url = "https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz"; + sha256 = "1bkqwmf056pkchf05ywbnf659wqlp6lljcdb0y88wr9f0vv32ijd"; }; meta = { license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ]; @@ -755,11 +748,11 @@ self: super: { }; }; "setuptools" = super.buildPythonPackage { - name = "setuptools-40.8.0"; + name = "setuptools-41.0.1"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/c2/f7/c7b501b783e5a74cf1768bc174ee4fb0a8a6ee5af6afa92274ff964703e0/setuptools-40.8.0.zip"; - sha256 = "0k9hifpgahnw2a26w3cr346iy733k6d3nwh3f7g9m13y6f8fqkkf"; + url = "https://files.pythonhosted.org/packages/1d/64/a18a487b4391a05b9c7f938b94a16d80305bf0369c6b0b9509e86165e1d3/setuptools-41.0.1.zip"; + sha256 = "04sns22y2hhsrwfy1mha2lgslvpjsjsz8xws7h2rh5a7ylkd28m2"; }; meta = { license = [ pkgs.lib.licenses.mit ]; @@ -799,11 +792,11 @@ self: super: { }; }; "subprocess32" = super.buildPythonPackage { - name = "subprocess32-3.5.3"; + name = "subprocess32-3.5.4"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/be/2b/beeba583e9877e64db10b52a96915afc0feabf7144dcbf2a0d0ea68bf73d/subprocess32-3.5.3.tar.gz"; - sha256 = "1hr5fan8i719hmlmz73hf8rhq74014w07d8ryg7krvvf6692kj3b"; + url = "https://files.pythonhosted.org/packages/32/c8/564be4d12629b912ea431f1a50eb8b3b9d00f1a0b1ceff17f266be190007/subprocess32-3.5.4.tar.gz"; + sha256 = "17f7mvwx2271s1wrl0qac3wjqqnrqag866zs3qc8v5wp0k43fagb"; }; meta = { license = [ pkgs.lib.licenses.psfl ]; @@ -871,11 +864,11 @@ self: super: { }; }; "waitress" = super.buildPythonPackage { - name = "waitress-1.1.0"; + name = "waitress-1.3.0"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/3c/68/1c10dd5c556872ceebe88483b0436140048d39de83a84a06a8baa8136f4f/waitress-1.1.0.tar.gz"; - sha256 = "1a85gyji0kajc3p0s1pwwfm06w4wfxjkvvl4rnrz3h164kbd6g6k"; + url = "https://files.pythonhosted.org/packages/43/50/9890471320d5ad22761ae46661cf745f487b1c8c4ec49352b99e1078b970/waitress-1.3.0.tar.gz"; + sha256 = "09j5dzbbcxib7vdskhx39s1qsydlr4n2p2png71d7mjnr9pnwajf"; }; meta = { license = [ pkgs.lib.licenses.zpl21 ]; @@ -893,18 +886,18 @@ self: super: { }; }; "webob" = super.buildPythonPackage { - name = "webob-1.8.4"; + name = "webob-1.8.5"; doCheck = false; src = fetchurl { - url = "https://files.pythonhosted.org/packages/e4/6c/99e322c3d4cc11d9060a67a9bf2f7c9c581f40988c11fffe89bb8c36bc5e/WebOb-1.8.4.tar.gz"; - sha256 = "16cfg5y4n6sihz59vsmns2yqbfm0gfsn3l5xgz2g0pdhilaib0x4"; + url = "https://files.pythonhosted.org/packages/9d/1a/0c89c070ee2829c934cb6c7082287c822e28236a4fcf90063e6be7c35532/WebOb-1.8.5.tar.gz"; + sha256 = "11khpzaxc88q31v25ic330gsf56fwmbdc9b30br8mvp0fmwspah5"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; "webtest" = super.buildPythonPackage { - name = "webtest-2.0.32"; + name = "webtest-2.0.33"; doCheck = false; propagatedBuildInputs = [ self."six" @@ -913,36 +906,36 @@ self: super: { self."beautifulsoup4" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/27/9f/9e74449d272ffbef4fb3012e6dbc53c0b24822d545e7a33a342f80131e59/WebTest-2.0.32.tar.gz"; - sha256 = "0qp0nnbazzm4ibjiyqfcn6f230svk09i4g58zg2i9x1ga06h48a2"; + url = "https://files.pythonhosted.org/packages/a8/b0/ffc9413b637dbe26e291429bb0f6ed731e518d0cd03da28524a8fe2e8a8f/WebTest-2.0.33.tar.gz"; + sha256 = "1l3z0cwqslsf4rcrhi2gr8kdfh74wn2dw76376i4g9i38gz8wd21"; }; meta = { license = [ pkgs.lib.licenses.mit ]; }; }; "zope.deprecation" = super.buildPythonPackage { - name = "zope.deprecation-4.3.0"; + name = "zope.deprecation-4.4.0"; doCheck = false; propagatedBuildInputs = [ self."setuptools" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/a1/18/2dc5e6bfe64fdc3b79411b67464c55bb0b43b127051a20f7f492ab767758/zope.deprecation-4.3.0.tar.gz"; - sha256 = "095jas41wbxgmw95kwdxqhbc3bgihw2hzj9b3qpdg85apcsf2lkx"; + url = "https://files.pythonhosted.org/packages/34/da/46e92d32d545dd067b9436279d84c339e8b16de2ca393d7b892bc1e1e9fd/zope.deprecation-4.4.0.tar.gz"; + sha256 = "1pz2cv7gv9y1r3m0bdv7ks1alagmrn5msm5spwdzkb2by0w36i8d"; }; meta = { license = [ pkgs.lib.licenses.zpl21 ]; }; }; "zope.interface" = super.buildPythonPackage { - name = "zope.interface-4.5.0"; + name = "zope.interface-4.6.0"; doCheck = false; propagatedBuildInputs = [ self."setuptools" ]; src = fetchurl { - url = "https://files.pythonhosted.org/packages/ac/8a/657532df378c2cd2a1fe6b12be3b4097521570769d4852ec02c24bd3594e/zope.interface-4.5.0.tar.gz"; - sha256 = "0k67m60ij06wkg82n15qgyn96waf4pmrkhv0njpkfzpmv5q89hsp"; + url = "https://files.pythonhosted.org/packages/4e/d0/c9d16bd5b38de44a20c6dc5d5ed80a49626fafcb3db9f9efdc2a19026db6/zope.interface-4.6.0.tar.gz"; + sha256 = "1rgh2x3rcl9r0v0499kf78xy86rnmanajf4ywmqb943wpk50sg8v"; }; meta = { license = [ pkgs.lib.licenses.zpl21 ]; diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -1,48 +1,42 @@ ## dependencies # our custom configobj -https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c#egg=configobj==5.0.6 -atomicwrites==1.2.1 -attrs==18.2.0 +https://code.rhodecode.com/upstream/configobj/artifacts/download/0-012de99a-b1e1-4f64-a5c0-07a98a41b324.tar.gz?md5=6a513f51fe04b2c18cf84c1395a7c626#egg=configobj==5.0.6 + dogpile.cache==0.7.1 dogpile.core==0.4.1 decorator==4.1.2 dulwich==0.13.0 hgsubversion==1.9.3 -hg-evolve==8.0.1 +hg-evolve==8.5.1 mako==1.0.7 markupsafe==1.1.0 -mercurial==4.6.2 +mercurial==4.9.1 msgpack-python==0.5.6 pastedeploy==2.0.1 -psutil==5.4.8 -pyramid==1.10.1 +pyramid==1.10.4 pyramid-mako==1.0.2 -pygments==2.3.1 -pathlib2==2.3.3 repoze.lru==0.7 simplejson==3.16.0 -subprocess32==3.5.3 +subprocess32==3.5.4 subvertpy==0.10.1 six==1.11.0 translationstring==1.3 -webob==1.8.4 -zope.deprecation==4.3.0 -zope.interface==4.5.0 -venusian==1.2.0 +webob==1.8.5 +zope.deprecation==4.4.0 +zope.interface==4.6.0 ## http servers gevent==1.4.0 greenlet==0.4.15 gunicorn==19.9.0 -waitress==1.1.0 -setproctitle==1.1.10 +waitress==1.3.0 ## debug -ipdb==0.11.0 +ipdb==0.12.0 ipython==5.1.0 ## test related requirements diff --git a/requirements_pinned.txt b/requirements_pinned.txt new file mode 100644 --- /dev/null +++ b/requirements_pinned.txt @@ -0,0 +1,12 @@ +# contains not directly required libraries we want to pin the version. + +atomicwrites==1.2.1 +attrs==18.2.0 +hupper==1.6.1 +pathlib2==2.3.4 +pygments==2.4.2 +psutil==5.5.1 +pluggy==0.11.0 +scandir==1.10.0 +setproctitle==1.1.10 +venusian==1.2.0 diff --git a/requirements_test.txt b/requirements_test.txt --- a/requirements_test.txt +++ b/requirements_test.txt @@ -10,7 +10,7 @@ gprof2dot==2017.9.19 mock==1.0.1 cov-core==1.15.0 -coverage==4.5.1 +coverage==4.5.3 -webtest==2.0.32 +webtest==2.0.33 beautifulsoup4==4.6.3 diff --git a/vcsserver/VERSION b/vcsserver/VERSION --- a/vcsserver/VERSION +++ b/vcsserver/VERSION @@ -1,1 +1,1 @@ -4.16.2 \ No newline at end of file +4.17.0 \ No newline at end of file diff --git a/vcsserver/exceptions.py b/vcsserver/exceptions.py --- a/vcsserver/exceptions.py +++ b/vcsserver/exceptions.py @@ -37,7 +37,7 @@ def _make_exception(kind, org_exc, *args exc = Exception(*args) exc._vcs_kind = kind exc._org_exc = org_exc - exc._org_exc_tb = '' + exc._org_exc_tb = getattr(org_exc, '_org_exc_tb', '') return exc diff --git a/vcsserver/git.py b/vcsserver/git.py --- a/vcsserver/git.py +++ b/vcsserver/git.py @@ -129,6 +129,15 @@ class GitRemote(object): return params @reraise_safe_exceptions + def is_empty(self, wire): + repo = self._factory.repo(wire) + try: + return not repo.head() + except Exception: + log.exception("failed to read object_store") + return True + + @reraise_safe_exceptions def add_object(self, wire, content): repo = self._factory.repo(wire) blob = objects.Blob() @@ -557,7 +566,8 @@ class GitRemote(object): return { 'id': obj.id, 'type': obj.type_name, - 'commit_id': commit_id + 'commit_id': commit_id, + 'idx': 0 } @reraise_safe_exceptions diff --git a/vcsserver/git_lfs/app.py b/vcsserver/git_lfs/app.py --- a/vcsserver/git_lfs/app.py +++ b/vcsserver/git_lfs/app.py @@ -90,6 +90,8 @@ def lfs_objects_batch(request): repo = request.matchdict.get('repo') data = request.json operation = data.get('operation') + http_scheme = request.registry.git_lfs_http_scheme + if operation not in ('download', 'upload'): log.debug('LFS: unsupported operation:%s', operation) return write_response_error( @@ -114,8 +116,10 @@ def lfs_objects_batch(request): obj_data = {'oid': oid} - obj_href = request.route_url('lfs_objects_oid', repo=repo, oid=oid) - obj_verify_href = request.route_url('lfs_objects_verify', repo=repo) + obj_href = request.route_url('lfs_objects_oid', repo=repo, oid=oid, + _scheme=http_scheme) + obj_verify_href = request.route_url('lfs_objects_verify', repo=repo, + _scheme=http_scheme) store = LFSOidStore( oid, repo, store_location=request.registry.git_lfs_store_path) handler = OidHandler( @@ -274,11 +278,12 @@ def git_lfs_app(config): config.add_notfound_view(not_found, renderer='json') -def create_app(git_lfs_enabled, git_lfs_store_path): +def create_app(git_lfs_enabled, git_lfs_store_path, git_lfs_http_scheme): config = Configurator() if git_lfs_enabled: config.include(git_lfs_app) config.registry.git_lfs_store_path = git_lfs_store_path + config.registry.git_lfs_http_scheme = git_lfs_http_scheme else: # not found handler for API, reporting disabled LFS support config.add_notfound_view(lfs_disabled, renderer='json') diff --git a/vcsserver/git_lfs/tests/test_lfs_app.py b/vcsserver/git_lfs/tests/test_lfs_app.py --- a/vcsserver/git_lfs/tests/test_lfs_app.py +++ b/vcsserver/git_lfs/tests/test_lfs_app.py @@ -26,7 +26,17 @@ from vcsserver.git_lfs.app import create @pytest.fixture(scope='function') def git_lfs_app(tmpdir): custom_app = WebObTestApp(create_app( - git_lfs_enabled=True, git_lfs_store_path=str(tmpdir))) + git_lfs_enabled=True, git_lfs_store_path=str(tmpdir), + git_lfs_http_scheme='http')) + custom_app._store = str(tmpdir) + return custom_app + + +@pytest.fixture(scope='function') +def git_lfs_https_app(tmpdir): + custom_app = WebObTestApp(create_app( + git_lfs_enabled=True, git_lfs_store_path=str(tmpdir), + git_lfs_http_scheme='https')) custom_app._store = str(tmpdir) return custom_app @@ -58,7 +68,7 @@ class TestLFSApplication(object): assert json.loads(response.text) == { u'message': u'GIT LFS locking api not supported'} - def test_app_batch_api_missing_auth(self, git_lfs_app,): + def test_app_batch_api_missing_auth(self, git_lfs_app): git_lfs_app.post_json( '/repo/info/lfs/objects/batch', params={}, status=403) @@ -155,8 +165,31 @@ class TestLFSApplication(object): assert json.loads(response.text) == { 'objects': expected_objects, 'transfer': 'basic'} + def test_app_batch_api_upload_for_https(self, git_lfs_https_app, http_auth): + params = {'operation': 'upload', + 'objects': [{'oid': '123', 'size': '1024'}]} + response = git_lfs_https_app.post_json( + '/repo/info/lfs/objects/batch', params=params, + extra_environ=http_auth) + expected_objects = [ + {u'authenticated': True, + u'actions': { + u'upload': { + u'header': {u'Authorization': u'Basic XXXXX', + u'Transfer-Encoding': u'chunked'}, + u'href': u'https://localhost/repo/info/lfs/objects/123'}, + u'verify': { + u'header': {u'Authorization': u'Basic XXXXX'}, + u'href': u'https://localhost/repo/info/lfs/verify'} + }, + u'oid': u'123', + u'size': u'1024'} + ] + assert json.loads(response.text) == { + 'objects': expected_objects, 'transfer': 'basic'} + def test_app_verify_api_missing_data(self, git_lfs_app): - params = {'oid': 'missing',} + params = {'oid': 'missing'} response = git_lfs_app.post_json( '/repo/info/lfs/verify', params=params, status=400) diff --git a/vcsserver/hg.py b/vcsserver/hg.py --- a/vcsserver/hg.py +++ b/vcsserver/hg.py @@ -20,6 +20,7 @@ import logging import stat import urllib import urllib2 +import traceback from hgext import largefiles, rebase from hgext.strip import strip as hgext_strip @@ -31,9 +32,9 @@ import vcsserver from vcsserver import exceptions from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original from vcsserver.hgcompat import ( - archival, bin, clone, config as hgconfig, diffopts, hex, + archival, bin, clone, config as hgconfig, diffopts, hex, get_ctx, hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler, - makepeer, localrepository, match, memctx, exchange, memfilectx, nullrev, + makepeer, instance, match, memctx, exchange, memfilectx, nullrev, hg_merge, patch, peer, revrange, ui, hg_tag, Abort, LookupError, RepoError, RepoLookupError, InterventionRequired, RequirementError) @@ -41,7 +42,29 @@ log = logging.getLogger(__name__) def make_ui_from_config(repo_config): - baseui = ui.ui() + + class LoggingUI(ui.ui): + def status(self, *msg, **opts): + log.info(' '.join(msg).rstrip('\n')) + super(LoggingUI, self).status(*msg, **opts) + + def warn(self, *msg, **opts): + log.warn(' '.join(msg).rstrip('\n')) + super(LoggingUI, self).warn(*msg, **opts) + + def error(self, *msg, **opts): + log.error(' '.join(msg).rstrip('\n')) + super(LoggingUI, self).error(*msg, **opts) + + def note(self, *msg, **opts): + log.info(' '.join(msg).rstrip('\n')) + super(LoggingUI, self).note(*msg, **opts) + + def debug(self, *msg, **opts): + log.debug(' '.join(msg).rstrip('\n')) + super(LoggingUI, self).debug(*msg, **opts) + + baseui = LoggingUI() # clean the baseui object baseui._ocfg = hgconfig.config() @@ -55,6 +78,9 @@ def make_ui_from_config(repo_config): baseui.setconfig('ui', 'quiet', 'true') baseui.setconfig('ui', 'paginate', 'never') + # for better Error reporting of Mercurial + baseui.setconfig('ui', 'message-output', 'stderr') + # force mercurial to only use 1 thread, otherwise it may try to set a # signal in a non-main thread, thus generating a ValueError. baseui.setconfig('worker', 'numcpus', 1) @@ -114,7 +140,7 @@ class MercurialFactory(RepoFactory): def _create_repo(self, wire, create): baseui = self._create_config(wire["config"]) - return localrepository(baseui, wire["path"], create) + return instance(baseui, wire["path"], create) class HgRemote(object): @@ -137,12 +163,25 @@ class HgRemote(object): "_file_paths": self.ctx_list, } + def _get_ctx(self, repo, ref): + return get_ctx(repo, ref) + @reraise_safe_exceptions def discover_hg_version(self): from mercurial import util return util.version() @reraise_safe_exceptions + def is_empty(self, wire): + repo = self._factory.repo(wire) + + try: + return len(repo) == 0 + except Exception: + log.exception("failed to read object_store") + return False + + @reraise_safe_exceptions def archive_repo(self, archive_path, mtime, file_info, kind): if kind == "tgz": archiver = archival.tarit(archive_path, mtime, "gz") @@ -198,7 +237,15 @@ class HgRemote(object): self, wire, message, parents, commit_time, commit_timezone, user, files, extra, removed, updated): - def _filectxfn(_repo, memctx, path): + repo = self._factory.repo(wire) + baseui = self._factory._create_config(wire['config']) + publishing = baseui.configbool('phases', 'publish') + if publishing: + new_commit = 'public' + else: + new_commit = 'draft' + + def _filectxfn(_repo, ctx, path): """ Marks given path as added/changed/removed in a given _repo. This is for internal mercurial commit function. @@ -214,7 +261,7 @@ class HgRemote(object): if node['path'] == path: return memfilectx( _repo, - changectx=memctx, + changectx=ctx, path=node['path'], data=node['content'], islink=False, @@ -225,103 +272,94 @@ class HgRemote(object): "Given path haven't been marked as added, " "changed or removed (%s)" % path) - repo = self._factory.repo(wire) + with repo.ui.configoverride({('phases', 'new-commit'): new_commit}): - commit_ctx = memctx( - repo=repo, - parents=parents, - text=message, - files=files, - filectxfn=_filectxfn, - user=user, - date=(commit_time, commit_timezone), - extra=extra) + commit_ctx = memctx( + repo=repo, + parents=parents, + text=message, + files=files, + filectxfn=_filectxfn, + user=user, + date=(commit_time, commit_timezone), + extra=extra) - n = repo.commitctx(commit_ctx) - new_id = hex(n) + n = repo.commitctx(commit_ctx) + new_id = hex(n) - return new_id + return new_id @reraise_safe_exceptions def ctx_branch(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.branch() @reraise_safe_exceptions def ctx_children(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return [child.rev() for child in ctx.children()] @reraise_safe_exceptions def ctx_date(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.date() @reraise_safe_exceptions def ctx_description(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.description() @reraise_safe_exceptions - def ctx_diff( - self, wire, revision, git=True, ignore_whitespace=True, context=3): - repo = self._factory.repo(wire) - ctx = repo[revision] - result = ctx.diff( - git=git, ignore_whitespace=ignore_whitespace, context=context) - return list(result) - - @reraise_safe_exceptions def ctx_files(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.files() @reraise_safe_exceptions def ctx_list(self, path, revision): repo = self._factory.repo(path) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return list(ctx) @reraise_safe_exceptions def ctx_parents(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return [parent.rev() for parent in ctx.parents()] @reraise_safe_exceptions def ctx_phase(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) # public=0, draft=1, secret=3 return ctx.phase() @reraise_safe_exceptions def ctx_obsolete(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.obsolete() @reraise_safe_exceptions def ctx_hidden(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.hidden() @reraise_safe_exceptions def ctx_substate(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.substate @reraise_safe_exceptions def ctx_status(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) status = repo[ctx.p1().node()].status(other=ctx.node()) # object of status (odd, custom named tuple in mercurial) is not # correctly serializable, we make it a list, as the underling @@ -331,7 +369,7 @@ class HgRemote(object): @reraise_safe_exceptions def ctx_user(self, wire, revision): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) return ctx.user() @reraise_safe_exceptions @@ -421,13 +459,17 @@ class HgRemote(object): def node_history(self, wire, revision, path, limit): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) fctx = ctx.filectx(path) def history_iter(): limit_rev = fctx.rev() for obj in reversed(list(fctx.filelog())): obj = fctx.filectx(obj) + ctx = obj.changectx() + if ctx.hidden() or ctx.obsolete(): + continue + if limit_rev >= obj.rev(): yield obj @@ -442,7 +484,7 @@ class HgRemote(object): @reraise_safe_exceptions def node_history_untill(self, wire, revision, path, limit): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) fctx = ctx.filectx(path) file_log = list(fctx.filelog()) @@ -455,7 +497,7 @@ class HgRemote(object): @reraise_safe_exceptions def fctx_annotate(self, wire, revision, path): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) fctx = ctx.filectx(path) result = [] @@ -469,29 +511,30 @@ class HgRemote(object): @reraise_safe_exceptions def fctx_data(self, wire, revision, path): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) fctx = ctx.filectx(path) return fctx.data() @reraise_safe_exceptions def fctx_flags(self, wire, revision, path): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) fctx = ctx.filectx(path) return fctx.flags() @reraise_safe_exceptions def fctx_size(self, wire, revision, path): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) fctx = ctx.filectx(path) return fctx.size() @reraise_safe_exceptions def get_all_commit_ids(self, wire, name): repo = self._factory.repo(wire) - revs = repo.filtered(name).changelog.index - return map(lambda x: hex(x[7]), revs)[:-1] + repo = repo.filtered(name) + revs = map(lambda x: hex(x[7]), repo.changelog.index) + return revs @reraise_safe_exceptions def get_config_value(self, wire, section, name, untrusted=False): @@ -544,22 +587,19 @@ class HgRemote(object): if isinstance(revision, int): # NOTE(marcink): - # since Mercurial doesn't support indexes properly + # since Mercurial doesn't support negative indexes properly # we need to shift accordingly by one to get proper index, e.g # repo[-1] => repo[-2] # repo[0] => repo[-1] - # repo[1] => repo[2] we also never call repo[0] because - # it's actually second commit if revision <= 0: revision = revision + -1 - else: - revision = revision + 1 - try: - ctx = repo[revision] - except RepoLookupError as e: + ctx = self._get_ctx(repo, revision) + except (TypeError, RepoLookupError) as e: + e._org_exc_tb = traceback.format_exc() raise exceptions.LookupException(e)(revision) except LookupError as e: + e._org_exc_tb = traceback.format_exc() raise exceptions.LookupException(e)(e.name) if not both: @@ -605,7 +645,7 @@ class HgRemote(object): @reraise_safe_exceptions def revision(self, wire, rev): repo = self._factory.repo(wire) - ctx = repo[rev] + ctx = self._get_ctx(repo, rev) return ctx.rev() @reraise_safe_exceptions @@ -638,7 +678,7 @@ class HgRemote(object): # case when we want to compare two independent repositories if other_path and other_path != wire["path"]: baseui = self._factory._create_config(wire["config"]) - repo = unionrepo.unionrepository(baseui, other_path, wire["path"]) + repo = unionrepo.makeunionrepository(baseui, other_path, wire["path"]) else: repo = self._factory.repo(wire) return list(repo.revs(rev_spec, *args)) @@ -646,7 +686,7 @@ class HgRemote(object): @reraise_safe_exceptions def strip(self, wire, revision, update, backup): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) hgext_strip( repo.baseui, repo, ctx.node(), update=update, backup=backup) @@ -669,7 +709,7 @@ class HgRemote(object): def tag(self, wire, name, revision, message, local, user, tag_time, tag_timezone): repo = self._factory.repo(wire) - ctx = repo[revision] + ctx = self._get_ctx(repo, revision) node = ctx.node() date = (tag_time, tag_timezone) @@ -766,8 +806,20 @@ class HgRemote(object): # setting the interactive flag to `False` mercurial doesn't prompt the # used but instead uses a default value. repo.ui.setconfig('ui', 'interactive', False) + commands.merge(baseui, repo, rev=revision) - commands.merge(baseui, repo, rev=revision) + @reraise_safe_exceptions + def merge_state(self, wire): + repo = self._factory.repo(wire) + repo.ui.setconfig('ui', 'merge', 'internal:dump') + + # In case of sub repositories are used mercurial prompts the user in + # case of merge conflicts or different sub repository sources. By + # setting the interactive flag to `False` mercurial doesn't prompt the + # used but instead uses a default value. + repo.ui.setconfig('ui', 'interactive', False) + ms = hg_merge.mergestate(repo) + return [x for x in ms.unresolved()] @reraise_safe_exceptions def commit(self, wire, message, username, close_branch=False): @@ -776,6 +828,7 @@ class HgRemote(object): repo.ui.setconfig('ui', 'username', username) commands.commit(baseui, repo, message=message, close_branch=close_branch) + @reraise_safe_exceptions def rebase(self, wire, source=None, dest=None, abort=False): repo = self._factory.repo(wire) diff --git a/vcsserver/hgcompat.py b/vcsserver/hgcompat.py --- a/vcsserver/hgcompat.py +++ b/vcsserver/hgcompat.py @@ -42,9 +42,9 @@ from mercurial.commands import clone, nu from mercurial.context import memctx, memfilectx from mercurial.error import ( LookupError, RepoError, RepoLookupError, Abort, InterventionRequired, - RequirementError) + RequirementError, ProgrammingError) from mercurial.hgweb import hgweb_mod -from mercurial.localrepo import localrepository +from mercurial.localrepo import instance from mercurial.match import match from mercurial.mdiff import diffopts from mercurial.node import bin, hex @@ -53,7 +53,7 @@ from mercurial.discovery import findcomm from mercurial.hg import peer from mercurial.httppeer import makepeer from mercurial.util import url as hg_url -from mercurial.scmutil import revrange +from mercurial.scmutil import revrange, revsymbol from mercurial.node import nullrev from mercurial import exchange from hgext import largefiles @@ -61,3 +61,14 @@ from hgext import largefiles # those authnadlers are patched for python 2.6.5 bug an # infinit looping when given invalid resources from mercurial.url import httpbasicauthhandler, httpdigestauthhandler + + +def get_ctx(repo, ref): + try: + ctx = repo[ref] + except ProgrammingError: + # we're unable to find the rev using a regular lookup, we fallback + # to slower, but backward compat revsymbol usage + ctx = revsymbol(repo, ref) + + return ctx diff --git a/vcsserver/hook_utils/__init__.py b/vcsserver/hook_utils/__init__.py --- a/vcsserver/hook_utils/__init__.py +++ b/vcsserver/hook_utils/__init__.py @@ -170,8 +170,10 @@ def check_rhodecode_hook(hook_path): def read_hook_content(hook_path): - with open(hook_path, 'rb') as f: - content = f.read() + content = '' + if os.path.isfile(hook_path): + with open(hook_path, 'rb') as f: + content = f.read() return content diff --git a/vcsserver/hooks.py b/vcsserver/hooks.py --- a/vcsserver/hooks.py +++ b/vcsserver/hooks.py @@ -33,6 +33,7 @@ import mercurial.node import simplejson as json from vcsserver import exceptions, subprocessio, settings +from vcsserver.hgcompat import get_ctx log = logging.getLogger(__name__) @@ -177,11 +178,11 @@ def _rev_range_hash(repo, node, check_he commits = [] revs = [] - start = repo[node].rev() + start = get_ctx(repo, node).rev() end = len(repo) for rev in range(start, end): revs.append(rev) - ctx = repo[rev] + ctx = get_ctx(repo, rev) commit_id = mercurial.node.hex(ctx.node()) branch = ctx.branch() commits.append((commit_id, branch)) @@ -204,12 +205,12 @@ def _check_heads(repo, start, end, commi parents.add(p) for p in parents: - branch = repo[p].branch() + branch = get_ctx(repo, p).branch() # The heads descending from that parent, on the same branch parent_heads = set([p]) reachable = set([p]) for x in xrange(p + 1, end): - if repo[x].branch() != branch: + if get_ctx(repo, x).branch() != branch: continue for pp in changelog.parentrevs(x): if pp in reachable: @@ -385,7 +386,7 @@ def post_push_ssh(ui, repo, node, **kwar def key_push(ui, repo, **kwargs): if kwargs['new'] != '0' and kwargs['namespace'] == 'bookmarks': # store new bookmarks in our UI object propagated later to post_push - ui._rc_pushkey_branches = repo[kwargs['key']].bookmarks() + ui._rc_pushkey_branches = get_ctx(repo, kwargs['key']).bookmarks() return @@ -622,7 +623,7 @@ def git_post_receive(unused_repo_path, r def _get_extras_from_txn_id(path, txn_id): extras = {} try: - cmd = ['svnlook', 'pget', + cmd = [settings.SVNLOOK_EXECUTABLE, 'pget', '-t', txn_id, '--revprop', path, 'rc-scm-extras'] stdout, stderr = subprocessio.run_command( @@ -637,7 +638,7 @@ def _get_extras_from_txn_id(path, txn_id def _get_extras_from_commit_id(commit_id, path): extras = {} try: - cmd = ['svnlook', 'pget', + cmd = [settings.SVNLOOK_EXECUTABLE, 'pget', '-r', commit_id, '--revprop', path, 'rc-scm-extras'] stdout, stderr = subprocessio.run_command( @@ -663,7 +664,7 @@ def svn_pre_commit(repo_path, commit_dat return 0 extras['hook_type'] = 'pre_commit' - extras['commit_ids'] = [] + extras['commit_ids'] = [txn_id] extras['txn_id'] = txn_id extras['new_refs'] = { 'total_commits': 1, diff --git a/vcsserver/lib/exc_tracking.py b/vcsserver/lib/exc_tracking.py --- a/vcsserver/lib/exc_tracking.py +++ b/vcsserver/lib/exc_tracking.py @@ -26,6 +26,7 @@ import logging import traceback import tempfile +from pyramid import compat log = logging.getLogger(__name__) @@ -69,9 +70,26 @@ def get_exc_store(): def _store_exception(exc_id, exc_info, prefix): exc_type, exc_value, exc_traceback = exc_info + tb = ''.join(traceback.format_exception( exc_type, exc_value, exc_traceback, None)) + detailed_tb = getattr(exc_value, '_org_exc_tb', None) + + if detailed_tb: + if isinstance(detailed_tb, compat.string_types): + remote_tb = [detailed_tb] + + tb += ( + '\n+++ BEG SOURCE EXCEPTION +++\n\n' + '{}\n' + '+++ END SOURCE EXCEPTION +++\n' + ''.format('\n'.join(remote_tb)) + ) + + # Avoid that remote_tb also appears in the frame + del remote_tb + exc_type_name = exc_type.__name__ exc_store_path = get_exc_store() exc_data, org_data = exc_serialize(exc_id, tb, exc_type_name) diff --git a/vcsserver/scm_app.py b/vcsserver/scm_app.py --- a/vcsserver/scm_app.py +++ b/vcsserver/scm_app.py @@ -218,8 +218,8 @@ class GitLFSHandler(object): self.git_path = git_path self.update_server_info = update_server_info - def get_app(self, git_lfs_enabled, git_lfs_store_path): - app = git_lfs.create_app(git_lfs_enabled, git_lfs_store_path) + def get_app(self, git_lfs_enabled, git_lfs_store_path, git_lfs_http_scheme): + app = git_lfs.create_app(git_lfs_enabled, git_lfs_store_path, git_lfs_http_scheme) return app @@ -228,7 +228,8 @@ def create_git_lfs_wsgi_app(repo_path, r update_server_info = config.pop('git_update_server_info') git_lfs_enabled = config.pop('git_lfs_enabled') git_lfs_store_path = config.pop('git_lfs_store_path') + git_lfs_http_scheme = config.pop('git_lfs_http_scheme', 'http') app = GitLFSHandler( repo_path, repo_name, git_path, update_server_info, config) - return app.get_app(git_lfs_enabled, git_lfs_store_path) + return app.get_app(git_lfs_enabled, git_lfs_store_path, git_lfs_http_scheme) diff --git a/vcsserver/settings.py b/vcsserver/settings.py --- a/vcsserver/settings.py +++ b/vcsserver/settings.py @@ -17,4 +17,6 @@ WIRE_ENCODING = 'UTF-8' GIT_EXECUTABLE = 'git' +SVN_EXECUTABLE = 'svn' +SVNLOOK_EXECUTABLE = 'svnlook' BINARY_DIR = '' diff --git a/vcsserver/svn.py b/vcsserver/svn.py --- a/vcsserver/svn.py +++ b/vcsserver/svn.py @@ -139,6 +139,16 @@ class SvnRemote(object): svn_ver = None return svn_ver + @reraise_safe_exceptions + def is_empty(self, wire): + repo = self._factory.repo(wire) + + try: + return self.lookup(wire, -1) == 0 + except Exception: + log.exception("failed to read object_store") + return False + def check_url(self, url, config_items): # this can throw exception if not installed, but we detect this from hgsubversion import svnrepo @@ -448,6 +458,39 @@ class SvnRemote(object): return False @reraise_safe_exceptions + def run_svn_command(self, wire, cmd, **opts): + path = wire.get('path', None) + + if path and os.path.isdir(path): + opts['cwd'] = path + + safe_call = False + if '_safe' in opts: + safe_call = True + + svnenv = os.environ.copy() + svnenv.update(opts.pop('extra_env', {})) + + _opts = {'env': svnenv, 'shell': False} + + try: + _opts.update(opts) + p = subprocessio.SubprocessIOChunker(cmd, **_opts) + + return ''.join(p), ''.join(p.error) + except (EnvironmentError, OSError) as err: + cmd = ' '.join(cmd) # human friendly CMD + tb_err = ("Couldn't run svn command (%s).\n" + "Original error was:%s\n" + "Call options:%s\n" + % (cmd, err, _opts)) + log.exception(tb_err) + if safe_call: + return '', err + else: + raise exceptions.VcsException()(tb_err) + + @reraise_safe_exceptions def install_hooks(self, wire, force=False): from vcsserver.hook_utils import install_svn_hooks repo_path = wire['path']