##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r595:0d6e5c13 merge stable
parent child Browse files
Show More
@@ -0,0 +1,26 b''
1 diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
2 --- a/Documentation/git-send-email.txt
3 +++ b/Documentation/git-send-email.txt
4 @@ -208,8 +208,7 @@ a password is obtained using 'git-credential'.
5 specify a full pathname of a sendmail-like program instead;
6 the program must support the `-i` option. Default value can
7 be specified by the `sendemail.smtpServer` configuration
8 - option; the built-in default is to search for `sendmail` in
9 - `/usr/sbin`, `/usr/lib` and $PATH if such program is
10 + option; the built-in default is to search in $PATH if such program is
11 available, falling back to `localhost` otherwise.
12
13 --smtp-server-port=<port>::
14 diff --git a/git-send-email.perl b/git-send-email.perl
15 --- a/git-send-email.perl
16 +++ b/git-send-email.perl
17 @@ -944,8 +944,7 @@ if (defined $reply_to) {
18 }
19
20 if (!defined $smtp_server) {
21 - my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail );
22 - push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH};
23 + my @sendmail_paths = map {"$_/sendmail"} split /:/, $ENV{PATH};
24 foreach (@sendmail_paths) {
25 if (-x $_) {
26 $smtp_server = $_;
@@ -0,0 +1,12 b''
1 diff --git a/t/test-lib.sh b/t/test-lib.sh
2 --- a/t/test-lib.sh
3 +++ b/t/test-lib.sh
4 @@ -923,7 +923,7 @@
5 then
6 GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) ||
7 error "Cannot run git from $GIT_TEST_INSTALLED."
8 - PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR:$PATH
9 + PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR/t/helper:$GIT_BUILD_DIR:$PATH
10 GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
11 else # normal case, use ../bin-wrappers only unless $with_dashes:
12 git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
@@ -1,6 +1,6 b''
1 [bumpversion]
1 [bumpversion]
2 current_version = 4.14.1
2 current_version = 4.15.0
3 message = release: Bump version {current_version} to {new_version}
3 message = release: Bump version {current_version} to {new_version}
4
4
5 [bumpversion:file:vcsserver/VERSION]
5 [bumpversion:file:vcsserver/VERSION]
6
6
@@ -1,16 +1,14 b''
1 [DEFAULT]
1 [DEFAULT]
2 done = false
2 done = false
3
3
4 [task:bump_version]
4 [task:bump_version]
5 done = true
5 done = true
6
6
7 [task:fixes_on_stable]
7 [task:fixes_on_stable]
8 done = true
9
8
10 [task:pip2nix_generated]
9 [task:pip2nix_generated]
11 done = true
12
10
13 [release]
11 [release]
14 state = prepared
12 state = in_progress
15 version = 4.14.1
13 version = 4.15.0
16
14
@@ -1,45 +1,47 b''
1 self: super: {
1 self: super: {
2 # bump GIT version
2 # bump GIT version
3 git = super.lib.overrideDerivation super.git (oldAttrs: {
3 git = super.lib.overrideDerivation super.git (oldAttrs: {
4 name = "git-2.17.2";
4 name = "git-2.19.1";
5 src = self.fetchurl {
5 src = self.fetchurl {
6 url = "https://www.kernel.org/pub/software/scm/git/git-2.17.2.tar.xz";
6 url = "https://www.kernel.org/pub/software/scm/git/git-2.19.1.tar.xz";
7 sha256 = "1ghljlxmyqphx13qspy382cpl2pbkbwbhqm7w7z57r9mkhswx668";
7 sha256 = "1dfv43lmdnxz42504jc89sihbv1d4d6kgqcz3c5ji140kfm5cl1l";
8 };
8 };
9
9
10 # patches come from: https://github.com/NixOS/nixpkgs/tree/master/pkgs/applications/version-management/git-and-tools/git
10 patches = [
11 patches = [
11 ./patches/git/docbook2texi.patch
12 ./patches/git/docbook2texi.patch
12 ./patches/git/symlinks-in-bin.patch
13 ./patches/git/git-sh-i18n.patch
13 ./patches/git/git-sh-i18n.patch
14 ./patches/git/ssh-path.patch
14 ./patches/git/ssh-path.patch
15 ./patches/git/git-send-email-honor-PATH.patch
16 ./patches/git/installCheck-path.patch
15 ];
17 ];
16
18
17 });
19 });
18
20
19 # Override subversion derivation to
21 # Override subversion derivation to
20 # - activate python bindings
22 # - activate python bindings
21 subversion =
23 subversion =
22 let
24 let
23 subversionWithPython = super.subversion.override {
25 subversionWithPython = super.subversion.override {
24 httpSupport = true;
26 httpSupport = true;
25 pythonBindings = true;
27 pythonBindings = true;
26 python = self.python27Packages.python;
28 python = self.python27Packages.python;
27 };
29 };
28 in
30 in
29 super.lib.overrideDerivation subversionWithPython (oldAttrs: {
31 super.lib.overrideDerivation subversionWithPython (oldAttrs: {
30 name = "subversion-1.10.2";
32 name = "subversion-1.10.2";
31 src = self.fetchurl {
33 src = self.fetchurl {
32 url = "https://archive.apache.org/dist/subversion/subversion-1.10.2.tar.gz";
34 url = "https://archive.apache.org/dist/subversion/subversion-1.10.2.tar.gz";
33 sha256 = "0xv5z2bg0lw7057g913yc13f60nfj257wvmsq22pr33m4syf26sg";
35 sha256 = "0xv5z2bg0lw7057g913yc13f60nfj257wvmsq22pr33m4syf26sg";
34 };
36 };
35
37
36 ## use internal lz4/utf8proc because it is stable and shipped with SVN
38 ## use internal lz4/utf8proc because it is stable and shipped with SVN
37 configureFlags = oldAttrs.configureFlags ++ [
39 configureFlags = oldAttrs.configureFlags ++ [
38 " --with-lz4=internal"
40 " --with-lz4=internal"
39 " --with-utf8proc=internal"
41 " --with-utf8proc=internal"
40 ];
42 ];
41
43
42
44
43 });
45 });
44
46
45 }
47 }
@@ -1,37 +1,37 b''
1 This patch does two things: (1) use the right name for `docbook2texi',
1 This patch does two things: (1) use the right name for `docbook2texi',
2 and (2) make sure `gitman.info' isn't produced since it's broken (duplicate
2 and (2) make sure `gitman.info' isn't produced since it's broken (duplicate
3 node names).
3 node names).
4
4
5 diff -ru git-1.8.4-orig/Documentation/Makefile git-1.8.4/Documentation/Makefile
5 diff --git a/Documentation/Makefile b/Documentation/Makefile
6 --- git-1.8.4-orig/Documentation/Makefile 2013-08-23 21:38:43.000000000 +0200
6 --- a/Documentation/Makefile
7 +++ git-1.8.4/Documentation/Makefile 2013-09-30 14:48:51.532890378 +0200
7 +++ b/Documentation/Makefile
8 @@ -101,7 +101,7 @@
8 @@ -122,7 +122,7 @@
9
9
10 MAKEINFO = makeinfo
10 MAKEINFO = makeinfo
11 INSTALL_INFO = install-info
11 INSTALL_INFO = install-info
12 -DOCBOOK2X_TEXI = docbook2x-texi
12 -DOCBOOK2X_TEXI = docbook2x-texi
13 +DOCBOOK2X_TEXI = docbook2texi
13 +DOCBOOK2X_TEXI = docbook2texi
14 DBLATEX = dblatex
14 DBLATEX = dblatex
15 ifndef PERL_PATH
15 ASCIIDOC_DBLATEX_DIR = /etc/asciidoc/dblatex
16 PERL_PATH = /usr/bin/perl
16 DBLATEX_COMMON = -p $(ASCIIDOC_DBLATEX_DIR)/asciidoc-dblatex.xsl -s $(ASCIIDOC_DBLATEX_DIR)/asciidoc-dblatex.sty
17 @@ -205,7 +205,7 @@
17 @@ -240,7 +240,7 @@
18 man5: $(DOC_MAN5)
18 man5: $(DOC_MAN5)
19 man7: $(DOC_MAN7)
19 man7: $(DOC_MAN7)
20
20
21 -info: git.info gitman.info
21 -info: git.info gitman.info
22 +info: git.info
22 +info: git.info
23
23
24 pdf: user-manual.pdf
24 pdf: user-manual.pdf
25
25
26 @@ -221,10 +221,9 @@
26 @@ -256,10 +256,9 @@
27
27
28 install-info: info
28 install-info: info
29 $(INSTALL) -d -m 755 $(DESTDIR)$(infodir)
29 $(INSTALL) -d -m 755 $(DESTDIR)$(infodir)
30 - $(INSTALL) -m 644 git.info gitman.info $(DESTDIR)$(infodir)
30 - $(INSTALL) -m 644 git.info gitman.info $(DESTDIR)$(infodir)
31 + $(INSTALL) -m 644 git.info $(DESTDIR)$(infodir)
31 + $(INSTALL) -m 644 git.info $(DESTDIR)$(infodir)
32 if test -r $(DESTDIR)$(infodir)/dir; then \
32 if test -r $(DESTDIR)$(infodir)/dir; then \
33 $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\
33 $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\
34 - $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) gitman.info ;\
34 - $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) gitman.info ;\
35 else \
35 else \
36 echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
36 echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
37 fi
37 fi
@@ -1,950 +1,950 b''
1 # Generated by pip2nix 0.8.0.dev1
1 # Generated by pip2nix 0.8.0.dev1
2 # See https://github.com/johbo/pip2nix
2 # See https://github.com/johbo/pip2nix
3
3
4 { pkgs, fetchurl, fetchgit, fetchhg }:
4 { pkgs, fetchurl, fetchgit, fetchhg }:
5
5
6 self: super: {
6 self: super: {
7 "atomicwrites" = super.buildPythonPackage {
7 "atomicwrites" = super.buildPythonPackage {
8 name = "atomicwrites-1.2.1";
8 name = "atomicwrites-1.2.1";
9 doCheck = false;
9 doCheck = false;
10 src = fetchurl {
10 src = fetchurl {
11 url = "https://files.pythonhosted.org/packages/ac/ed/a311712ef6b4355035489f665e63e1a73f9eb371929e3c98e5efd451069e/atomicwrites-1.2.1.tar.gz";
11 url = "https://files.pythonhosted.org/packages/ac/ed/a311712ef6b4355035489f665e63e1a73f9eb371929e3c98e5efd451069e/atomicwrites-1.2.1.tar.gz";
12 sha256 = "1vmkbw9j0qammwxbxycrs39gvdg4lc2d4lk98kwf8ag2manyi6pc";
12 sha256 = "1vmkbw9j0qammwxbxycrs39gvdg4lc2d4lk98kwf8ag2manyi6pc";
13 };
13 };
14 meta = {
14 meta = {
15 license = [ pkgs.lib.licenses.mit ];
15 license = [ pkgs.lib.licenses.mit ];
16 };
16 };
17 };
17 };
18 "attrs" = super.buildPythonPackage {
18 "attrs" = super.buildPythonPackage {
19 name = "attrs-18.2.0";
19 name = "attrs-18.2.0";
20 doCheck = false;
20 doCheck = false;
21 src = fetchurl {
21 src = fetchurl {
22 url = "https://files.pythonhosted.org/packages/0f/9e/26b1d194aab960063b266170e53c39f73ea0d0d3f5ce23313e0ec8ee9bdf/attrs-18.2.0.tar.gz";
22 url = "https://files.pythonhosted.org/packages/0f/9e/26b1d194aab960063b266170e53c39f73ea0d0d3f5ce23313e0ec8ee9bdf/attrs-18.2.0.tar.gz";
23 sha256 = "0s9ydh058wmmf5v391pym877x4ahxg45dw6a0w4c7s5wgpigdjqh";
23 sha256 = "0s9ydh058wmmf5v391pym877x4ahxg45dw6a0w4c7s5wgpigdjqh";
24 };
24 };
25 meta = {
25 meta = {
26 license = [ pkgs.lib.licenses.mit ];
26 license = [ pkgs.lib.licenses.mit ];
27 };
27 };
28 };
28 };
29 "backports.shutil-get-terminal-size" = super.buildPythonPackage {
29 "backports.shutil-get-terminal-size" = super.buildPythonPackage {
30 name = "backports.shutil-get-terminal-size-1.0.0";
30 name = "backports.shutil-get-terminal-size-1.0.0";
31 doCheck = false;
31 doCheck = false;
32 src = fetchurl {
32 src = fetchurl {
33 url = "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
33 url = "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
34 sha256 = "107cmn7g3jnbkp826zlj8rrj19fam301qvaqf0f3905f5217lgki";
34 sha256 = "107cmn7g3jnbkp826zlj8rrj19fam301qvaqf0f3905f5217lgki";
35 };
35 };
36 meta = {
36 meta = {
37 license = [ pkgs.lib.licenses.mit ];
37 license = [ pkgs.lib.licenses.mit ];
38 };
38 };
39 };
39 };
40 "beautifulsoup4" = super.buildPythonPackage {
40 "beautifulsoup4" = super.buildPythonPackage {
41 name = "beautifulsoup4-4.6.3";
41 name = "beautifulsoup4-4.6.3";
42 doCheck = false;
42 doCheck = false;
43 src = fetchurl {
43 src = fetchurl {
44 url = "https://files.pythonhosted.org/packages/88/df/86bffad6309f74f3ff85ea69344a078fc30003270c8df6894fca7a3c72ff/beautifulsoup4-4.6.3.tar.gz";
44 url = "https://files.pythonhosted.org/packages/88/df/86bffad6309f74f3ff85ea69344a078fc30003270c8df6894fca7a3c72ff/beautifulsoup4-4.6.3.tar.gz";
45 sha256 = "041dhalzjciw6qyzzq7a2k4h1yvyk76xigp35hv5ibnn448ydy4h";
45 sha256 = "041dhalzjciw6qyzzq7a2k4h1yvyk76xigp35hv5ibnn448ydy4h";
46 };
46 };
47 meta = {
47 meta = {
48 license = [ pkgs.lib.licenses.mit ];
48 license = [ pkgs.lib.licenses.mit ];
49 };
49 };
50 };
50 };
51 "configobj" = super.buildPythonPackage {
51 "configobj" = super.buildPythonPackage {
52 name = "configobj-5.0.6";
52 name = "configobj-5.0.6";
53 doCheck = false;
53 doCheck = false;
54 propagatedBuildInputs = [
54 propagatedBuildInputs = [
55 self."six"
55 self."six"
56 ];
56 ];
57 src = fetchurl {
57 src = fetchurl {
58 url = "https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c";
58 url = "https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c";
59 sha256 = "1hhcxirwvg58grlfr177b3awhbq8hlx1l3lh69ifl1ki7lfd1s1x";
59 sha256 = "1hhcxirwvg58grlfr177b3awhbq8hlx1l3lh69ifl1ki7lfd1s1x";
60 };
60 };
61 meta = {
61 meta = {
62 license = [ pkgs.lib.licenses.bsdOriginal ];
62 license = [ pkgs.lib.licenses.bsdOriginal ];
63 };
63 };
64 };
64 };
65 "cov-core" = super.buildPythonPackage {
65 "cov-core" = super.buildPythonPackage {
66 name = "cov-core-1.15.0";
66 name = "cov-core-1.15.0";
67 doCheck = false;
67 doCheck = false;
68 propagatedBuildInputs = [
68 propagatedBuildInputs = [
69 self."coverage"
69 self."coverage"
70 ];
70 ];
71 src = fetchurl {
71 src = fetchurl {
72 url = "https://files.pythonhosted.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
72 url = "https://files.pythonhosted.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
73 sha256 = "0k3np9ymh06yv1ib96sb6wfsxjkqhmik8qfsn119vnhga9ywc52a";
73 sha256 = "0k3np9ymh06yv1ib96sb6wfsxjkqhmik8qfsn119vnhga9ywc52a";
74 };
74 };
75 meta = {
75 meta = {
76 license = [ pkgs.lib.licenses.mit ];
76 license = [ pkgs.lib.licenses.mit ];
77 };
77 };
78 };
78 };
79 "coverage" = super.buildPythonPackage {
79 "coverage" = super.buildPythonPackage {
80 name = "coverage-4.5.1";
80 name = "coverage-4.5.1";
81 doCheck = false;
81 doCheck = false;
82 src = fetchurl {
82 src = fetchurl {
83 url = "https://files.pythonhosted.org/packages/35/fe/e7df7289d717426093c68d156e0fd9117c8f4872b6588e8a8928a0f68424/coverage-4.5.1.tar.gz";
83 url = "https://files.pythonhosted.org/packages/35/fe/e7df7289d717426093c68d156e0fd9117c8f4872b6588e8a8928a0f68424/coverage-4.5.1.tar.gz";
84 sha256 = "1wbrzpxka3xd4nmmkc6q0ir343d91kymwsm8pbmwa0d2a7q4ir2n";
84 sha256 = "1wbrzpxka3xd4nmmkc6q0ir343d91kymwsm8pbmwa0d2a7q4ir2n";
85 };
85 };
86 meta = {
86 meta = {
87 license = [ pkgs.lib.licenses.asl20 ];
87 license = [ pkgs.lib.licenses.asl20 ];
88 };
88 };
89 };
89 };
90 "decorator" = super.buildPythonPackage {
90 "decorator" = super.buildPythonPackage {
91 name = "decorator-4.1.2";
91 name = "decorator-4.1.2";
92 doCheck = false;
92 doCheck = false;
93 src = fetchurl {
93 src = fetchurl {
94 url = "https://files.pythonhosted.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
94 url = "https://files.pythonhosted.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
95 sha256 = "1d8npb11kxyi36mrvjdpcjij76l5zfyrz2f820brf0l0rcw4vdkw";
95 sha256 = "1d8npb11kxyi36mrvjdpcjij76l5zfyrz2f820brf0l0rcw4vdkw";
96 };
96 };
97 meta = {
97 meta = {
98 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
98 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
99 };
99 };
100 };
100 };
101 "dogpile.cache" = super.buildPythonPackage {
101 "dogpile.cache" = super.buildPythonPackage {
102 name = "dogpile.cache-0.6.7";
102 name = "dogpile.cache-0.6.7";
103 doCheck = false;
103 doCheck = false;
104 src = fetchurl {
104 src = fetchurl {
105 url = "https://files.pythonhosted.org/packages/ee/bd/440da735a11c6087eed7cc8747fc4b995cbac2464168682f8ee1c8e43844/dogpile.cache-0.6.7.tar.gz";
105 url = "https://files.pythonhosted.org/packages/ee/bd/440da735a11c6087eed7cc8747fc4b995cbac2464168682f8ee1c8e43844/dogpile.cache-0.6.7.tar.gz";
106 sha256 = "1aw8rx8vhb75y7zc6gi67g21sw057jdx7i8m3jq7kf3nqavxx9zw";
106 sha256 = "1aw8rx8vhb75y7zc6gi67g21sw057jdx7i8m3jq7kf3nqavxx9zw";
107 };
107 };
108 meta = {
108 meta = {
109 license = [ pkgs.lib.licenses.bsdOriginal ];
109 license = [ pkgs.lib.licenses.bsdOriginal ];
110 };
110 };
111 };
111 };
112 "dogpile.core" = super.buildPythonPackage {
112 "dogpile.core" = super.buildPythonPackage {
113 name = "dogpile.core-0.4.1";
113 name = "dogpile.core-0.4.1";
114 doCheck = false;
114 doCheck = false;
115 src = fetchurl {
115 src = fetchurl {
116 url = "https://files.pythonhosted.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
116 url = "https://files.pythonhosted.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
117 sha256 = "0xpdvg4kr1isfkrh1rfsh7za4q5a5s6l2kf9wpvndbwf3aqjyrdy";
117 sha256 = "0xpdvg4kr1isfkrh1rfsh7za4q5a5s6l2kf9wpvndbwf3aqjyrdy";
118 };
118 };
119 meta = {
119 meta = {
120 license = [ pkgs.lib.licenses.bsdOriginal ];
120 license = [ pkgs.lib.licenses.bsdOriginal ];
121 };
121 };
122 };
122 };
123 "dulwich" = super.buildPythonPackage {
123 "dulwich" = super.buildPythonPackage {
124 name = "dulwich-0.13.0";
124 name = "dulwich-0.13.0";
125 doCheck = false;
125 doCheck = false;
126 src = fetchurl {
126 src = fetchurl {
127 url = "https://files.pythonhosted.org/packages/84/95/732d280eee829dacc954e8109f97b47abcadcca472c2ab013e1635eb4792/dulwich-0.13.0.tar.gz";
127 url = "https://files.pythonhosted.org/packages/84/95/732d280eee829dacc954e8109f97b47abcadcca472c2ab013e1635eb4792/dulwich-0.13.0.tar.gz";
128 sha256 = "0f1jwvrh549c4rgavkn3wizrch904s73s4fmrxykxy9cw8s57lwf";
128 sha256 = "0f1jwvrh549c4rgavkn3wizrch904s73s4fmrxykxy9cw8s57lwf";
129 };
129 };
130 meta = {
130 meta = {
131 license = [ pkgs.lib.licenses.gpl2Plus ];
131 license = [ pkgs.lib.licenses.gpl2Plus ];
132 };
132 };
133 };
133 };
134 "enum34" = super.buildPythonPackage {
134 "enum34" = super.buildPythonPackage {
135 name = "enum34-1.1.6";
135 name = "enum34-1.1.6";
136 doCheck = false;
136 doCheck = false;
137 src = fetchurl {
137 src = fetchurl {
138 url = "https://files.pythonhosted.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
138 url = "https://files.pythonhosted.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
139 sha256 = "1cgm5ng2gcfrkrm3hc22brl6chdmv67b9zvva9sfs7gn7dwc9n4a";
139 sha256 = "1cgm5ng2gcfrkrm3hc22brl6chdmv67b9zvva9sfs7gn7dwc9n4a";
140 };
140 };
141 meta = {
141 meta = {
142 license = [ pkgs.lib.licenses.bsdOriginal ];
142 license = [ pkgs.lib.licenses.bsdOriginal ];
143 };
143 };
144 };
144 };
145 "funcsigs" = super.buildPythonPackage {
145 "funcsigs" = super.buildPythonPackage {
146 name = "funcsigs-1.0.2";
146 name = "funcsigs-1.0.2";
147 doCheck = false;
147 doCheck = false;
148 src = fetchurl {
148 src = fetchurl {
149 url = "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
149 url = "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
150 sha256 = "0l4g5818ffyfmfs1a924811azhjj8ax9xd1cffr1mzd3ycn0zfx7";
150 sha256 = "0l4g5818ffyfmfs1a924811azhjj8ax9xd1cffr1mzd3ycn0zfx7";
151 };
151 };
152 meta = {
152 meta = {
153 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
153 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
154 };
154 };
155 };
155 };
156 "gevent" = super.buildPythonPackage {
156 "gevent" = super.buildPythonPackage {
157 name = "gevent-1.3.6";
157 name = "gevent-1.3.7";
158 doCheck = false;
158 doCheck = false;
159 propagatedBuildInputs = [
159 propagatedBuildInputs = [
160 self."greenlet"
160 self."greenlet"
161 ];
161 ];
162 src = fetchurl {
162 src = fetchurl {
163 url = "https://files.pythonhosted.org/packages/49/13/aa4bb3640b5167fe58875d3d7e65390cdb14f9682a41a741a566bb560842/gevent-1.3.6.tar.gz";
163 url = "https://files.pythonhosted.org/packages/10/c1/9499b146bfa43aa4f1e0ed1bab1bd3209a4861d25650c11725036c731cf5/gevent-1.3.7.tar.gz";
164 sha256 = "1ih4k73dqz2zb561hda99vbanja3m6cdch3mgxxn1mla3qwkqhbv";
164 sha256 = "0b0fr04qdk1p4sniv87fh8z5psac60x01pv054kpgi94520g81iz";
165 };
165 };
166 meta = {
166 meta = {
167 license = [ pkgs.lib.licenses.mit ];
167 license = [ pkgs.lib.licenses.mit ];
168 };
168 };
169 };
169 };
170 "gprof2dot" = super.buildPythonPackage {
170 "gprof2dot" = super.buildPythonPackage {
171 name = "gprof2dot-2017.9.19";
171 name = "gprof2dot-2017.9.19";
172 doCheck = false;
172 doCheck = false;
173 src = fetchurl {
173 src = fetchurl {
174 url = "https://files.pythonhosted.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
174 url = "https://files.pythonhosted.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
175 sha256 = "17ih23ld2nzgc3xwgbay911l6lh96jp1zshmskm17n1gg2i7mg6f";
175 sha256 = "17ih23ld2nzgc3xwgbay911l6lh96jp1zshmskm17n1gg2i7mg6f";
176 };
176 };
177 meta = {
177 meta = {
178 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
178 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
179 };
179 };
180 };
180 };
181 "greenlet" = super.buildPythonPackage {
181 "greenlet" = super.buildPythonPackage {
182 name = "greenlet-0.4.15";
182 name = "greenlet-0.4.15";
183 doCheck = false;
183 doCheck = false;
184 src = fetchurl {
184 src = fetchurl {
185 url = "https://files.pythonhosted.org/packages/f8/e8/b30ae23b45f69aa3f024b46064c0ac8e5fcb4f22ace0dca8d6f9c8bbe5e7/greenlet-0.4.15.tar.gz";
185 url = "https://files.pythonhosted.org/packages/f8/e8/b30ae23b45f69aa3f024b46064c0ac8e5fcb4f22ace0dca8d6f9c8bbe5e7/greenlet-0.4.15.tar.gz";
186 sha256 = "1g4g1wwc472ds89zmqlpyan3fbnzpa8qm48z3z1y6mlk44z485ll";
186 sha256 = "1g4g1wwc472ds89zmqlpyan3fbnzpa8qm48z3z1y6mlk44z485ll";
187 };
187 };
188 meta = {
188 meta = {
189 license = [ pkgs.lib.licenses.mit ];
189 license = [ pkgs.lib.licenses.mit ];
190 };
190 };
191 };
191 };
192 "gunicorn" = super.buildPythonPackage {
192 "gunicorn" = super.buildPythonPackage {
193 name = "gunicorn-19.9.0";
193 name = "gunicorn-19.9.0";
194 doCheck = false;
194 doCheck = false;
195 src = fetchurl {
195 src = fetchurl {
196 url = "https://files.pythonhosted.org/packages/47/52/68ba8e5e8ba251e54006a49441f7ccabca83b6bef5aedacb4890596c7911/gunicorn-19.9.0.tar.gz";
196 url = "https://files.pythonhosted.org/packages/47/52/68ba8e5e8ba251e54006a49441f7ccabca83b6bef5aedacb4890596c7911/gunicorn-19.9.0.tar.gz";
197 sha256 = "1wzlf4xmn6qjirh5w81l6i6kqjnab1n1qqkh7zsj1yb6gh4n49ps";
197 sha256 = "1wzlf4xmn6qjirh5w81l6i6kqjnab1n1qqkh7zsj1yb6gh4n49ps";
198 };
198 };
199 meta = {
199 meta = {
200 license = [ pkgs.lib.licenses.mit ];
200 license = [ pkgs.lib.licenses.mit ];
201 };
201 };
202 };
202 };
203 "hg-evolve" = super.buildPythonPackage {
203 "hg-evolve" = super.buildPythonPackage {
204 name = "hg-evolve-8.0.1";
204 name = "hg-evolve-8.0.1";
205 doCheck = false;
205 doCheck = false;
206 src = fetchurl {
206 src = fetchurl {
207 url = "https://files.pythonhosted.org/packages/06/1a/c5c12d8f117426f05285a820ee5a23121882f5381104e86276b72598934f/hg-evolve-8.0.1.tar.gz";
207 url = "https://files.pythonhosted.org/packages/06/1a/c5c12d8f117426f05285a820ee5a23121882f5381104e86276b72598934f/hg-evolve-8.0.1.tar.gz";
208 sha256 = "1brafifb42k71gl7qssb5m3ijnm7y30lfvm90z8xxcr2fgz19p29";
208 sha256 = "1brafifb42k71gl7qssb5m3ijnm7y30lfvm90z8xxcr2fgz19p29";
209 };
209 };
210 meta = {
210 meta = {
211 license = [ { fullName = "GPLv2+"; } ];
211 license = [ { fullName = "GPLv2+"; } ];
212 };
212 };
213 };
213 };
214 "hgsubversion" = super.buildPythonPackage {
214 "hgsubversion" = super.buildPythonPackage {
215 name = "hgsubversion-1.9.2";
215 name = "hgsubversion-1.9.3";
216 doCheck = false;
216 doCheck = false;
217 propagatedBuildInputs = [
217 propagatedBuildInputs = [
218 self."mercurial"
218 self."mercurial"
219 self."subvertpy"
219 self."subvertpy"
220 ];
220 ];
221 src = fetchurl {
221 src = fetchurl {
222 url = "https://files.pythonhosted.org/packages/05/80/3a3cef10dd65e86528ef8d7ac57a41ebc782d0f3c6cfa4fed021aa9fbee0/hgsubversion-1.9.2.tar.gz";
222 url = "https://files.pythonhosted.org/packages/a3/53/6d205e641f3e09abcf1ddaed66e5e4b20da22d0145566d440a02c9e35f0d/hgsubversion-1.9.3.tar.gz";
223 sha256 = "16490narhq14vskml3dam8g5y3w3hdqj3g8bgm2b0c0i85l1xvcz";
223 sha256 = "0nymcjlch8c4zjbncrs30p2nrbylsf25g3h6mr0zzzxr141h3sig";
224 };
224 };
225 meta = {
225 meta = {
226 license = [ pkgs.lib.licenses.gpl1 ];
226 license = [ pkgs.lib.licenses.gpl1 ];
227 };
227 };
228 };
228 };
229 "hupper" = super.buildPythonPackage {
229 "hupper" = super.buildPythonPackage {
230 name = "hupper-1.3.1";
230 name = "hupper-1.4.2";
231 doCheck = false;
231 doCheck = false;
232 src = fetchurl {
232 src = fetchurl {
233 url = "https://files.pythonhosted.org/packages/cf/4b/467b826a84c8594b81f414b5ab6794e981951dac90ca40abaf9ea1cb36b0/hupper-1.3.1.tar.gz";
233 url = "https://files.pythonhosted.org/packages/f1/75/1915dc7650b4867fa3049256e24ca8eddb5989998fcec788cf52b9812dfc/hupper-1.4.2.tar.gz";
234 sha256 = "03mf13n6i4dd60wlb9m99ddl4m3lmly70cjp7f82vdkibfl1v6l9";
234 sha256 = "16vb9fkiaakdpcp6pn56h3w0dwvm67bxq2k2dv4i382qhqwphdzb";
235 };
235 };
236 meta = {
236 meta = {
237 license = [ pkgs.lib.licenses.mit ];
237 license = [ pkgs.lib.licenses.mit ];
238 };
238 };
239 };
239 };
240 "ipdb" = super.buildPythonPackage {
240 "ipdb" = super.buildPythonPackage {
241 name = "ipdb-0.11";
241 name = "ipdb-0.11";
242 doCheck = false;
242 doCheck = false;
243 propagatedBuildInputs = [
243 propagatedBuildInputs = [
244 self."setuptools"
244 self."setuptools"
245 self."ipython"
245 self."ipython"
246 ];
246 ];
247 src = fetchurl {
247 src = fetchurl {
248 url = "https://files.pythonhosted.org/packages/80/fe/4564de08f174f3846364b3add8426d14cebee228f741c27e702b2877e85b/ipdb-0.11.tar.gz";
248 url = "https://files.pythonhosted.org/packages/80/fe/4564de08f174f3846364b3add8426d14cebee228f741c27e702b2877e85b/ipdb-0.11.tar.gz";
249 sha256 = "02m0l8wrhhd3z7dg3czn5ys1g5pxib516hpshdzp7rxzsxgcd0bh";
249 sha256 = "02m0l8wrhhd3z7dg3czn5ys1g5pxib516hpshdzp7rxzsxgcd0bh";
250 };
250 };
251 meta = {
251 meta = {
252 license = [ pkgs.lib.licenses.bsdOriginal ];
252 license = [ pkgs.lib.licenses.bsdOriginal ];
253 };
253 };
254 };
254 };
255 "ipython" = super.buildPythonPackage {
255 "ipython" = super.buildPythonPackage {
256 name = "ipython-5.1.0";
256 name = "ipython-5.1.0";
257 doCheck = false;
257 doCheck = false;
258 propagatedBuildInputs = [
258 propagatedBuildInputs = [
259 self."setuptools"
259 self."setuptools"
260 self."decorator"
260 self."decorator"
261 self."pickleshare"
261 self."pickleshare"
262 self."simplegeneric"
262 self."simplegeneric"
263 self."traitlets"
263 self."traitlets"
264 self."prompt-toolkit"
264 self."prompt-toolkit"
265 self."pygments"
265 self."pygments"
266 self."pexpect"
266 self."pexpect"
267 self."backports.shutil-get-terminal-size"
267 self."backports.shutil-get-terminal-size"
268 self."pathlib2"
268 self."pathlib2"
269 self."pexpect"
269 self."pexpect"
270 ];
270 ];
271 src = fetchurl {
271 src = fetchurl {
272 url = "https://files.pythonhosted.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
272 url = "https://files.pythonhosted.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
273 sha256 = "0qdrf6aj9kvjczd5chj1my8y2iq09am9l8bb2a1334a52d76kx3y";
273 sha256 = "0qdrf6aj9kvjczd5chj1my8y2iq09am9l8bb2a1334a52d76kx3y";
274 };
274 };
275 meta = {
275 meta = {
276 license = [ pkgs.lib.licenses.bsdOriginal ];
276 license = [ pkgs.lib.licenses.bsdOriginal ];
277 };
277 };
278 };
278 };
279 "ipython-genutils" = super.buildPythonPackage {
279 "ipython-genutils" = super.buildPythonPackage {
280 name = "ipython-genutils-0.2.0";
280 name = "ipython-genutils-0.2.0";
281 doCheck = false;
281 doCheck = false;
282 src = fetchurl {
282 src = fetchurl {
283 url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
283 url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
284 sha256 = "1a4bc9y8hnvq6cp08qs4mckgm6i6ajpndp4g496rvvzcfmp12bpb";
284 sha256 = "1a4bc9y8hnvq6cp08qs4mckgm6i6ajpndp4g496rvvzcfmp12bpb";
285 };
285 };
286 meta = {
286 meta = {
287 license = [ pkgs.lib.licenses.bsdOriginal ];
287 license = [ pkgs.lib.licenses.bsdOriginal ];
288 };
288 };
289 };
289 };
290 "mako" = super.buildPythonPackage {
290 "mako" = super.buildPythonPackage {
291 name = "mako-1.0.7";
291 name = "mako-1.0.7";
292 doCheck = false;
292 doCheck = false;
293 propagatedBuildInputs = [
293 propagatedBuildInputs = [
294 self."markupsafe"
294 self."markupsafe"
295 ];
295 ];
296 src = fetchurl {
296 src = fetchurl {
297 url = "https://files.pythonhosted.org/packages/eb/f3/67579bb486517c0d49547f9697e36582cd19dafb5df9e687ed8e22de57fa/Mako-1.0.7.tar.gz";
297 url = "https://files.pythonhosted.org/packages/eb/f3/67579bb486517c0d49547f9697e36582cd19dafb5df9e687ed8e22de57fa/Mako-1.0.7.tar.gz";
298 sha256 = "1bi5gnr8r8dva06qpyx4kgjc6spm2k1y908183nbbaylggjzs0jf";
298 sha256 = "1bi5gnr8r8dva06qpyx4kgjc6spm2k1y908183nbbaylggjzs0jf";
299 };
299 };
300 meta = {
300 meta = {
301 license = [ pkgs.lib.licenses.mit ];
301 license = [ pkgs.lib.licenses.mit ];
302 };
302 };
303 };
303 };
304 "markupsafe" = super.buildPythonPackage {
304 "markupsafe" = super.buildPythonPackage {
305 name = "markupsafe-1.0";
305 name = "markupsafe-1.0";
306 doCheck = false;
306 doCheck = false;
307 src = fetchurl {
307 src = fetchurl {
308 url = "https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz";
308 url = "https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz";
309 sha256 = "0rdn1s8x9ni7ss8rfiacj7x1085lx8mh2zdwqslnw8xc3l4nkgm6";
309 sha256 = "0rdn1s8x9ni7ss8rfiacj7x1085lx8mh2zdwqslnw8xc3l4nkgm6";
310 };
310 };
311 meta = {
311 meta = {
312 license = [ pkgs.lib.licenses.bsdOriginal ];
312 license = [ pkgs.lib.licenses.bsdOriginal ];
313 };
313 };
314 };
314 };
315 "mercurial" = super.buildPythonPackage {
315 "mercurial" = super.buildPythonPackage {
316 name = "mercurial-4.6.2";
316 name = "mercurial-4.6.2";
317 doCheck = false;
317 doCheck = false;
318 src = fetchurl {
318 src = fetchurl {
319 url = "https://files.pythonhosted.org/packages/d9/fb/c7ecf2b7fd349878dbf45b8390b8db735cef73d49dd9ce8a364b4ca3a846/mercurial-4.6.2.tar.gz";
319 url = "https://files.pythonhosted.org/packages/d9/fb/c7ecf2b7fd349878dbf45b8390b8db735cef73d49dd9ce8a364b4ca3a846/mercurial-4.6.2.tar.gz";
320 sha256 = "1bv6wgcdx8glihjjfg22khhc52mclsn4kwfqvzbzlg0b42h4xl0w";
320 sha256 = "1bv6wgcdx8glihjjfg22khhc52mclsn4kwfqvzbzlg0b42h4xl0w";
321 };
321 };
322 meta = {
322 meta = {
323 license = [ pkgs.lib.licenses.gpl1 pkgs.lib.licenses.gpl2Plus ];
323 license = [ pkgs.lib.licenses.gpl1 pkgs.lib.licenses.gpl2Plus ];
324 };
324 };
325 };
325 };
326 "mock" = super.buildPythonPackage {
326 "mock" = super.buildPythonPackage {
327 name = "mock-1.0.1";
327 name = "mock-1.0.1";
328 doCheck = false;
328 doCheck = false;
329 src = fetchurl {
329 src = fetchurl {
330 url = "https://files.pythonhosted.org/packages/a2/52/7edcd94f0afb721a2d559a5b9aae8af4f8f2c79bc63fdbe8a8a6c9b23bbe/mock-1.0.1.tar.gz";
330 url = "https://files.pythonhosted.org/packages/a2/52/7edcd94f0afb721a2d559a5b9aae8af4f8f2c79bc63fdbe8a8a6c9b23bbe/mock-1.0.1.tar.gz";
331 sha256 = "0kzlsbki6q0awf89rc287f3aj8x431lrajf160a70z0ikhnxsfdq";
331 sha256 = "0kzlsbki6q0awf89rc287f3aj8x431lrajf160a70z0ikhnxsfdq";
332 };
332 };
333 meta = {
333 meta = {
334 license = [ pkgs.lib.licenses.bsdOriginal ];
334 license = [ pkgs.lib.licenses.bsdOriginal ];
335 };
335 };
336 };
336 };
337 "more-itertools" = super.buildPythonPackage {
337 "more-itertools" = super.buildPythonPackage {
338 name = "more-itertools-4.3.0";
338 name = "more-itertools-4.3.0";
339 doCheck = false;
339 doCheck = false;
340 propagatedBuildInputs = [
340 propagatedBuildInputs = [
341 self."six"
341 self."six"
342 ];
342 ];
343 src = fetchurl {
343 src = fetchurl {
344 url = "https://files.pythonhosted.org/packages/88/ff/6d485d7362f39880810278bdc906c13300db05485d9c65971dec1142da6a/more-itertools-4.3.0.tar.gz";
344 url = "https://files.pythonhosted.org/packages/88/ff/6d485d7362f39880810278bdc906c13300db05485d9c65971dec1142da6a/more-itertools-4.3.0.tar.gz";
345 sha256 = "17h3na0rdh8xq30w4b9pizgkdxmm51896bxw600x84jflg9vaxn4";
345 sha256 = "17h3na0rdh8xq30w4b9pizgkdxmm51896bxw600x84jflg9vaxn4";
346 };
346 };
347 meta = {
347 meta = {
348 license = [ pkgs.lib.licenses.mit ];
348 license = [ pkgs.lib.licenses.mit ];
349 };
349 };
350 };
350 };
351 "msgpack-python" = super.buildPythonPackage {
351 "msgpack-python" = super.buildPythonPackage {
352 name = "msgpack-python-0.5.6";
352 name = "msgpack-python-0.5.6";
353 doCheck = false;
353 doCheck = false;
354 src = fetchurl {
354 src = fetchurl {
355 url = "https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz";
355 url = "https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz";
356 sha256 = "16wh8qgybmfh4pjp8vfv78mdlkxfmcasg78lzlnm6nslsfkci31p";
356 sha256 = "16wh8qgybmfh4pjp8vfv78mdlkxfmcasg78lzlnm6nslsfkci31p";
357 };
357 };
358 meta = {
358 meta = {
359 license = [ pkgs.lib.licenses.asl20 ];
359 license = [ pkgs.lib.licenses.asl20 ];
360 };
360 };
361 };
361 };
362 "pastedeploy" = super.buildPythonPackage {
362 "pastedeploy" = super.buildPythonPackage {
363 name = "pastedeploy-1.5.2";
363 name = "pastedeploy-1.5.2";
364 doCheck = false;
364 doCheck = false;
365 src = fetchurl {
365 src = fetchurl {
366 url = "https://files.pythonhosted.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
366 url = "https://files.pythonhosted.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
367 sha256 = "1jz3m4hq8v6hyhfjz9425nd3nvn52cvbfipdcd72krjmla4qz1fm";
367 sha256 = "1jz3m4hq8v6hyhfjz9425nd3nvn52cvbfipdcd72krjmla4qz1fm";
368 };
368 };
369 meta = {
369 meta = {
370 license = [ pkgs.lib.licenses.mit ];
370 license = [ pkgs.lib.licenses.mit ];
371 };
371 };
372 };
372 };
373 "pathlib2" = super.buildPythonPackage {
373 "pathlib2" = super.buildPythonPackage {
374 name = "pathlib2-2.3.2";
374 name = "pathlib2-2.3.2";
375 doCheck = false;
375 doCheck = false;
376 propagatedBuildInputs = [
376 propagatedBuildInputs = [
377 self."six"
377 self."six"
378 self."scandir"
378 self."scandir"
379 ];
379 ];
380 src = fetchurl {
380 src = fetchurl {
381 url = "https://files.pythonhosted.org/packages/db/a8/7d6439c1aec525ed70810abee5b7d7f3aa35347f59bc28343e8f62019aa2/pathlib2-2.3.2.tar.gz";
381 url = "https://files.pythonhosted.org/packages/db/a8/7d6439c1aec525ed70810abee5b7d7f3aa35347f59bc28343e8f62019aa2/pathlib2-2.3.2.tar.gz";
382 sha256 = "10yb0iv5x2hs631rcppkhbddx799d3h8pcwmkbh2a66ns3w71ccf";
382 sha256 = "10yb0iv5x2hs631rcppkhbddx799d3h8pcwmkbh2a66ns3w71ccf";
383 };
383 };
384 meta = {
384 meta = {
385 license = [ pkgs.lib.licenses.mit ];
385 license = [ pkgs.lib.licenses.mit ];
386 };
386 };
387 };
387 };
388 "pexpect" = super.buildPythonPackage {
388 "pexpect" = super.buildPythonPackage {
389 name = "pexpect-4.6.0";
389 name = "pexpect-4.6.0";
390 doCheck = false;
390 doCheck = false;
391 propagatedBuildInputs = [
391 propagatedBuildInputs = [
392 self."ptyprocess"
392 self."ptyprocess"
393 ];
393 ];
394 src = fetchurl {
394 src = fetchurl {
395 url = "https://files.pythonhosted.org/packages/89/43/07d07654ee3e25235d8cea4164cdee0ec39d1fda8e9203156ebe403ffda4/pexpect-4.6.0.tar.gz";
395 url = "https://files.pythonhosted.org/packages/89/43/07d07654ee3e25235d8cea4164cdee0ec39d1fda8e9203156ebe403ffda4/pexpect-4.6.0.tar.gz";
396 sha256 = "1fla85g47iaxxpjhp9vkxdnv4pgc7rplfy6ja491smrrk0jqi3ia";
396 sha256 = "1fla85g47iaxxpjhp9vkxdnv4pgc7rplfy6ja491smrrk0jqi3ia";
397 };
397 };
398 meta = {
398 meta = {
399 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
399 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
400 };
400 };
401 };
401 };
402 "pickleshare" = super.buildPythonPackage {
402 "pickleshare" = super.buildPythonPackage {
403 name = "pickleshare-0.7.5";
403 name = "pickleshare-0.7.5";
404 doCheck = false;
404 doCheck = false;
405 propagatedBuildInputs = [
405 propagatedBuildInputs = [
406 self."pathlib2"
406 self."pathlib2"
407 ];
407 ];
408 src = fetchurl {
408 src = fetchurl {
409 url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz";
409 url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz";
410 sha256 = "1jmghg3c53yp1i8cm6pcrm280ayi8621rwyav9fac7awjr3kss47";
410 sha256 = "1jmghg3c53yp1i8cm6pcrm280ayi8621rwyav9fac7awjr3kss47";
411 };
411 };
412 meta = {
412 meta = {
413 license = [ pkgs.lib.licenses.mit ];
413 license = [ pkgs.lib.licenses.mit ];
414 };
414 };
415 };
415 };
416 "plaster" = super.buildPythonPackage {
416 "plaster" = super.buildPythonPackage {
417 name = "plaster-1.0";
417 name = "plaster-1.0";
418 doCheck = false;
418 doCheck = false;
419 propagatedBuildInputs = [
419 propagatedBuildInputs = [
420 self."setuptools"
420 self."setuptools"
421 ];
421 ];
422 src = fetchurl {
422 src = fetchurl {
423 url = "https://files.pythonhosted.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
423 url = "https://files.pythonhosted.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
424 sha256 = "1hy8k0nv2mxq94y5aysk6hjk9ryb4bsd13g83m60hcyzxz3wflc3";
424 sha256 = "1hy8k0nv2mxq94y5aysk6hjk9ryb4bsd13g83m60hcyzxz3wflc3";
425 };
425 };
426 meta = {
426 meta = {
427 license = [ pkgs.lib.licenses.mit ];
427 license = [ pkgs.lib.licenses.mit ];
428 };
428 };
429 };
429 };
430 "plaster-pastedeploy" = super.buildPythonPackage {
430 "plaster-pastedeploy" = super.buildPythonPackage {
431 name = "plaster-pastedeploy-0.6";
431 name = "plaster-pastedeploy-0.6";
432 doCheck = false;
432 doCheck = false;
433 propagatedBuildInputs = [
433 propagatedBuildInputs = [
434 self."pastedeploy"
434 self."pastedeploy"
435 self."plaster"
435 self."plaster"
436 ];
436 ];
437 src = fetchurl {
437 src = fetchurl {
438 url = "https://files.pythonhosted.org/packages/3f/e7/6a6833158d2038ec40085433308a1e164fd1dac595513f6dd556d5669bb8/plaster_pastedeploy-0.6.tar.gz";
438 url = "https://files.pythonhosted.org/packages/3f/e7/6a6833158d2038ec40085433308a1e164fd1dac595513f6dd556d5669bb8/plaster_pastedeploy-0.6.tar.gz";
439 sha256 = "1bkggk18f4z2bmsmxyxabvf62znvjwbivzh880419r3ap0616cf2";
439 sha256 = "1bkggk18f4z2bmsmxyxabvf62znvjwbivzh880419r3ap0616cf2";
440 };
440 };
441 meta = {
441 meta = {
442 license = [ pkgs.lib.licenses.mit ];
442 license = [ pkgs.lib.licenses.mit ];
443 };
443 };
444 };
444 };
445 "pluggy" = super.buildPythonPackage {
445 "pluggy" = super.buildPythonPackage {
446 name = "pluggy-0.8.0";
446 name = "pluggy-0.8.0";
447 doCheck = false;
447 doCheck = false;
448 src = fetchurl {
448 src = fetchurl {
449 url = "https://files.pythonhosted.org/packages/65/25/81d0de17cd00f8ca994a4e74e3c4baf7cd25072c0b831dad5c7d9d6138f8/pluggy-0.8.0.tar.gz";
449 url = "https://files.pythonhosted.org/packages/65/25/81d0de17cd00f8ca994a4e74e3c4baf7cd25072c0b831dad5c7d9d6138f8/pluggy-0.8.0.tar.gz";
450 sha256 = "1580p47l2zqzsza8jcnw1h2wh3vvmygk6ly8bvi4w0g8j14sjys4";
450 sha256 = "1580p47l2zqzsza8jcnw1h2wh3vvmygk6ly8bvi4w0g8j14sjys4";
451 };
451 };
452 meta = {
452 meta = {
453 license = [ pkgs.lib.licenses.mit ];
453 license = [ pkgs.lib.licenses.mit ];
454 };
454 };
455 };
455 };
456 "prompt-toolkit" = super.buildPythonPackage {
456 "prompt-toolkit" = super.buildPythonPackage {
457 name = "prompt-toolkit-1.0.15";
457 name = "prompt-toolkit-1.0.15";
458 doCheck = false;
458 doCheck = false;
459 propagatedBuildInputs = [
459 propagatedBuildInputs = [
460 self."six"
460 self."six"
461 self."wcwidth"
461 self."wcwidth"
462 ];
462 ];
463 src = fetchurl {
463 src = fetchurl {
464 url = "https://files.pythonhosted.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz";
464 url = "https://files.pythonhosted.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz";
465 sha256 = "05v9h5nydljwpj5nm8n804ms0glajwfy1zagrzqrg91wk3qqi1c5";
465 sha256 = "05v9h5nydljwpj5nm8n804ms0glajwfy1zagrzqrg91wk3qqi1c5";
466 };
466 };
467 meta = {
467 meta = {
468 license = [ pkgs.lib.licenses.bsdOriginal ];
468 license = [ pkgs.lib.licenses.bsdOriginal ];
469 };
469 };
470 };
470 };
471 "psutil" = super.buildPythonPackage {
471 "psutil" = super.buildPythonPackage {
472 name = "psutil-5.4.7";
472 name = "psutil-5.4.7";
473 doCheck = false;
473 doCheck = false;
474 src = fetchurl {
474 src = fetchurl {
475 url = "https://files.pythonhosted.org/packages/7d/9a/1e93d41708f8ed2b564395edfa3389f0fd6d567597401c2e5e2775118d8b/psutil-5.4.7.tar.gz";
475 url = "https://files.pythonhosted.org/packages/7d/9a/1e93d41708f8ed2b564395edfa3389f0fd6d567597401c2e5e2775118d8b/psutil-5.4.7.tar.gz";
476 sha256 = "0fsgmvzwbdbszkwfnqhib8jcxm4w6zyhvlxlcda0rfm5cyqj4qsv";
476 sha256 = "0fsgmvzwbdbszkwfnqhib8jcxm4w6zyhvlxlcda0rfm5cyqj4qsv";
477 };
477 };
478 meta = {
478 meta = {
479 license = [ pkgs.lib.licenses.bsdOriginal ];
479 license = [ pkgs.lib.licenses.bsdOriginal ];
480 };
480 };
481 };
481 };
482 "ptyprocess" = super.buildPythonPackage {
482 "ptyprocess" = super.buildPythonPackage {
483 name = "ptyprocess-0.6.0";
483 name = "ptyprocess-0.6.0";
484 doCheck = false;
484 doCheck = false;
485 src = fetchurl {
485 src = fetchurl {
486 url = "https://files.pythonhosted.org/packages/7d/2d/e4b8733cf79b7309d84c9081a4ab558c89d8c89da5961bf4ddb050ca1ce0/ptyprocess-0.6.0.tar.gz";
486 url = "https://files.pythonhosted.org/packages/7d/2d/e4b8733cf79b7309d84c9081a4ab558c89d8c89da5961bf4ddb050ca1ce0/ptyprocess-0.6.0.tar.gz";
487 sha256 = "1h4lcd3w5nrxnsk436ar7fwkiy5rfn5wj2xwy9l0r4mdqnf2jgwj";
487 sha256 = "1h4lcd3w5nrxnsk436ar7fwkiy5rfn5wj2xwy9l0r4mdqnf2jgwj";
488 };
488 };
489 meta = {
489 meta = {
490 license = [ ];
490 license = [ ];
491 };
491 };
492 };
492 };
493 "py" = super.buildPythonPackage {
493 "py" = super.buildPythonPackage {
494 name = "py-1.6.0";
494 name = "py-1.6.0";
495 doCheck = false;
495 doCheck = false;
496 src = fetchurl {
496 src = fetchurl {
497 url = "https://files.pythonhosted.org/packages/4f/38/5f427d1eedae73063ce4da680d2bae72014995f9fdeaa57809df61c968cd/py-1.6.0.tar.gz";
497 url = "https://files.pythonhosted.org/packages/4f/38/5f427d1eedae73063ce4da680d2bae72014995f9fdeaa57809df61c968cd/py-1.6.0.tar.gz";
498 sha256 = "1wcs3zv9wl5m5x7p16avqj2gsrviyb23yvc3pr330isqs0sh98q6";
498 sha256 = "1wcs3zv9wl5m5x7p16avqj2gsrviyb23yvc3pr330isqs0sh98q6";
499 };
499 };
500 meta = {
500 meta = {
501 license = [ pkgs.lib.licenses.mit ];
501 license = [ pkgs.lib.licenses.mit ];
502 };
502 };
503 };
503 };
504 "pygments" = super.buildPythonPackage {
504 "pygments" = super.buildPythonPackage {
505 name = "pygments-2.2.0";
505 name = "pygments-2.3.0";
506 doCheck = false;
506 doCheck = false;
507 src = fetchurl {
507 src = fetchurl {
508 url = "https://files.pythonhosted.org/packages/71/2a/2e4e77803a8bd6408a2903340ac498cb0a2181811af7c9ec92cb70b0308a/Pygments-2.2.0.tar.gz";
508 url = "https://files.pythonhosted.org/packages/63/a2/91c31c4831853dedca2a08a0f94d788fc26a48f7281c99a303769ad2721b/Pygments-2.3.0.tar.gz";
509 sha256 = "1k78qdvir1yb1c634nkv6rbga8wv4289xarghmsbbvzhvr311bnv";
509 sha256 = "1z34ms51dh4jq4h3cizp7vd1dmsxcbvffkjsd2xxfav22nn6lrl2";
510 };
510 };
511 meta = {
511 meta = {
512 license = [ pkgs.lib.licenses.bsdOriginal ];
512 license = [ pkgs.lib.licenses.bsdOriginal ];
513 };
513 };
514 };
514 };
515 "pyramid" = super.buildPythonPackage {
515 "pyramid" = super.buildPythonPackage {
516 name = "pyramid-1.9.2";
516 name = "pyramid-1.9.2";
517 doCheck = false;
517 doCheck = false;
518 propagatedBuildInputs = [
518 propagatedBuildInputs = [
519 self."setuptools"
519 self."setuptools"
520 self."webob"
520 self."webob"
521 self."repoze.lru"
521 self."repoze.lru"
522 self."zope.interface"
522 self."zope.interface"
523 self."zope.deprecation"
523 self."zope.deprecation"
524 self."venusian"
524 self."venusian"
525 self."translationstring"
525 self."translationstring"
526 self."pastedeploy"
526 self."pastedeploy"
527 self."plaster"
527 self."plaster"
528 self."plaster-pastedeploy"
528 self."plaster-pastedeploy"
529 self."hupper"
529 self."hupper"
530 ];
530 ];
531 src = fetchurl {
531 src = fetchurl {
532 url = "https://files.pythonhosted.org/packages/a0/c1/b321d07cfc4870541989ad131c86a1d593bfe802af0eca9718a0dadfb97a/pyramid-1.9.2.tar.gz";
532 url = "https://files.pythonhosted.org/packages/a0/c1/b321d07cfc4870541989ad131c86a1d593bfe802af0eca9718a0dadfb97a/pyramid-1.9.2.tar.gz";
533 sha256 = "09drsl0346nchgxp2j7sa5hlk7mkhfld9wvbd0wicacrp26a92fg";
533 sha256 = "09drsl0346nchgxp2j7sa5hlk7mkhfld9wvbd0wicacrp26a92fg";
534 };
534 };
535 meta = {
535 meta = {
536 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
536 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
537 };
537 };
538 };
538 };
539 "pyramid-mako" = super.buildPythonPackage {
539 "pyramid-mako" = super.buildPythonPackage {
540 name = "pyramid-mako-1.0.2";
540 name = "pyramid-mako-1.0.2";
541 doCheck = false;
541 doCheck = false;
542 propagatedBuildInputs = [
542 propagatedBuildInputs = [
543 self."pyramid"
543 self."pyramid"
544 self."mako"
544 self."mako"
545 ];
545 ];
546 src = fetchurl {
546 src = fetchurl {
547 url = "https://files.pythonhosted.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
547 url = "https://files.pythonhosted.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
548 sha256 = "18gk2vliq8z4acblsl6yzgbvnr9rlxjlcqir47km7kvlk1xri83d";
548 sha256 = "18gk2vliq8z4acblsl6yzgbvnr9rlxjlcqir47km7kvlk1xri83d";
549 };
549 };
550 meta = {
550 meta = {
551 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
551 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
552 };
552 };
553 };
553 };
554 "pytest" = super.buildPythonPackage {
554 "pytest" = super.buildPythonPackage {
555 name = "pytest-3.8.2";
555 name = "pytest-3.8.2";
556 doCheck = false;
556 doCheck = false;
557 propagatedBuildInputs = [
557 propagatedBuildInputs = [
558 self."py"
558 self."py"
559 self."six"
559 self."six"
560 self."setuptools"
560 self."setuptools"
561 self."attrs"
561 self."attrs"
562 self."more-itertools"
562 self."more-itertools"
563 self."atomicwrites"
563 self."atomicwrites"
564 self."pluggy"
564 self."pluggy"
565 self."funcsigs"
565 self."funcsigs"
566 self."pathlib2"
566 self."pathlib2"
567 ];
567 ];
568 src = fetchurl {
568 src = fetchurl {
569 url = "https://files.pythonhosted.org/packages/5f/d2/7f77f406ac505abda02ab4afb50d06ebf304f6ea42fca34f8f37529106b2/pytest-3.8.2.tar.gz";
569 url = "https://files.pythonhosted.org/packages/5f/d2/7f77f406ac505abda02ab4afb50d06ebf304f6ea42fca34f8f37529106b2/pytest-3.8.2.tar.gz";
570 sha256 = "18nrwzn61kph2y6gxwfz9ms68rfvr9d4vcffsxng9p7jk9z18clk";
570 sha256 = "18nrwzn61kph2y6gxwfz9ms68rfvr9d4vcffsxng9p7jk9z18clk";
571 };
571 };
572 meta = {
572 meta = {
573 license = [ pkgs.lib.licenses.mit ];
573 license = [ pkgs.lib.licenses.mit ];
574 };
574 };
575 };
575 };
576 "pytest-cov" = super.buildPythonPackage {
576 "pytest-cov" = super.buildPythonPackage {
577 name = "pytest-cov-2.6.0";
577 name = "pytest-cov-2.6.0";
578 doCheck = false;
578 doCheck = false;
579 propagatedBuildInputs = [
579 propagatedBuildInputs = [
580 self."pytest"
580 self."pytest"
581 self."coverage"
581 self."coverage"
582 ];
582 ];
583 src = fetchurl {
583 src = fetchurl {
584 url = "https://files.pythonhosted.org/packages/d9/e2/58f90a316fbd94dd50bf5c826a23f3f5d079fb3cc448c1e9f0e3c33a3d2a/pytest-cov-2.6.0.tar.gz";
584 url = "https://files.pythonhosted.org/packages/d9/e2/58f90a316fbd94dd50bf5c826a23f3f5d079fb3cc448c1e9f0e3c33a3d2a/pytest-cov-2.6.0.tar.gz";
585 sha256 = "0qnpp9y3ygx4jk4pf5ad71fh2skbvnr6gl54m7rg5qysnx4g0q73";
585 sha256 = "0qnpp9y3ygx4jk4pf5ad71fh2skbvnr6gl54m7rg5qysnx4g0q73";
586 };
586 };
587 meta = {
587 meta = {
588 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
588 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
589 };
589 };
590 };
590 };
591 "pytest-profiling" = super.buildPythonPackage {
591 "pytest-profiling" = super.buildPythonPackage {
592 name = "pytest-profiling-1.3.0";
592 name = "pytest-profiling-1.3.0";
593 doCheck = false;
593 doCheck = false;
594 propagatedBuildInputs = [
594 propagatedBuildInputs = [
595 self."six"
595 self."six"
596 self."pytest"
596 self."pytest"
597 self."gprof2dot"
597 self."gprof2dot"
598 ];
598 ];
599 src = fetchurl {
599 src = fetchurl {
600 url = "https://files.pythonhosted.org/packages/f5/34/4626126e041a51ef50a80d0619519b18d20aef249aac25b0d0fdd47e57ee/pytest-profiling-1.3.0.tar.gz";
600 url = "https://files.pythonhosted.org/packages/f5/34/4626126e041a51ef50a80d0619519b18d20aef249aac25b0d0fdd47e57ee/pytest-profiling-1.3.0.tar.gz";
601 sha256 = "08r5afx5z22yvpmsnl91l4amsy1yxn8qsmm61mhp06mz8zjs51kb";
601 sha256 = "08r5afx5z22yvpmsnl91l4amsy1yxn8qsmm61mhp06mz8zjs51kb";
602 };
602 };
603 meta = {
603 meta = {
604 license = [ pkgs.lib.licenses.mit ];
604 license = [ pkgs.lib.licenses.mit ];
605 };
605 };
606 };
606 };
607 "pytest-runner" = super.buildPythonPackage {
607 "pytest-runner" = super.buildPythonPackage {
608 name = "pytest-runner-4.2";
608 name = "pytest-runner-4.2";
609 doCheck = false;
609 doCheck = false;
610 src = fetchurl {
610 src = fetchurl {
611 url = "https://files.pythonhosted.org/packages/9e/b7/fe6e8f87f9a756fd06722216f1b6698ccba4d269eac6329d9f0c441d0f93/pytest-runner-4.2.tar.gz";
611 url = "https://files.pythonhosted.org/packages/9e/b7/fe6e8f87f9a756fd06722216f1b6698ccba4d269eac6329d9f0c441d0f93/pytest-runner-4.2.tar.gz";
612 sha256 = "1gkpyphawxz38ni1gdq1fmwyqcg02m7ypzqvv46z06crwdxi2gyj";
612 sha256 = "1gkpyphawxz38ni1gdq1fmwyqcg02m7ypzqvv46z06crwdxi2gyj";
613 };
613 };
614 meta = {
614 meta = {
615 license = [ pkgs.lib.licenses.mit ];
615 license = [ pkgs.lib.licenses.mit ];
616 };
616 };
617 };
617 };
618 "pytest-sugar" = super.buildPythonPackage {
618 "pytest-sugar" = super.buildPythonPackage {
619 name = "pytest-sugar-0.9.1";
619 name = "pytest-sugar-0.9.1";
620 doCheck = false;
620 doCheck = false;
621 propagatedBuildInputs = [
621 propagatedBuildInputs = [
622 self."pytest"
622 self."pytest"
623 self."termcolor"
623 self."termcolor"
624 ];
624 ];
625 src = fetchurl {
625 src = fetchurl {
626 url = "https://files.pythonhosted.org/packages/3e/6a/a3f909083079d03bde11d06ab23088886bbe25f2c97fbe4bb865e2bf05bc/pytest-sugar-0.9.1.tar.gz";
626 url = "https://files.pythonhosted.org/packages/3e/6a/a3f909083079d03bde11d06ab23088886bbe25f2c97fbe4bb865e2bf05bc/pytest-sugar-0.9.1.tar.gz";
627 sha256 = "0b4av40dv30727m54v211r0nzwjp2ajkjgxix6j484qjmwpw935b";
627 sha256 = "0b4av40dv30727m54v211r0nzwjp2ajkjgxix6j484qjmwpw935b";
628 };
628 };
629 meta = {
629 meta = {
630 license = [ pkgs.lib.licenses.bsdOriginal ];
630 license = [ pkgs.lib.licenses.bsdOriginal ];
631 };
631 };
632 };
632 };
633 "pytest-timeout" = super.buildPythonPackage {
633 "pytest-timeout" = super.buildPythonPackage {
634 name = "pytest-timeout-1.3.2";
634 name = "pytest-timeout-1.3.2";
635 doCheck = false;
635 doCheck = false;
636 propagatedBuildInputs = [
636 propagatedBuildInputs = [
637 self."pytest"
637 self."pytest"
638 ];
638 ];
639 src = fetchurl {
639 src = fetchurl {
640 url = "https://files.pythonhosted.org/packages/8c/3e/1b6a319d12ae7baa3acb7c18ff2c8630a09471a0319d43535c683b4d03eb/pytest-timeout-1.3.2.tar.gz";
640 url = "https://files.pythonhosted.org/packages/8c/3e/1b6a319d12ae7baa3acb7c18ff2c8630a09471a0319d43535c683b4d03eb/pytest-timeout-1.3.2.tar.gz";
641 sha256 = "09wnmzvnls2mnsdz7x3c3sk2zdp6jl4dryvyj5i8hqz16q2zq5qi";
641 sha256 = "09wnmzvnls2mnsdz7x3c3sk2zdp6jl4dryvyj5i8hqz16q2zq5qi";
642 };
642 };
643 meta = {
643 meta = {
644 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
644 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
645 };
645 };
646 };
646 };
647 "repoze.lru" = super.buildPythonPackage {
647 "repoze.lru" = super.buildPythonPackage {
648 name = "repoze.lru-0.7";
648 name = "repoze.lru-0.7";
649 doCheck = false;
649 doCheck = false;
650 src = fetchurl {
650 src = fetchurl {
651 url = "https://files.pythonhosted.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
651 url = "https://files.pythonhosted.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
652 sha256 = "0xzz1aw2smy8hdszrq8yhnklx6w1r1mf55061kalw3iq35gafa84";
652 sha256 = "0xzz1aw2smy8hdszrq8yhnklx6w1r1mf55061kalw3iq35gafa84";
653 };
653 };
654 meta = {
654 meta = {
655 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
655 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
656 };
656 };
657 };
657 };
658 "rhodecode-vcsserver" = super.buildPythonPackage {
658 "rhodecode-vcsserver" = super.buildPythonPackage {
659 name = "rhodecode-vcsserver-4.14.1";
659 name = "rhodecode-vcsserver-4.15.0";
660 buildInputs = [
660 buildInputs = [
661 self."pytest"
661 self."pytest"
662 self."py"
662 self."py"
663 self."pytest-cov"
663 self."pytest-cov"
664 self."pytest-sugar"
664 self."pytest-sugar"
665 self."pytest-runner"
665 self."pytest-runner"
666 self."pytest-profiling"
666 self."pytest-profiling"
667 self."pytest-timeout"
667 self."pytest-timeout"
668 self."gprof2dot"
668 self."gprof2dot"
669 self."mock"
669 self."mock"
670 self."webtest"
670 self."webtest"
671 self."cov-core"
671 self."cov-core"
672 self."coverage"
672 self."coverage"
673 self."configobj"
673 self."configobj"
674 ];
674 ];
675 doCheck = true;
675 doCheck = true;
676 propagatedBuildInputs = [
676 propagatedBuildInputs = [
677 self."configobj"
677 self."configobj"
678 self."atomicwrites"
678 self."atomicwrites"
679 self."attrs"
679 self."attrs"
680 self."dogpile.cache"
680 self."dogpile.cache"
681 self."dogpile.core"
681 self."dogpile.core"
682 self."decorator"
682 self."decorator"
683 self."dulwich"
683 self."dulwich"
684 self."hgsubversion"
684 self."hgsubversion"
685 self."hg-evolve"
685 self."hg-evolve"
686 self."mako"
686 self."mako"
687 self."markupsafe"
687 self."markupsafe"
688 self."mercurial"
688 self."mercurial"
689 self."msgpack-python"
689 self."msgpack-python"
690 self."pastedeploy"
690 self."pastedeploy"
691 self."psutil"
691 self."psutil"
692 self."pyramid"
692 self."pyramid"
693 self."pyramid-mako"
693 self."pyramid-mako"
694 self."pygments"
694 self."pygments"
695 self."pathlib2"
695 self."pathlib2"
696 self."repoze.lru"
696 self."repoze.lru"
697 self."simplejson"
697 self."simplejson"
698 self."subprocess32"
698 self."subprocess32"
699 self."subvertpy"
699 self."subvertpy"
700 self."six"
700 self."six"
701 self."translationstring"
701 self."translationstring"
702 self."webob"
702 self."webob"
703 self."zope.deprecation"
703 self."zope.deprecation"
704 self."zope.interface"
704 self."zope.interface"
705 self."gevent"
705 self."gevent"
706 self."greenlet"
706 self."greenlet"
707 self."gunicorn"
707 self."gunicorn"
708 self."waitress"
708 self."waitress"
709 self."setproctitle"
709 self."setproctitle"
710 self."ipdb"
710 self."ipdb"
711 self."ipython"
711 self."ipython"
712 self."pytest"
712 self."pytest"
713 self."py"
713 self."py"
714 self."pytest-cov"
714 self."pytest-cov"
715 self."pytest-sugar"
715 self."pytest-sugar"
716 self."pytest-runner"
716 self."pytest-runner"
717 self."pytest-profiling"
717 self."pytest-profiling"
718 self."pytest-timeout"
718 self."pytest-timeout"
719 self."gprof2dot"
719 self."gprof2dot"
720 self."mock"
720 self."mock"
721 self."webtest"
721 self."webtest"
722 self."cov-core"
722 self."cov-core"
723 self."coverage"
723 self."coverage"
724 ];
724 ];
725 src = ./.;
725 src = ./.;
726 meta = {
726 meta = {
727 license = [ { fullName = "GPL V3"; } { fullName = "GNU General Public License v3 or later (GPLv3+)"; } ];
727 license = [ { fullName = "GPL V3"; } { fullName = "GNU General Public License v3 or later (GPLv3+)"; } ];
728 };
728 };
729 };
729 };
730 "scandir" = super.buildPythonPackage {
730 "scandir" = super.buildPythonPackage {
731 name = "scandir-1.9.0";
731 name = "scandir-1.9.0";
732 doCheck = false;
732 doCheck = false;
733 src = fetchurl {
733 src = fetchurl {
734 url = "https://files.pythonhosted.org/packages/16/2a/557af1181e6b4e30254d5a6163b18f5053791ca66e251e77ab08887e8fe3/scandir-1.9.0.tar.gz";
734 url = "https://files.pythonhosted.org/packages/16/2a/557af1181e6b4e30254d5a6163b18f5053791ca66e251e77ab08887e8fe3/scandir-1.9.0.tar.gz";
735 sha256 = "0r3hvf1a9jm1rkqgx40gxkmccknkaiqjavs8lccgq9s8khh5x5s4";
735 sha256 = "0r3hvf1a9jm1rkqgx40gxkmccknkaiqjavs8lccgq9s8khh5x5s4";
736 };
736 };
737 meta = {
737 meta = {
738 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
738 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
739 };
739 };
740 };
740 };
741 "setproctitle" = super.buildPythonPackage {
741 "setproctitle" = super.buildPythonPackage {
742 name = "setproctitle-1.1.10";
742 name = "setproctitle-1.1.10";
743 doCheck = false;
743 doCheck = false;
744 src = fetchurl {
744 src = fetchurl {
745 url = "https://files.pythonhosted.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
745 url = "https://files.pythonhosted.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
746 sha256 = "163kplw9dcrw0lffq1bvli5yws3rngpnvrxrzdw89pbphjjvg0v2";
746 sha256 = "163kplw9dcrw0lffq1bvli5yws3rngpnvrxrzdw89pbphjjvg0v2";
747 };
747 };
748 meta = {
748 meta = {
749 license = [ pkgs.lib.licenses.bsdOriginal ];
749 license = [ pkgs.lib.licenses.bsdOriginal ];
750 };
750 };
751 };
751 };
752 "setuptools" = super.buildPythonPackage {
752 "setuptools" = super.buildPythonPackage {
753 name = "setuptools-40.4.3";
753 name = "setuptools-40.6.2";
754 doCheck = false;
754 doCheck = false;
755 src = fetchurl {
755 src = fetchurl {
756 url = "https://files.pythonhosted.org/packages/6e/9c/6a003320b00ef237f94aa74e4ad66c57a7618f6c79d67527136e2544b728/setuptools-40.4.3.zip";
756 url = "https://files.pythonhosted.org/packages/b0/d1/8acb42f391cba52e35b131e442e80deffbb8d0676b93261d761b1f0ef8fb/setuptools-40.6.2.zip";
757 sha256 = "058v6zns4634n4al2nmmvp15j8nrgwn8wjrbdks47wk3vm05gg5c";
757 sha256 = "0r2c5hapirlzm34h7pl1lgkm6gk7bcrlrdj28qgsvaqg3f74vfw6";
758 };
758 };
759 meta = {
759 meta = {
760 license = [ pkgs.lib.licenses.mit ];
760 license = [ pkgs.lib.licenses.mit ];
761 };
761 };
762 };
762 };
763 "simplegeneric" = super.buildPythonPackage {
763 "simplegeneric" = super.buildPythonPackage {
764 name = "simplegeneric-0.8.1";
764 name = "simplegeneric-0.8.1";
765 doCheck = false;
765 doCheck = false;
766 src = fetchurl {
766 src = fetchurl {
767 url = "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
767 url = "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
768 sha256 = "0wwi1c6md4vkbcsfsf8dklf3vr4mcdj4mpxkanwgb6jb1432x5yw";
768 sha256 = "0wwi1c6md4vkbcsfsf8dklf3vr4mcdj4mpxkanwgb6jb1432x5yw";
769 };
769 };
770 meta = {
770 meta = {
771 license = [ pkgs.lib.licenses.zpl21 ];
771 license = [ pkgs.lib.licenses.zpl21 ];
772 };
772 };
773 };
773 };
774 "simplejson" = super.buildPythonPackage {
774 "simplejson" = super.buildPythonPackage {
775 name = "simplejson-3.11.1";
775 name = "simplejson-3.11.1";
776 doCheck = false;
776 doCheck = false;
777 src = fetchurl {
777 src = fetchurl {
778 url = "https://files.pythonhosted.org/packages/08/48/c97b668d6da7d7bebe7ea1817a6f76394b0ec959cb04214ca833c34359df/simplejson-3.11.1.tar.gz";
778 url = "https://files.pythonhosted.org/packages/08/48/c97b668d6da7d7bebe7ea1817a6f76394b0ec959cb04214ca833c34359df/simplejson-3.11.1.tar.gz";
779 sha256 = "1rr58dppsq73p0qcd9bsw066cdd3v63sqv7j6sqni8frvm4jv8h1";
779 sha256 = "1rr58dppsq73p0qcd9bsw066cdd3v63sqv7j6sqni8frvm4jv8h1";
780 };
780 };
781 meta = {
781 meta = {
782 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
782 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
783 };
783 };
784 };
784 };
785 "six" = super.buildPythonPackage {
785 "six" = super.buildPythonPackage {
786 name = "six-1.11.0";
786 name = "six-1.11.0";
787 doCheck = false;
787 doCheck = false;
788 src = fetchurl {
788 src = fetchurl {
789 url = "https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
789 url = "https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
790 sha256 = "1scqzwc51c875z23phj48gircqjgnn3af8zy2izjwmnlxrxsgs3h";
790 sha256 = "1scqzwc51c875z23phj48gircqjgnn3af8zy2izjwmnlxrxsgs3h";
791 };
791 };
792 meta = {
792 meta = {
793 license = [ pkgs.lib.licenses.mit ];
793 license = [ pkgs.lib.licenses.mit ];
794 };
794 };
795 };
795 };
796 "subprocess32" = super.buildPythonPackage {
796 "subprocess32" = super.buildPythonPackage {
797 name = "subprocess32-3.5.2";
797 name = "subprocess32-3.5.2";
798 doCheck = false;
798 doCheck = false;
799 src = fetchurl {
799 src = fetchurl {
800 url = "https://files.pythonhosted.org/packages/c3/5f/7117737fc7114061837a4f51670d863dd7f7f9c762a6546fa8a0dcfe61c8/subprocess32-3.5.2.tar.gz";
800 url = "https://files.pythonhosted.org/packages/c3/5f/7117737fc7114061837a4f51670d863dd7f7f9c762a6546fa8a0dcfe61c8/subprocess32-3.5.2.tar.gz";
801 sha256 = "11v62shwmdys48g7ncs3a8jwwnkcl8d4zcwy6dk73z1zy2f9hazb";
801 sha256 = "11v62shwmdys48g7ncs3a8jwwnkcl8d4zcwy6dk73z1zy2f9hazb";
802 };
802 };
803 meta = {
803 meta = {
804 license = [ pkgs.lib.licenses.psfl ];
804 license = [ pkgs.lib.licenses.psfl ];
805 };
805 };
806 };
806 };
807 "subvertpy" = super.buildPythonPackage {
807 "subvertpy" = super.buildPythonPackage {
808 name = "subvertpy-0.10.1";
808 name = "subvertpy-0.10.1";
809 doCheck = false;
809 doCheck = false;
810 src = fetchurl {
810 src = fetchurl {
811 url = "https://files.pythonhosted.org/packages/9d/76/99fa82affce75f5ac0f7dbe513796c3f37311ace0c68e1b063683b4f9b99/subvertpy-0.10.1.tar.gz";
811 url = "https://files.pythonhosted.org/packages/9d/76/99fa82affce75f5ac0f7dbe513796c3f37311ace0c68e1b063683b4f9b99/subvertpy-0.10.1.tar.gz";
812 sha256 = "061ncy9wjz3zyv527avcrdyk0xygyssyy7p1644nhzhwp8zpybij";
812 sha256 = "061ncy9wjz3zyv527avcrdyk0xygyssyy7p1644nhzhwp8zpybij";
813 };
813 };
814 meta = {
814 meta = {
815 license = [ pkgs.lib.licenses.lgpl21Plus pkgs.lib.licenses.gpl2Plus ];
815 license = [ pkgs.lib.licenses.lgpl21Plus pkgs.lib.licenses.gpl2Plus ];
816 };
816 };
817 };
817 };
818 "termcolor" = super.buildPythonPackage {
818 "termcolor" = super.buildPythonPackage {
819 name = "termcolor-1.1.0";
819 name = "termcolor-1.1.0";
820 doCheck = false;
820 doCheck = false;
821 src = fetchurl {
821 src = fetchurl {
822 url = "https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
822 url = "https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
823 sha256 = "0fv1vq14rpqwgazxg4981904lfyp84mnammw7y046491cv76jv8x";
823 sha256 = "0fv1vq14rpqwgazxg4981904lfyp84mnammw7y046491cv76jv8x";
824 };
824 };
825 meta = {
825 meta = {
826 license = [ pkgs.lib.licenses.mit ];
826 license = [ pkgs.lib.licenses.mit ];
827 };
827 };
828 };
828 };
829 "traitlets" = super.buildPythonPackage {
829 "traitlets" = super.buildPythonPackage {
830 name = "traitlets-4.3.2";
830 name = "traitlets-4.3.2";
831 doCheck = false;
831 doCheck = false;
832 propagatedBuildInputs = [
832 propagatedBuildInputs = [
833 self."ipython-genutils"
833 self."ipython-genutils"
834 self."six"
834 self."six"
835 self."decorator"
835 self."decorator"
836 self."enum34"
836 self."enum34"
837 ];
837 ];
838 src = fetchurl {
838 src = fetchurl {
839 url = "https://files.pythonhosted.org/packages/a5/98/7f5ef2fe9e9e071813aaf9cb91d1a732e0a68b6c44a32b38cb8e14c3f069/traitlets-4.3.2.tar.gz";
839 url = "https://files.pythonhosted.org/packages/a5/98/7f5ef2fe9e9e071813aaf9cb91d1a732e0a68b6c44a32b38cb8e14c3f069/traitlets-4.3.2.tar.gz";
840 sha256 = "0dbq7sx26xqz5ixs711k5nc88p8a0nqyz6162pwks5dpcz9d4jww";
840 sha256 = "0dbq7sx26xqz5ixs711k5nc88p8a0nqyz6162pwks5dpcz9d4jww";
841 };
841 };
842 meta = {
842 meta = {
843 license = [ pkgs.lib.licenses.bsdOriginal ];
843 license = [ pkgs.lib.licenses.bsdOriginal ];
844 };
844 };
845 };
845 };
846 "translationstring" = super.buildPythonPackage {
846 "translationstring" = super.buildPythonPackage {
847 name = "translationstring-1.3";
847 name = "translationstring-1.3";
848 doCheck = false;
848 doCheck = false;
849 src = fetchurl {
849 src = fetchurl {
850 url = "https://files.pythonhosted.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
850 url = "https://files.pythonhosted.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
851 sha256 = "0bdpcnd9pv0131dl08h4zbcwmgc45lyvq3pa224xwan5b3x4rr2f";
851 sha256 = "0bdpcnd9pv0131dl08h4zbcwmgc45lyvq3pa224xwan5b3x4rr2f";
852 };
852 };
853 meta = {
853 meta = {
854 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
854 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
855 };
855 };
856 };
856 };
857 "venusian" = super.buildPythonPackage {
857 "venusian" = super.buildPythonPackage {
858 name = "venusian-1.1.0";
858 name = "venusian-1.1.0";
859 doCheck = false;
859 doCheck = false;
860 src = fetchurl {
860 src = fetchurl {
861 url = "https://files.pythonhosted.org/packages/38/24/b4b470ab9e0a2e2e9b9030c7735828c8934b4c6b45befd1bb713ec2aeb2d/venusian-1.1.0.tar.gz";
861 url = "https://files.pythonhosted.org/packages/38/24/b4b470ab9e0a2e2e9b9030c7735828c8934b4c6b45befd1bb713ec2aeb2d/venusian-1.1.0.tar.gz";
862 sha256 = "0zapz131686qm0gazwy8bh11vr57pr89jbwbl50s528sqy9f80lr";
862 sha256 = "0zapz131686qm0gazwy8bh11vr57pr89jbwbl50s528sqy9f80lr";
863 };
863 };
864 meta = {
864 meta = {
865 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
865 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
866 };
866 };
867 };
867 };
868 "waitress" = super.buildPythonPackage {
868 "waitress" = super.buildPythonPackage {
869 name = "waitress-1.1.0";
869 name = "waitress-1.1.0";
870 doCheck = false;
870 doCheck = false;
871 src = fetchurl {
871 src = fetchurl {
872 url = "https://files.pythonhosted.org/packages/3c/68/1c10dd5c556872ceebe88483b0436140048d39de83a84a06a8baa8136f4f/waitress-1.1.0.tar.gz";
872 url = "https://files.pythonhosted.org/packages/3c/68/1c10dd5c556872ceebe88483b0436140048d39de83a84a06a8baa8136f4f/waitress-1.1.0.tar.gz";
873 sha256 = "1a85gyji0kajc3p0s1pwwfm06w4wfxjkvvl4rnrz3h164kbd6g6k";
873 sha256 = "1a85gyji0kajc3p0s1pwwfm06w4wfxjkvvl4rnrz3h164kbd6g6k";
874 };
874 };
875 meta = {
875 meta = {
876 license = [ pkgs.lib.licenses.zpl21 ];
876 license = [ pkgs.lib.licenses.zpl21 ];
877 };
877 };
878 };
878 };
879 "wcwidth" = super.buildPythonPackage {
879 "wcwidth" = super.buildPythonPackage {
880 name = "wcwidth-0.1.7";
880 name = "wcwidth-0.1.7";
881 doCheck = false;
881 doCheck = false;
882 src = fetchurl {
882 src = fetchurl {
883 url = "https://files.pythonhosted.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
883 url = "https://files.pythonhosted.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
884 sha256 = "0pn6dflzm609m4r3i8ik5ni9ijjbb5fa3vg1n7hn6vkd49r77wrx";
884 sha256 = "0pn6dflzm609m4r3i8ik5ni9ijjbb5fa3vg1n7hn6vkd49r77wrx";
885 };
885 };
886 meta = {
886 meta = {
887 license = [ pkgs.lib.licenses.mit ];
887 license = [ pkgs.lib.licenses.mit ];
888 };
888 };
889 };
889 };
890 "webob" = super.buildPythonPackage {
890 "webob" = super.buildPythonPackage {
891 name = "webob-1.7.4";
891 name = "webob-1.7.4";
892 doCheck = false;
892 doCheck = false;
893 src = fetchurl {
893 src = fetchurl {
894 url = "https://files.pythonhosted.org/packages/75/34/731e23f52371852dfe7490a61644826ba7fe70fd52a377aaca0f4956ba7f/WebOb-1.7.4.tar.gz";
894 url = "https://files.pythonhosted.org/packages/75/34/731e23f52371852dfe7490a61644826ba7fe70fd52a377aaca0f4956ba7f/WebOb-1.7.4.tar.gz";
895 sha256 = "1na01ljg04z40il7vcrn8g29vaw7nvg1xvhk64cr4jys5wcay44d";
895 sha256 = "1na01ljg04z40il7vcrn8g29vaw7nvg1xvhk64cr4jys5wcay44d";
896 };
896 };
897 meta = {
897 meta = {
898 license = [ pkgs.lib.licenses.mit ];
898 license = [ pkgs.lib.licenses.mit ];
899 };
899 };
900 };
900 };
901 "webtest" = super.buildPythonPackage {
901 "webtest" = super.buildPythonPackage {
902 name = "webtest-2.0.29";
902 name = "webtest-2.0.29";
903 doCheck = false;
903 doCheck = false;
904 propagatedBuildInputs = [
904 propagatedBuildInputs = [
905 self."six"
905 self."six"
906 self."webob"
906 self."webob"
907 self."waitress"
907 self."waitress"
908 self."beautifulsoup4"
908 self."beautifulsoup4"
909 ];
909 ];
910 src = fetchurl {
910 src = fetchurl {
911 url = "https://files.pythonhosted.org/packages/94/de/8f94738be649997da99c47b104aa3c3984ecec51a1d8153ed09638253d56/WebTest-2.0.29.tar.gz";
911 url = "https://files.pythonhosted.org/packages/94/de/8f94738be649997da99c47b104aa3c3984ecec51a1d8153ed09638253d56/WebTest-2.0.29.tar.gz";
912 sha256 = "0bcj1ica5lnmj5zbvk46x28kgphcsgh7sfnwjmn0cr94mhawrg6v";
912 sha256 = "0bcj1ica5lnmj5zbvk46x28kgphcsgh7sfnwjmn0cr94mhawrg6v";
913 };
913 };
914 meta = {
914 meta = {
915 license = [ pkgs.lib.licenses.mit ];
915 license = [ pkgs.lib.licenses.mit ];
916 };
916 };
917 };
917 };
918 "zope.deprecation" = super.buildPythonPackage {
918 "zope.deprecation" = super.buildPythonPackage {
919 name = "zope.deprecation-4.3.0";
919 name = "zope.deprecation-4.3.0";
920 doCheck = false;
920 doCheck = false;
921 propagatedBuildInputs = [
921 propagatedBuildInputs = [
922 self."setuptools"
922 self."setuptools"
923 ];
923 ];
924 src = fetchurl {
924 src = fetchurl {
925 url = "https://files.pythonhosted.org/packages/a1/18/2dc5e6bfe64fdc3b79411b67464c55bb0b43b127051a20f7f492ab767758/zope.deprecation-4.3.0.tar.gz";
925 url = "https://files.pythonhosted.org/packages/a1/18/2dc5e6bfe64fdc3b79411b67464c55bb0b43b127051a20f7f492ab767758/zope.deprecation-4.3.0.tar.gz";
926 sha256 = "095jas41wbxgmw95kwdxqhbc3bgihw2hzj9b3qpdg85apcsf2lkx";
926 sha256 = "095jas41wbxgmw95kwdxqhbc3bgihw2hzj9b3qpdg85apcsf2lkx";
927 };
927 };
928 meta = {
928 meta = {
929 license = [ pkgs.lib.licenses.zpl21 ];
929 license = [ pkgs.lib.licenses.zpl21 ];
930 };
930 };
931 };
931 };
932 "zope.interface" = super.buildPythonPackage {
932 "zope.interface" = super.buildPythonPackage {
933 name = "zope.interface-4.5.0";
933 name = "zope.interface-4.5.0";
934 doCheck = false;
934 doCheck = false;
935 propagatedBuildInputs = [
935 propagatedBuildInputs = [
936 self."setuptools"
936 self."setuptools"
937 ];
937 ];
938 src = fetchurl {
938 src = fetchurl {
939 url = "https://files.pythonhosted.org/packages/ac/8a/657532df378c2cd2a1fe6b12be3b4097521570769d4852ec02c24bd3594e/zope.interface-4.5.0.tar.gz";
939 url = "https://files.pythonhosted.org/packages/ac/8a/657532df378c2cd2a1fe6b12be3b4097521570769d4852ec02c24bd3594e/zope.interface-4.5.0.tar.gz";
940 sha256 = "0k67m60ij06wkg82n15qgyn96waf4pmrkhv0njpkfzpmv5q89hsp";
940 sha256 = "0k67m60ij06wkg82n15qgyn96waf4pmrkhv0njpkfzpmv5q89hsp";
941 };
941 };
942 meta = {
942 meta = {
943 license = [ pkgs.lib.licenses.zpl21 ];
943 license = [ pkgs.lib.licenses.zpl21 ];
944 };
944 };
945 };
945 };
946
946
947 ### Test requirements
947 ### Test requirements
948
948
949
949
950 }
950 }
@@ -1,48 +1,48 b''
1 ## dependencies
1 ## dependencies
2
2
3 # our custom configobj
3 # our custom configobj
4 https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c#egg=configobj==5.0.6
4 https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c#egg=configobj==5.0.6
5 atomicwrites==1.2.1
5 atomicwrites==1.2.1
6 attrs==18.2.0
6 attrs==18.2.0
7 dogpile.cache==0.6.7
7 dogpile.cache==0.6.7
8 dogpile.core==0.4.1
8 dogpile.core==0.4.1
9 decorator==4.1.2
9 decorator==4.1.2
10 dulwich==0.13.0
10 dulwich==0.13.0
11 hgsubversion==1.9.2
11 hgsubversion==1.9.3
12 hg-evolve==8.0.1
12 hg-evolve==8.0.1
13 mako==1.0.7
13 mako==1.0.7
14 markupsafe==1.0.0
14 markupsafe==1.0.0
15 mercurial==4.6.2
15 mercurial==4.6.2
16 msgpack-python==0.5.6
16 msgpack-python==0.5.6
17
17
18 pastedeploy==1.5.2
18 pastedeploy==1.5.2
19 psutil==5.4.7
19 psutil==5.4.7
20 pyramid==1.9.2
20 pyramid==1.9.2
21 pyramid-mako==1.0.2
21 pyramid-mako==1.0.2
22
22
23 pygments==2.2.0
23 pygments==2.3.0
24 pathlib2==2.3.2
24 pathlib2==2.3.2
25 repoze.lru==0.7
25 repoze.lru==0.7
26 simplejson==3.11.1
26 simplejson==3.11.1
27 subprocess32==3.5.2
27 subprocess32==3.5.2
28 subvertpy==0.10.1
28 subvertpy==0.10.1
29
29
30 six==1.11.0
30 six==1.11.0
31 translationstring==1.3
31 translationstring==1.3
32 webob==1.7.4
32 webob==1.7.4
33 zope.deprecation==4.3.0
33 zope.deprecation==4.3.0
34 zope.interface==4.5.0
34 zope.interface==4.5.0
35
35
36 ## http servers
36 ## http servers
37 gevent==1.3.6
37 gevent==1.3.7
38 greenlet==0.4.15
38 greenlet==0.4.15
39 gunicorn==19.9.0
39 gunicorn==19.9.0
40 waitress==1.1.0
40 waitress==1.1.0
41 setproctitle==1.1.10
41 setproctitle==1.1.10
42
42
43 ## debug
43 ## debug
44 ipdb==0.11.0
44 ipdb==0.11.0
45 ipython==5.1.0
45 ipython==5.1.0
46
46
47 ## test related requirements
47 ## test related requirements
48 -r requirements_test.txt
48 -r requirements_test.txt
@@ -1,1 +1,1 b''
1 4.14.1 No newline at end of file
1 4.15.0 No newline at end of file
@@ -1,728 +1,719 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2018 RhodeCode GmbH
2 # Copyright (C) 2014-2018 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 import collections
17 import collections
18 import logging
18 import logging
19 import os
19 import os
20 import posixpath as vcspath
20 import posixpath as vcspath
21 import re
21 import re
22 import stat
22 import stat
23 import traceback
23 import traceback
24 import urllib
24 import urllib
25 import urllib2
25 import urllib2
26 from functools import wraps
26 from functools import wraps
27
27
28 from dulwich import index, objects
28 from dulwich import index, objects
29 from dulwich.client import HttpGitClient, LocalGitClient
29 from dulwich.client import HttpGitClient, LocalGitClient
30 from dulwich.errors import (
30 from dulwich.errors import (
31 NotGitRepository, ChecksumMismatch, WrongObjectException,
31 NotGitRepository, ChecksumMismatch, WrongObjectException,
32 MissingCommitError, ObjectMissing, HangupException,
32 MissingCommitError, ObjectMissing, HangupException,
33 UnexpectedCommandError)
33 UnexpectedCommandError)
34 from dulwich.repo import Repo as DulwichRepo, Tag
34 from dulwich.repo import Repo as DulwichRepo, Tag
35 from dulwich.server import update_server_info
35 from dulwich.server import update_server_info
36
36
37 from vcsserver import exceptions, settings, subprocessio
37 from vcsserver import exceptions, settings, subprocessio
38 from vcsserver.utils import safe_str
38 from vcsserver.utils import safe_str
39 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
39 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
40 from vcsserver.hgcompat import (
40 from vcsserver.hgcompat import (
41 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
41 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler)
42 from vcsserver.git_lfs.lib import LFSOidStore
42 from vcsserver.git_lfs.lib import LFSOidStore
43
43
44 DIR_STAT = stat.S_IFDIR
44 DIR_STAT = stat.S_IFDIR
45 FILE_MODE = stat.S_IFMT
45 FILE_MODE = stat.S_IFMT
46 GIT_LINK = objects.S_IFGITLINK
46 GIT_LINK = objects.S_IFGITLINK
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 def reraise_safe_exceptions(func):
51 def reraise_safe_exceptions(func):
52 """Converts Dulwich exceptions to something neutral."""
52 """Converts Dulwich exceptions to something neutral."""
53 @wraps(func)
53 @wraps(func)
54 def wrapper(*args, **kwargs):
54 def wrapper(*args, **kwargs):
55 try:
55 try:
56 return func(*args, **kwargs)
56 return func(*args, **kwargs)
57 except (ChecksumMismatch, WrongObjectException, MissingCommitError,
57 except (ChecksumMismatch, WrongObjectException, MissingCommitError,
58 ObjectMissing) as e:
58 ObjectMissing) as e:
59 exc = exceptions.LookupException(e)
59 exc = exceptions.LookupException(e)
60 raise exc(e)
60 raise exc(e)
61 except (HangupException, UnexpectedCommandError) as e:
61 except (HangupException, UnexpectedCommandError) as e:
62 exc = exceptions.VcsException(e)
62 exc = exceptions.VcsException(e)
63 raise exc(e)
63 raise exc(e)
64 except Exception as e:
64 except Exception as e:
65 # NOTE(marcink): becuase of how dulwich handles some exceptions
65 # NOTE(marcink): becuase of how dulwich handles some exceptions
66 # (KeyError on empty repos), we cannot track this and catch all
66 # (KeyError on empty repos), we cannot track this and catch all
67 # exceptions, it's an exceptions from other handlers
67 # exceptions, it's an exceptions from other handlers
68 #if not hasattr(e, '_vcs_kind'):
68 #if not hasattr(e, '_vcs_kind'):
69 #log.exception("Unhandled exception in git remote call")
69 #log.exception("Unhandled exception in git remote call")
70 #raise_from_original(exceptions.UnhandledException)
70 #raise_from_original(exceptions.UnhandledException)
71 raise
71 raise
72 return wrapper
72 return wrapper
73
73
74
74
75 class Repo(DulwichRepo):
75 class Repo(DulwichRepo):
76 """
76 """
77 A wrapper for dulwich Repo class.
77 A wrapper for dulwich Repo class.
78
78
79 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
79 Since dulwich is sometimes keeping .idx file descriptors open, it leads to
80 "Too many open files" error. We need to close all opened file descriptors
80 "Too many open files" error. We need to close all opened file descriptors
81 once the repo object is destroyed.
81 once the repo object is destroyed.
82
82
83 TODO: mikhail: please check if we need this wrapper after updating dulwich
83 TODO: mikhail: please check if we need this wrapper after updating dulwich
84 to 0.12.0 +
84 to 0.12.0 +
85 """
85 """
86 def __del__(self):
86 def __del__(self):
87 if hasattr(self, 'object_store'):
87 if hasattr(self, 'object_store'):
88 self.close()
88 self.close()
89
89
90
90
91 class GitFactory(RepoFactory):
91 class GitFactory(RepoFactory):
92 repo_type = 'git'
92 repo_type = 'git'
93
93
94 def _create_repo(self, wire, create):
94 def _create_repo(self, wire, create):
95 repo_path = str_to_dulwich(wire['path'])
95 repo_path = str_to_dulwich(wire['path'])
96 return Repo(repo_path)
96 return Repo(repo_path)
97
97
98
98
99 class GitRemote(object):
99 class GitRemote(object):
100
100
101 def __init__(self, factory):
101 def __init__(self, factory):
102 self._factory = factory
102 self._factory = factory
103 self.peeled_ref_marker = '^{}'
103 self.peeled_ref_marker = '^{}'
104 self._bulk_methods = {
104 self._bulk_methods = {
105 "author": self.commit_attribute,
105 "author": self.commit_attribute,
106 "date": self.get_object_attrs,
106 "date": self.get_object_attrs,
107 "message": self.commit_attribute,
107 "message": self.commit_attribute,
108 "parents": self.commit_attribute,
108 "parents": self.commit_attribute,
109 "_commit": self.revision,
109 "_commit": self.revision,
110 }
110 }
111
111
112 def _wire_to_config(self, wire):
112 def _wire_to_config(self, wire):
113 if 'config' in wire:
113 if 'config' in wire:
114 return dict([(x[0] + '_' + x[1], x[2]) for x in wire['config']])
114 return dict([(x[0] + '_' + x[1], x[2]) for x in wire['config']])
115 return {}
115 return {}
116
116
117 def _assign_ref(self, wire, ref, commit_id):
117 def _assign_ref(self, wire, ref, commit_id):
118 repo = self._factory.repo(wire)
118 repo = self._factory.repo(wire)
119 repo[ref] = commit_id
119 repo[ref] = commit_id
120
120
121 @reraise_safe_exceptions
121 @reraise_safe_exceptions
122 def add_object(self, wire, content):
122 def add_object(self, wire, content):
123 repo = self._factory.repo(wire)
123 repo = self._factory.repo(wire)
124 blob = objects.Blob()
124 blob = objects.Blob()
125 blob.set_raw_string(content)
125 blob.set_raw_string(content)
126 repo.object_store.add_object(blob)
126 repo.object_store.add_object(blob)
127 return blob.id
127 return blob.id
128
128
129 @reraise_safe_exceptions
129 @reraise_safe_exceptions
130 def assert_correct_path(self, wire):
130 def assert_correct_path(self, wire):
131 path = wire.get('path')
131 path = wire.get('path')
132 try:
132 try:
133 self._factory.repo(wire)
133 self._factory.repo(wire)
134 except NotGitRepository as e:
134 except NotGitRepository as e:
135 tb = traceback.format_exc()
135 tb = traceback.format_exc()
136 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
136 log.debug("Invalid Git path `%s`, tb: %s", path, tb)
137 return False
137 return False
138
138
139 return True
139 return True
140
140
141 @reraise_safe_exceptions
141 @reraise_safe_exceptions
142 def bare(self, wire):
142 def bare(self, wire):
143 repo = self._factory.repo(wire)
143 repo = self._factory.repo(wire)
144 return repo.bare
144 return repo.bare
145
145
146 @reraise_safe_exceptions
146 @reraise_safe_exceptions
147 def blob_as_pretty_string(self, wire, sha):
147 def blob_as_pretty_string(self, wire, sha):
148 repo = self._factory.repo(wire)
148 repo = self._factory.repo(wire)
149 return repo[sha].as_pretty_string()
149 return repo[sha].as_pretty_string()
150
150
151 @reraise_safe_exceptions
151 @reraise_safe_exceptions
152 def blob_raw_length(self, wire, sha):
152 def blob_raw_length(self, wire, sha):
153 repo = self._factory.repo(wire)
153 repo = self._factory.repo(wire)
154 blob = repo[sha]
154 blob = repo[sha]
155 return blob.raw_length()
155 return blob.raw_length()
156
156
157 def _parse_lfs_pointer(self, raw_content):
157 def _parse_lfs_pointer(self, raw_content):
158
158
159 spec_string = 'version https://git-lfs.github.com/spec'
159 spec_string = 'version https://git-lfs.github.com/spec'
160 if raw_content and raw_content.startswith(spec_string):
160 if raw_content and raw_content.startswith(spec_string):
161 pattern = re.compile(r"""
161 pattern = re.compile(r"""
162 (?:\n)?
162 (?:\n)?
163 ^version[ ]https://git-lfs\.github\.com/spec/(?P<spec_ver>v\d+)\n
163 ^version[ ]https://git-lfs\.github\.com/spec/(?P<spec_ver>v\d+)\n
164 ^oid[ ] sha256:(?P<oid_hash>[0-9a-f]{64})\n
164 ^oid[ ] sha256:(?P<oid_hash>[0-9a-f]{64})\n
165 ^size[ ](?P<oid_size>[0-9]+)\n
165 ^size[ ](?P<oid_size>[0-9]+)\n
166 (?:\n)?
166 (?:\n)?
167 """, re.VERBOSE | re.MULTILINE)
167 """, re.VERBOSE | re.MULTILINE)
168 match = pattern.match(raw_content)
168 match = pattern.match(raw_content)
169 if match:
169 if match:
170 return match.groupdict()
170 return match.groupdict()
171
171
172 return {}
172 return {}
173
173
174 @reraise_safe_exceptions
174 @reraise_safe_exceptions
175 def is_large_file(self, wire, sha):
175 def is_large_file(self, wire, sha):
176 repo = self._factory.repo(wire)
176 repo = self._factory.repo(wire)
177 blob = repo[sha]
177 blob = repo[sha]
178 return self._parse_lfs_pointer(blob.as_raw_string())
178 return self._parse_lfs_pointer(blob.as_raw_string())
179
179
180 @reraise_safe_exceptions
180 @reraise_safe_exceptions
181 def in_largefiles_store(self, wire, oid):
181 def in_largefiles_store(self, wire, oid):
182 repo = self._factory.repo(wire)
182 repo = self._factory.repo(wire)
183 conf = self._wire_to_config(wire)
183 conf = self._wire_to_config(wire)
184
184
185 store_location = conf.get('vcs_git_lfs_store_location')
185 store_location = conf.get('vcs_git_lfs_store_location')
186 if store_location:
186 if store_location:
187 repo_name = repo.path
187 repo_name = repo.path
188 store = LFSOidStore(
188 store = LFSOidStore(
189 oid=oid, repo=repo_name, store_location=store_location)
189 oid=oid, repo=repo_name, store_location=store_location)
190 return store.has_oid()
190 return store.has_oid()
191
191
192 return False
192 return False
193
193
194 @reraise_safe_exceptions
194 @reraise_safe_exceptions
195 def store_path(self, wire, oid):
195 def store_path(self, wire, oid):
196 repo = self._factory.repo(wire)
196 repo = self._factory.repo(wire)
197 conf = self._wire_to_config(wire)
197 conf = self._wire_to_config(wire)
198
198
199 store_location = conf.get('vcs_git_lfs_store_location')
199 store_location = conf.get('vcs_git_lfs_store_location')
200 if store_location:
200 if store_location:
201 repo_name = repo.path
201 repo_name = repo.path
202 store = LFSOidStore(
202 store = LFSOidStore(
203 oid=oid, repo=repo_name, store_location=store_location)
203 oid=oid, repo=repo_name, store_location=store_location)
204 return store.oid_path
204 return store.oid_path
205 raise ValueError('Unable to fetch oid with path {}'.format(oid))
205 raise ValueError('Unable to fetch oid with path {}'.format(oid))
206
206
207 @reraise_safe_exceptions
207 @reraise_safe_exceptions
208 def bulk_request(self, wire, rev, pre_load):
208 def bulk_request(self, wire, rev, pre_load):
209 result = {}
209 result = {}
210 for attr in pre_load:
210 for attr in pre_load:
211 try:
211 try:
212 method = self._bulk_methods[attr]
212 method = self._bulk_methods[attr]
213 args = [wire, rev]
213 args = [wire, rev]
214 if attr == "date":
214 if attr == "date":
215 args.extend(["commit_time", "commit_timezone"])
215 args.extend(["commit_time", "commit_timezone"])
216 elif attr in ["author", "message", "parents"]:
216 elif attr in ["author", "message", "parents"]:
217 args.append(attr)
217 args.append(attr)
218 result[attr] = method(*args)
218 result[attr] = method(*args)
219 except KeyError as e:
219 except KeyError as e:
220 raise exceptions.VcsException(e)(
220 raise exceptions.VcsException(e)(
221 "Unknown bulk attribute: %s" % attr)
221 "Unknown bulk attribute: %s" % attr)
222 return result
222 return result
223
223
224 def _build_opener(self, url):
224 def _build_opener(self, url):
225 handlers = []
225 handlers = []
226 url_obj = url_parser(url)
226 url_obj = url_parser(url)
227 _, authinfo = url_obj.authinfo()
227 _, authinfo = url_obj.authinfo()
228
228
229 if authinfo:
229 if authinfo:
230 # create a password manager
230 # create a password manager
231 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
231 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
232 passmgr.add_password(*authinfo)
232 passmgr.add_password(*authinfo)
233
233
234 handlers.extend((httpbasicauthhandler(passmgr),
234 handlers.extend((httpbasicauthhandler(passmgr),
235 httpdigestauthhandler(passmgr)))
235 httpdigestauthhandler(passmgr)))
236
236
237 return urllib2.build_opener(*handlers)
237 return urllib2.build_opener(*handlers)
238
238
239 @reraise_safe_exceptions
239 @reraise_safe_exceptions
240 def check_url(self, url, config):
240 def check_url(self, url, config):
241 url_obj = url_parser(url)
241 url_obj = url_parser(url)
242 test_uri, _ = url_obj.authinfo()
242 test_uri, _ = url_obj.authinfo()
243 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
243 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
244 url_obj.query = obfuscate_qs(url_obj.query)
244 url_obj.query = obfuscate_qs(url_obj.query)
245 cleaned_uri = str(url_obj)
245 cleaned_uri = str(url_obj)
246 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
246 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
247
247
248 if not test_uri.endswith('info/refs'):
248 if not test_uri.endswith('info/refs'):
249 test_uri = test_uri.rstrip('/') + '/info/refs'
249 test_uri = test_uri.rstrip('/') + '/info/refs'
250
250
251 o = self._build_opener(url)
251 o = self._build_opener(url)
252 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
252 o.addheaders = [('User-Agent', 'git/1.7.8.0')] # fake some git
253
253
254 q = {"service": 'git-upload-pack'}
254 q = {"service": 'git-upload-pack'}
255 qs = '?%s' % urllib.urlencode(q)
255 qs = '?%s' % urllib.urlencode(q)
256 cu = "%s%s" % (test_uri, qs)
256 cu = "%s%s" % (test_uri, qs)
257 req = urllib2.Request(cu, None, {})
257 req = urllib2.Request(cu, None, {})
258
258
259 try:
259 try:
260 log.debug("Trying to open URL %s", cleaned_uri)
260 log.debug("Trying to open URL %s", cleaned_uri)
261 resp = o.open(req)
261 resp = o.open(req)
262 if resp.code != 200:
262 if resp.code != 200:
263 raise exceptions.URLError()('Return Code is not 200')
263 raise exceptions.URLError()('Return Code is not 200')
264 except Exception as e:
264 except Exception as e:
265 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
265 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
266 # means it cannot be cloned
266 # means it cannot be cloned
267 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
267 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
268
268
269 # now detect if it's proper git repo
269 # now detect if it's proper git repo
270 gitdata = resp.read()
270 gitdata = resp.read()
271 if 'service=git-upload-pack' in gitdata:
271 if 'service=git-upload-pack' in gitdata:
272 pass
272 pass
273 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
273 elif re.findall(r'[0-9a-fA-F]{40}\s+refs', gitdata):
274 # old style git can return some other format !
274 # old style git can return some other format !
275 pass
275 pass
276 else:
276 else:
277 raise exceptions.URLError()(
277 raise exceptions.URLError()(
278 "url [%s] does not look like an git" % (cleaned_uri,))
278 "url [%s] does not look like an git" % (cleaned_uri,))
279
279
280 return True
280 return True
281
281
282 @reraise_safe_exceptions
282 @reraise_safe_exceptions
283 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
283 def clone(self, wire, url, deferred, valid_refs, update_after_clone):
284 # TODO(marcink): deprecate this method. Last i checked we don't use it anymore
284 # TODO(marcink): deprecate this method. Last i checked we don't use it anymore
285 remote_refs = self.pull(wire, url, apply_refs=False)
285 remote_refs = self.pull(wire, url, apply_refs=False)
286 repo = self._factory.repo(wire)
286 repo = self._factory.repo(wire)
287 if isinstance(valid_refs, list):
287 if isinstance(valid_refs, list):
288 valid_refs = tuple(valid_refs)
288 valid_refs = tuple(valid_refs)
289
289
290 for k in remote_refs:
290 for k in remote_refs:
291 # only parse heads/tags and skip so called deferred tags
291 # only parse heads/tags and skip so called deferred tags
292 if k.startswith(valid_refs) and not k.endswith(deferred):
292 if k.startswith(valid_refs) and not k.endswith(deferred):
293 repo[k] = remote_refs[k]
293 repo[k] = remote_refs[k]
294
294
295 if update_after_clone:
295 if update_after_clone:
296 # we want to checkout HEAD
296 # we want to checkout HEAD
297 repo["HEAD"] = remote_refs["HEAD"]
297 repo["HEAD"] = remote_refs["HEAD"]
298 index.build_index_from_tree(repo.path, repo.index_path(),
298 index.build_index_from_tree(repo.path, repo.index_path(),
299 repo.object_store, repo["HEAD"].tree)
299 repo.object_store, repo["HEAD"].tree)
300
300
301 # TODO: this is quite complex, check if that can be simplified
301 # TODO: this is quite complex, check if that can be simplified
302 @reraise_safe_exceptions
302 @reraise_safe_exceptions
303 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
303 def commit(self, wire, commit_data, branch, commit_tree, updated, removed):
304 repo = self._factory.repo(wire)
304 repo = self._factory.repo(wire)
305 object_store = repo.object_store
305 object_store = repo.object_store
306
306
307 # Create tree and populates it with blobs
307 # Create tree and populates it with blobs
308 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
308 commit_tree = commit_tree and repo[commit_tree] or objects.Tree()
309
309
310 for node in updated:
310 for node in updated:
311 # Compute subdirs if needed
311 # Compute subdirs if needed
312 dirpath, nodename = vcspath.split(node['path'])
312 dirpath, nodename = vcspath.split(node['path'])
313 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
313 dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
314 parent = commit_tree
314 parent = commit_tree
315 ancestors = [('', parent)]
315 ancestors = [('', parent)]
316
316
317 # Tries to dig for the deepest existing tree
317 # Tries to dig for the deepest existing tree
318 while dirnames:
318 while dirnames:
319 curdir = dirnames.pop(0)
319 curdir = dirnames.pop(0)
320 try:
320 try:
321 dir_id = parent[curdir][1]
321 dir_id = parent[curdir][1]
322 except KeyError:
322 except KeyError:
323 # put curdir back into dirnames and stops
323 # put curdir back into dirnames and stops
324 dirnames.insert(0, curdir)
324 dirnames.insert(0, curdir)
325 break
325 break
326 else:
326 else:
327 # If found, updates parent
327 # If found, updates parent
328 parent = repo[dir_id]
328 parent = repo[dir_id]
329 ancestors.append((curdir, parent))
329 ancestors.append((curdir, parent))
330 # Now parent is deepest existing tree and we need to create
330 # Now parent is deepest existing tree and we need to create
331 # subtrees for dirnames (in reverse order)
331 # subtrees for dirnames (in reverse order)
332 # [this only applies for nodes from added]
332 # [this only applies for nodes from added]
333 new_trees = []
333 new_trees = []
334
334
335 blob = objects.Blob.from_string(node['content'])
335 blob = objects.Blob.from_string(node['content'])
336
336
337 if dirnames:
337 if dirnames:
338 # If there are trees which should be created we need to build
338 # If there are trees which should be created we need to build
339 # them now (in reverse order)
339 # them now (in reverse order)
340 reversed_dirnames = list(reversed(dirnames))
340 reversed_dirnames = list(reversed(dirnames))
341 curtree = objects.Tree()
341 curtree = objects.Tree()
342 curtree[node['node_path']] = node['mode'], blob.id
342 curtree[node['node_path']] = node['mode'], blob.id
343 new_trees.append(curtree)
343 new_trees.append(curtree)
344 for dirname in reversed_dirnames[:-1]:
344 for dirname in reversed_dirnames[:-1]:
345 newtree = objects.Tree()
345 newtree = objects.Tree()
346 newtree[dirname] = (DIR_STAT, curtree.id)
346 newtree[dirname] = (DIR_STAT, curtree.id)
347 new_trees.append(newtree)
347 new_trees.append(newtree)
348 curtree = newtree
348 curtree = newtree
349 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
349 parent[reversed_dirnames[-1]] = (DIR_STAT, curtree.id)
350 else:
350 else:
351 parent.add(
351 parent.add(
352 name=node['node_path'], mode=node['mode'], hexsha=blob.id)
352 name=node['node_path'], mode=node['mode'], hexsha=blob.id)
353
353
354 new_trees.append(parent)
354 new_trees.append(parent)
355 # Update ancestors
355 # Update ancestors
356 reversed_ancestors = reversed(
356 reversed_ancestors = reversed(
357 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
357 [(a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])])
358 for parent, tree, path in reversed_ancestors:
358 for parent, tree, path in reversed_ancestors:
359 parent[path] = (DIR_STAT, tree.id)
359 parent[path] = (DIR_STAT, tree.id)
360 object_store.add_object(tree)
360 object_store.add_object(tree)
361
361
362 object_store.add_object(blob)
362 object_store.add_object(blob)
363 for tree in new_trees:
363 for tree in new_trees:
364 object_store.add_object(tree)
364 object_store.add_object(tree)
365
365
366 for node_path in removed:
366 for node_path in removed:
367 paths = node_path.split('/')
367 paths = node_path.split('/')
368 tree = commit_tree
368 tree = commit_tree
369 trees = [tree]
369 trees = [tree]
370 # Traverse deep into the forest...
370 # Traverse deep into the forest...
371 for path in paths:
371 for path in paths:
372 try:
372 try:
373 obj = repo[tree[path][1]]
373 obj = repo[tree[path][1]]
374 if isinstance(obj, objects.Tree):
374 if isinstance(obj, objects.Tree):
375 trees.append(obj)
375 trees.append(obj)
376 tree = obj
376 tree = obj
377 except KeyError:
377 except KeyError:
378 break
378 break
379 # Cut down the blob and all rotten trees on the way back...
379 # Cut down the blob and all rotten trees on the way back...
380 for path, tree in reversed(zip(paths, trees)):
380 for path, tree in reversed(zip(paths, trees)):
381 del tree[path]
381 del tree[path]
382 if tree:
382 if tree:
383 # This tree still has elements - don't remove it or any
383 # This tree still has elements - don't remove it or any
384 # of it's parents
384 # of it's parents
385 break
385 break
386
386
387 object_store.add_object(commit_tree)
387 object_store.add_object(commit_tree)
388
388
389 # Create commit
389 # Create commit
390 commit = objects.Commit()
390 commit = objects.Commit()
391 commit.tree = commit_tree.id
391 commit.tree = commit_tree.id
392 for k, v in commit_data.iteritems():
392 for k, v in commit_data.iteritems():
393 setattr(commit, k, v)
393 setattr(commit, k, v)
394 object_store.add_object(commit)
394 object_store.add_object(commit)
395
395
396 ref = 'refs/heads/%s' % branch
396 ref = 'refs/heads/%s' % branch
397 repo.refs[ref] = commit.id
397 repo.refs[ref] = commit.id
398
398
399 return commit.id
399 return commit.id
400
400
401 @reraise_safe_exceptions
401 @reraise_safe_exceptions
402 def pull(self, wire, url, apply_refs=True, refs=None, update_after=False):
402 def pull(self, wire, url, apply_refs=True, refs=None, update_after=False):
403 if url != 'default' and '://' not in url:
403 if url != 'default' and '://' not in url:
404 client = LocalGitClient(url)
404 client = LocalGitClient(url)
405 else:
405 else:
406 url_obj = url_parser(url)
406 url_obj = url_parser(url)
407 o = self._build_opener(url)
407 o = self._build_opener(url)
408 url, _ = url_obj.authinfo()
408 url, _ = url_obj.authinfo()
409 client = HttpGitClient(base_url=url, opener=o)
409 client = HttpGitClient(base_url=url, opener=o)
410 repo = self._factory.repo(wire)
410 repo = self._factory.repo(wire)
411
411
412 determine_wants = repo.object_store.determine_wants_all
412 determine_wants = repo.object_store.determine_wants_all
413 if refs:
413 if refs:
414 def determine_wants_requested(references):
414 def determine_wants_requested(references):
415 return [references[r] for r in references if r in refs]
415 return [references[r] for r in references if r in refs]
416 determine_wants = determine_wants_requested
416 determine_wants = determine_wants_requested
417
417
418 try:
418 try:
419 remote_refs = client.fetch(
419 remote_refs = client.fetch(
420 path=url, target=repo, determine_wants=determine_wants)
420 path=url, target=repo, determine_wants=determine_wants)
421 except NotGitRepository as e:
421 except NotGitRepository as e:
422 log.warning(
422 log.warning(
423 'Trying to fetch from "%s" failed, not a Git repository.', url)
423 'Trying to fetch from "%s" failed, not a Git repository.', url)
424 # Exception can contain unicode which we convert
424 # Exception can contain unicode which we convert
425 raise exceptions.AbortException(e)(repr(e))
425 raise exceptions.AbortException(e)(repr(e))
426
426
427 # mikhail: client.fetch() returns all the remote refs, but fetches only
427 # mikhail: client.fetch() returns all the remote refs, but fetches only
428 # refs filtered by `determine_wants` function. We need to filter result
428 # refs filtered by `determine_wants` function. We need to filter result
429 # as well
429 # as well
430 if refs:
430 if refs:
431 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
431 remote_refs = {k: remote_refs[k] for k in remote_refs if k in refs}
432
432
433 if apply_refs:
433 if apply_refs:
434 # TODO: johbo: Needs proper test coverage with a git repository
434 # TODO: johbo: Needs proper test coverage with a git repository
435 # that contains a tag object, so that we would end up with
435 # that contains a tag object, so that we would end up with
436 # a peeled ref at this point.
436 # a peeled ref at this point.
437 for k in remote_refs:
437 for k in remote_refs:
438 if k.endswith(self.peeled_ref_marker):
438 if k.endswith(self.peeled_ref_marker):
439 log.debug("Skipping peeled reference %s", k)
439 log.debug("Skipping peeled reference %s", k)
440 continue
440 continue
441 repo[k] = remote_refs[k]
441 repo[k] = remote_refs[k]
442
442
443 if refs and not update_after:
443 if refs and not update_after:
444 # mikhail: explicitly set the head to the last ref.
444 # mikhail: explicitly set the head to the last ref.
445 repo['HEAD'] = remote_refs[refs[-1]]
445 repo['HEAD'] = remote_refs[refs[-1]]
446
446
447 if update_after:
447 if update_after:
448 # we want to checkout HEAD
448 # we want to checkout HEAD
449 repo["HEAD"] = remote_refs["HEAD"]
449 repo["HEAD"] = remote_refs["HEAD"]
450 index.build_index_from_tree(repo.path, repo.index_path(),
450 index.build_index_from_tree(repo.path, repo.index_path(),
451 repo.object_store, repo["HEAD"].tree)
451 repo.object_store, repo["HEAD"].tree)
452 return remote_refs
452 return remote_refs
453
453
454 @reraise_safe_exceptions
454 @reraise_safe_exceptions
455 def sync_fetch(self, wire, url, refs=None):
455 def sync_fetch(self, wire, url, refs=None):
456 repo = self._factory.repo(wire)
456 repo = self._factory.repo(wire)
457 if refs and not isinstance(refs, (list, tuple)):
457 if refs and not isinstance(refs, (list, tuple)):
458 refs = [refs]
458 refs = [refs]
459
459
460 # get all remote refs we'll use to fetch later
460 # get all remote refs we'll use to fetch later
461 output, __ = self.run_git_command(
461 output, __ = self.run_git_command(
462 wire, ['ls-remote', url], fail_on_stderr=False,
462 wire, ['ls-remote', url], fail_on_stderr=False,
463 _copts=['-c', 'core.askpass=""'],
463 _copts=['-c', 'core.askpass=""'],
464 extra_env={'GIT_TERMINAL_PROMPT': '0'})
464 extra_env={'GIT_TERMINAL_PROMPT': '0'})
465
465
466 remote_refs = collections.OrderedDict()
466 remote_refs = collections.OrderedDict()
467 fetch_refs = []
467 fetch_refs = []
468
468
469 for ref_line in output.splitlines():
469 for ref_line in output.splitlines():
470 sha, ref = ref_line.split('\t')
470 sha, ref = ref_line.split('\t')
471 sha = sha.strip()
471 sha = sha.strip()
472 if ref in remote_refs:
472 if ref in remote_refs:
473 # duplicate, skip
473 # duplicate, skip
474 continue
474 continue
475 if ref.endswith(self.peeled_ref_marker):
475 if ref.endswith(self.peeled_ref_marker):
476 log.debug("Skipping peeled reference %s", ref)
476 log.debug("Skipping peeled reference %s", ref)
477 continue
477 continue
478 # don't sync HEAD
478 # don't sync HEAD
479 if ref in ['HEAD']:
479 if ref in ['HEAD']:
480 continue
480 continue
481
481
482 remote_refs[ref] = sha
482 remote_refs[ref] = sha
483
483
484 if refs and sha in refs:
484 if refs and sha in refs:
485 # we filter fetch using our specified refs
485 # we filter fetch using our specified refs
486 fetch_refs.append('{}:{}'.format(ref, ref))
486 fetch_refs.append('{}:{}'.format(ref, ref))
487 elif not refs:
487 elif not refs:
488 fetch_refs.append('{}:{}'.format(ref, ref))
488 fetch_refs.append('{}:{}'.format(ref, ref))
489
489
490 if fetch_refs:
490 if fetch_refs:
491 _out, _err = self.run_git_command(
491 _out, _err = self.run_git_command(
492 wire, ['fetch', url, '--force', '--prune', '--'] + fetch_refs,
492 wire, ['fetch', url, '--force', '--prune', '--'] + fetch_refs,
493 fail_on_stderr=False,
493 fail_on_stderr=False,
494 _copts=['-c', 'core.askpass=""'],
494 _copts=['-c', 'core.askpass=""'],
495 extra_env={'GIT_TERMINAL_PROMPT': '0'})
495 extra_env={'GIT_TERMINAL_PROMPT': '0'})
496
496
497 return remote_refs
497 return remote_refs
498
498
499 @reraise_safe_exceptions
499 @reraise_safe_exceptions
500 def sync_push(self, wire, url, refs=None):
500 def sync_push(self, wire, url, refs=None):
501 if not self.check_url(url, wire):
501 if not self.check_url(url, wire):
502 return
502 return
503
503
504 repo = self._factory.repo(wire)
504 repo = self._factory.repo(wire)
505 self.run_git_command(
505 self.run_git_command(
506 wire, ['push', url, '--mirror'], fail_on_stderr=False,
506 wire, ['push', url, '--mirror'], fail_on_stderr=False,
507 _copts=['-c', 'core.askpass=""'],
507 _copts=['-c', 'core.askpass=""'],
508 extra_env={'GIT_TERMINAL_PROMPT': '0'})
508 extra_env={'GIT_TERMINAL_PROMPT': '0'})
509
509
510 @reraise_safe_exceptions
510 @reraise_safe_exceptions
511 def get_remote_refs(self, wire, url):
511 def get_remote_refs(self, wire, url):
512 repo = Repo(url)
512 repo = Repo(url)
513 return repo.get_refs()
513 return repo.get_refs()
514
514
515 @reraise_safe_exceptions
515 @reraise_safe_exceptions
516 def get_description(self, wire):
516 def get_description(self, wire):
517 repo = self._factory.repo(wire)
517 repo = self._factory.repo(wire)
518 return repo.get_description()
518 return repo.get_description()
519
519
520 @reraise_safe_exceptions
520 @reraise_safe_exceptions
521 def get_file_history(self, wire, file_path, commit_id, limit):
522 repo = self._factory.repo(wire)
523 include = [commit_id]
524 paths = [file_path]
525
526 walker = repo.get_walker(include, paths=paths, max_entries=limit)
527 return [x.commit.id for x in walker]
528
529 @reraise_safe_exceptions
530 def get_missing_revs(self, wire, rev1, rev2, path2):
521 def get_missing_revs(self, wire, rev1, rev2, path2):
531 repo = self._factory.repo(wire)
522 repo = self._factory.repo(wire)
532 LocalGitClient(thin_packs=False).fetch(path2, repo)
523 LocalGitClient(thin_packs=False).fetch(path2, repo)
533
524
534 wire_remote = wire.copy()
525 wire_remote = wire.copy()
535 wire_remote['path'] = path2
526 wire_remote['path'] = path2
536 repo_remote = self._factory.repo(wire_remote)
527 repo_remote = self._factory.repo(wire_remote)
537 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
528 LocalGitClient(thin_packs=False).fetch(wire["path"], repo_remote)
538
529
539 revs = [
530 revs = [
540 x.commit.id
531 x.commit.id
541 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
532 for x in repo_remote.get_walker(include=[rev2], exclude=[rev1])]
542 return revs
533 return revs
543
534
544 @reraise_safe_exceptions
535 @reraise_safe_exceptions
545 def get_object(self, wire, sha):
536 def get_object(self, wire, sha):
546 repo = self._factory.repo(wire)
537 repo = self._factory.repo(wire)
547 obj = repo.get_object(sha)
538 obj = repo.get_object(sha)
548 commit_id = obj.id
539 commit_id = obj.id
549
540
550 if isinstance(obj, Tag):
541 if isinstance(obj, Tag):
551 commit_id = obj.object[1]
542 commit_id = obj.object[1]
552
543
553 return {
544 return {
554 'id': obj.id,
545 'id': obj.id,
555 'type': obj.type_name,
546 'type': obj.type_name,
556 'commit_id': commit_id
547 'commit_id': commit_id
557 }
548 }
558
549
559 @reraise_safe_exceptions
550 @reraise_safe_exceptions
560 def get_object_attrs(self, wire, sha, *attrs):
551 def get_object_attrs(self, wire, sha, *attrs):
561 repo = self._factory.repo(wire)
552 repo = self._factory.repo(wire)
562 obj = repo.get_object(sha)
553 obj = repo.get_object(sha)
563 return list(getattr(obj, a) for a in attrs)
554 return list(getattr(obj, a) for a in attrs)
564
555
565 @reraise_safe_exceptions
556 @reraise_safe_exceptions
566 def get_refs(self, wire):
557 def get_refs(self, wire):
567 repo = self._factory.repo(wire)
558 repo = self._factory.repo(wire)
568 result = {}
559 result = {}
569 for ref, sha in repo.refs.as_dict().items():
560 for ref, sha in repo.refs.as_dict().items():
570 peeled_sha = repo.get_peeled(ref)
561 peeled_sha = repo.get_peeled(ref)
571 result[ref] = peeled_sha
562 result[ref] = peeled_sha
572 return result
563 return result
573
564
574 @reraise_safe_exceptions
565 @reraise_safe_exceptions
575 def get_refs_path(self, wire):
566 def get_refs_path(self, wire):
576 repo = self._factory.repo(wire)
567 repo = self._factory.repo(wire)
577 return repo.refs.path
568 return repo.refs.path
578
569
579 @reraise_safe_exceptions
570 @reraise_safe_exceptions
580 def head(self, wire, show_exc=True):
571 def head(self, wire, show_exc=True):
581 repo = self._factory.repo(wire)
572 repo = self._factory.repo(wire)
582 try:
573 try:
583 return repo.head()
574 return repo.head()
584 except Exception:
575 except Exception:
585 if show_exc:
576 if show_exc:
586 raise
577 raise
587
578
588 @reraise_safe_exceptions
579 @reraise_safe_exceptions
589 def init(self, wire):
580 def init(self, wire):
590 repo_path = str_to_dulwich(wire['path'])
581 repo_path = str_to_dulwich(wire['path'])
591 self.repo = Repo.init(repo_path)
582 self.repo = Repo.init(repo_path)
592
583
593 @reraise_safe_exceptions
584 @reraise_safe_exceptions
594 def init_bare(self, wire):
585 def init_bare(self, wire):
595 repo_path = str_to_dulwich(wire['path'])
586 repo_path = str_to_dulwich(wire['path'])
596 self.repo = Repo.init_bare(repo_path)
587 self.repo = Repo.init_bare(repo_path)
597
588
598 @reraise_safe_exceptions
589 @reraise_safe_exceptions
599 def revision(self, wire, rev):
590 def revision(self, wire, rev):
600 repo = self._factory.repo(wire)
591 repo = self._factory.repo(wire)
601 obj = repo[rev]
592 obj = repo[rev]
602 obj_data = {
593 obj_data = {
603 'id': obj.id,
594 'id': obj.id,
604 }
595 }
605 try:
596 try:
606 obj_data['tree'] = obj.tree
597 obj_data['tree'] = obj.tree
607 except AttributeError:
598 except AttributeError:
608 pass
599 pass
609 return obj_data
600 return obj_data
610
601
611 @reraise_safe_exceptions
602 @reraise_safe_exceptions
612 def commit_attribute(self, wire, rev, attr):
603 def commit_attribute(self, wire, rev, attr):
613 repo = self._factory.repo(wire)
604 repo = self._factory.repo(wire)
614 obj = repo[rev]
605 obj = repo[rev]
615 return getattr(obj, attr)
606 return getattr(obj, attr)
616
607
617 @reraise_safe_exceptions
608 @reraise_safe_exceptions
618 def set_refs(self, wire, key, value):
609 def set_refs(self, wire, key, value):
619 repo = self._factory.repo(wire)
610 repo = self._factory.repo(wire)
620 repo.refs[key] = value
611 repo.refs[key] = value
621
612
622 @reraise_safe_exceptions
613 @reraise_safe_exceptions
623 def remove_ref(self, wire, key):
614 def remove_ref(self, wire, key):
624 repo = self._factory.repo(wire)
615 repo = self._factory.repo(wire)
625 del repo.refs[key]
616 del repo.refs[key]
626
617
627 @reraise_safe_exceptions
618 @reraise_safe_exceptions
628 def tree_changes(self, wire, source_id, target_id):
619 def tree_changes(self, wire, source_id, target_id):
629 repo = self._factory.repo(wire)
620 repo = self._factory.repo(wire)
630 source = repo[source_id].tree if source_id else None
621 source = repo[source_id].tree if source_id else None
631 target = repo[target_id].tree
622 target = repo[target_id].tree
632 result = repo.object_store.tree_changes(source, target)
623 result = repo.object_store.tree_changes(source, target)
633 return list(result)
624 return list(result)
634
625
635 @reraise_safe_exceptions
626 @reraise_safe_exceptions
636 def tree_items(self, wire, tree_id):
627 def tree_items(self, wire, tree_id):
637 repo = self._factory.repo(wire)
628 repo = self._factory.repo(wire)
638 tree = repo[tree_id]
629 tree = repo[tree_id]
639
630
640 result = []
631 result = []
641 for item in tree.iteritems():
632 for item in tree.iteritems():
642 item_sha = item.sha
633 item_sha = item.sha
643 item_mode = item.mode
634 item_mode = item.mode
644
635
645 if FILE_MODE(item_mode) == GIT_LINK:
636 if FILE_MODE(item_mode) == GIT_LINK:
646 item_type = "link"
637 item_type = "link"
647 else:
638 else:
648 item_type = repo[item_sha].type_name
639 item_type = repo[item_sha].type_name
649
640
650 result.append((item.path, item_mode, item_sha, item_type))
641 result.append((item.path, item_mode, item_sha, item_type))
651 return result
642 return result
652
643
653 @reraise_safe_exceptions
644 @reraise_safe_exceptions
654 def update_server_info(self, wire):
645 def update_server_info(self, wire):
655 repo = self._factory.repo(wire)
646 repo = self._factory.repo(wire)
656 update_server_info(repo)
647 update_server_info(repo)
657
648
658 @reraise_safe_exceptions
649 @reraise_safe_exceptions
659 def discover_git_version(self):
650 def discover_git_version(self):
660 stdout, _ = self.run_git_command(
651 stdout, _ = self.run_git_command(
661 {}, ['--version'], _bare=True, _safe=True)
652 {}, ['--version'], _bare=True, _safe=True)
662 prefix = 'git version'
653 prefix = 'git version'
663 if stdout.startswith(prefix):
654 if stdout.startswith(prefix):
664 stdout = stdout[len(prefix):]
655 stdout = stdout[len(prefix):]
665 return stdout.strip()
656 return stdout.strip()
666
657
667 @reraise_safe_exceptions
658 @reraise_safe_exceptions
668 def run_git_command(self, wire, cmd, **opts):
659 def run_git_command(self, wire, cmd, **opts):
669 path = wire.get('path', None)
660 path = wire.get('path', None)
670
661
671 if path and os.path.isdir(path):
662 if path and os.path.isdir(path):
672 opts['cwd'] = path
663 opts['cwd'] = path
673
664
674 if '_bare' in opts:
665 if '_bare' in opts:
675 _copts = []
666 _copts = []
676 del opts['_bare']
667 del opts['_bare']
677 else:
668 else:
678 _copts = ['-c', 'core.quotepath=false', ]
669 _copts = ['-c', 'core.quotepath=false', ]
679 safe_call = False
670 safe_call = False
680 if '_safe' in opts:
671 if '_safe' in opts:
681 # no exc on failure
672 # no exc on failure
682 del opts['_safe']
673 del opts['_safe']
683 safe_call = True
674 safe_call = True
684
675
685 if '_copts' in opts:
676 if '_copts' in opts:
686 _copts.extend(opts['_copts'] or [])
677 _copts.extend(opts['_copts'] or [])
687 del opts['_copts']
678 del opts['_copts']
688
679
689 gitenv = os.environ.copy()
680 gitenv = os.environ.copy()
690 gitenv.update(opts.pop('extra_env', {}))
681 gitenv.update(opts.pop('extra_env', {}))
691 # need to clean fix GIT_DIR !
682 # need to clean fix GIT_DIR !
692 if 'GIT_DIR' in gitenv:
683 if 'GIT_DIR' in gitenv:
693 del gitenv['GIT_DIR']
684 del gitenv['GIT_DIR']
694 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
685 gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
695 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
686 gitenv['GIT_DISCOVERY_ACROSS_FILESYSTEM'] = '1'
696
687
697 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
688 cmd = [settings.GIT_EXECUTABLE] + _copts + cmd
698 _opts = {'env': gitenv, 'shell': False}
689 _opts = {'env': gitenv, 'shell': False}
699
690
700 try:
691 try:
701 _opts.update(opts)
692 _opts.update(opts)
702 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
693 p = subprocessio.SubprocessIOChunker(cmd, **_opts)
703
694
704 return ''.join(p), ''.join(p.error)
695 return ''.join(p), ''.join(p.error)
705 except (EnvironmentError, OSError) as err:
696 except (EnvironmentError, OSError) as err:
706 cmd = ' '.join(cmd) # human friendly CMD
697 cmd = ' '.join(cmd) # human friendly CMD
707 tb_err = ("Couldn't run git command (%s).\n"
698 tb_err = ("Couldn't run git command (%s).\n"
708 "Original error was:%s\n"
699 "Original error was:%s\n"
709 "Call options:%s\n"
700 "Call options:%s\n"
710 % (cmd, err, _opts))
701 % (cmd, err, _opts))
711 log.exception(tb_err)
702 log.exception(tb_err)
712 if safe_call:
703 if safe_call:
713 return '', err
704 return '', err
714 else:
705 else:
715 raise exceptions.VcsException()(tb_err)
706 raise exceptions.VcsException()(tb_err)
716
707
717 @reraise_safe_exceptions
708 @reraise_safe_exceptions
718 def install_hooks(self, wire, force=False):
709 def install_hooks(self, wire, force=False):
719 from vcsserver.hook_utils import install_git_hooks
710 from vcsserver.hook_utils import install_git_hooks
720 repo = self._factory.repo(wire)
711 repo = self._factory.repo(wire)
721 return install_git_hooks(repo.path, repo.bare, force_create=force)
712 return install_git_hooks(repo.path, repo.bare, force_create=force)
722
713
723
714
724 def str_to_dulwich(value):
715 def str_to_dulwich(value):
725 """
716 """
726 Dulwich 0.10.1a requires `unicode` objects to be passed in.
717 Dulwich 0.10.1a requires `unicode` objects to be passed in.
727 """
718 """
728 return value.decode(settings.WIRE_ENCODING)
719 return value.decode(settings.WIRE_ENCODING)
@@ -1,795 +1,795 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2018 RhodeCode GmbH
2 # Copyright (C) 2014-2018 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import io
18 import io
19 import logging
19 import logging
20 import stat
20 import stat
21 import urllib
21 import urllib
22 import urllib2
22 import urllib2
23
23
24 from hgext import largefiles, rebase
24 from hgext import largefiles, rebase
25 from hgext.strip import strip as hgext_strip
25 from hgext.strip import strip as hgext_strip
26 from mercurial import commands
26 from mercurial import commands
27 from mercurial import unionrepo
27 from mercurial import unionrepo
28 from mercurial import verify
28 from mercurial import verify
29
29
30 from vcsserver import exceptions
30 from vcsserver import exceptions
31 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
31 from vcsserver.base import RepoFactory, obfuscate_qs, raise_from_original
32 from vcsserver.hgcompat import (
32 from vcsserver.hgcompat import (
33 archival, bin, clone, config as hgconfig, diffopts, hex,
33 archival, bin, clone, config as hgconfig, diffopts, hex,
34 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler,
34 hg_url as url_parser, httpbasicauthhandler, httpdigestauthhandler,
35 makepeer, localrepository, match, memctx, exchange, memfilectx, nullrev,
35 makepeer, localrepository, match, memctx, exchange, memfilectx, nullrev,
36 patch, peer, revrange, ui, hg_tag, Abort, LookupError, RepoError,
36 patch, peer, revrange, ui, hg_tag, Abort, LookupError, RepoError,
37 RepoLookupError, InterventionRequired, RequirementError)
37 RepoLookupError, InterventionRequired, RequirementError)
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 def make_ui_from_config(repo_config):
42 def make_ui_from_config(repo_config):
43 baseui = ui.ui()
43 baseui = ui.ui()
44
44
45 # clean the baseui object
45 # clean the baseui object
46 baseui._ocfg = hgconfig.config()
46 baseui._ocfg = hgconfig.config()
47 baseui._ucfg = hgconfig.config()
47 baseui._ucfg = hgconfig.config()
48 baseui._tcfg = hgconfig.config()
48 baseui._tcfg = hgconfig.config()
49
49
50 for section, option, value in repo_config:
50 for section, option, value in repo_config:
51 baseui.setconfig(section, option, value)
51 baseui.setconfig(section, option, value)
52
52
53 # make our hgweb quiet so it doesn't print output
53 # make our hgweb quiet so it doesn't print output
54 baseui.setconfig('ui', 'quiet', 'true')
54 baseui.setconfig('ui', 'quiet', 'true')
55
55
56 baseui.setconfig('ui', 'paginate', 'never')
56 baseui.setconfig('ui', 'paginate', 'never')
57 # force mercurial to only use 1 thread, otherwise it may try to set a
57 # force mercurial to only use 1 thread, otherwise it may try to set a
58 # signal in a non-main thread, thus generating a ValueError.
58 # signal in a non-main thread, thus generating a ValueError.
59 baseui.setconfig('worker', 'numcpus', 1)
59 baseui.setconfig('worker', 'numcpus', 1)
60
60
61 # If there is no config for the largefiles extension, we explicitly disable
61 # If there is no config for the largefiles extension, we explicitly disable
62 # it here. This overrides settings from repositories hgrc file. Recent
62 # it here. This overrides settings from repositories hgrc file. Recent
63 # mercurial versions enable largefiles in hgrc on clone from largefile
63 # mercurial versions enable largefiles in hgrc on clone from largefile
64 # repo.
64 # repo.
65 if not baseui.hasconfig('extensions', 'largefiles'):
65 if not baseui.hasconfig('extensions', 'largefiles'):
66 log.debug('Explicitly disable largefiles extension for repo.')
66 log.debug('Explicitly disable largefiles extension for repo.')
67 baseui.setconfig('extensions', 'largefiles', '!')
67 baseui.setconfig('extensions', 'largefiles', '!')
68
68
69 return baseui
69 return baseui
70
70
71
71
72 def reraise_safe_exceptions(func):
72 def reraise_safe_exceptions(func):
73 """Decorator for converting mercurial exceptions to something neutral."""
73 """Decorator for converting mercurial exceptions to something neutral."""
74 def wrapper(*args, **kwargs):
74 def wrapper(*args, **kwargs):
75 try:
75 try:
76 return func(*args, **kwargs)
76 return func(*args, **kwargs)
77 except (Abort, InterventionRequired) as e:
77 except (Abort, InterventionRequired) as e:
78 raise_from_original(exceptions.AbortException(e))
78 raise_from_original(exceptions.AbortException(e))
79 except RepoLookupError as e:
79 except RepoLookupError as e:
80 raise_from_original(exceptions.LookupException(e))
80 raise_from_original(exceptions.LookupException(e))
81 except RequirementError as e:
81 except RequirementError as e:
82 raise_from_original(exceptions.RequirementException(e))
82 raise_from_original(exceptions.RequirementException(e))
83 except RepoError as e:
83 except RepoError as e:
84 raise_from_original(exceptions.VcsException(e))
84 raise_from_original(exceptions.VcsException(e))
85 except LookupError as e:
85 except LookupError as e:
86 raise_from_original(exceptions.LookupException(e))
86 raise_from_original(exceptions.LookupException(e))
87 except Exception as e:
87 except Exception as e:
88 if not hasattr(e, '_vcs_kind'):
88 if not hasattr(e, '_vcs_kind'):
89 log.exception("Unhandled exception in hg remote call")
89 log.exception("Unhandled exception in hg remote call")
90 raise_from_original(exceptions.UnhandledException(e))
90 raise_from_original(exceptions.UnhandledException(e))
91
91
92 raise
92 raise
93 return wrapper
93 return wrapper
94
94
95
95
96 class MercurialFactory(RepoFactory):
96 class MercurialFactory(RepoFactory):
97 repo_type = 'hg'
97 repo_type = 'hg'
98
98
99 def _create_config(self, config, hooks=True):
99 def _create_config(self, config, hooks=True):
100 if not hooks:
100 if not hooks:
101 hooks_to_clean = frozenset((
101 hooks_to_clean = frozenset((
102 'changegroup.repo_size', 'preoutgoing.pre_pull',
102 'changegroup.repo_size', 'preoutgoing.pre_pull',
103 'outgoing.pull_logger', 'prechangegroup.pre_push'))
103 'outgoing.pull_logger', 'prechangegroup.pre_push'))
104 new_config = []
104 new_config = []
105 for section, option, value in config:
105 for section, option, value in config:
106 if section == 'hooks' and option in hooks_to_clean:
106 if section == 'hooks' and option in hooks_to_clean:
107 continue
107 continue
108 new_config.append((section, option, value))
108 new_config.append((section, option, value))
109 config = new_config
109 config = new_config
110
110
111 baseui = make_ui_from_config(config)
111 baseui = make_ui_from_config(config)
112 return baseui
112 return baseui
113
113
114 def _create_repo(self, wire, create):
114 def _create_repo(self, wire, create):
115 baseui = self._create_config(wire["config"])
115 baseui = self._create_config(wire["config"])
116 return localrepository(baseui, wire["path"], create)
116 return localrepository(baseui, wire["path"], create)
117
117
118
118
119 class HgRemote(object):
119 class HgRemote(object):
120
120
121 def __init__(self, factory):
121 def __init__(self, factory):
122 self._factory = factory
122 self._factory = factory
123
123
124 self._bulk_methods = {
124 self._bulk_methods = {
125 "affected_files": self.ctx_files,
125 "affected_files": self.ctx_files,
126 "author": self.ctx_user,
126 "author": self.ctx_user,
127 "branch": self.ctx_branch,
127 "branch": self.ctx_branch,
128 "children": self.ctx_children,
128 "children": self.ctx_children,
129 "date": self.ctx_date,
129 "date": self.ctx_date,
130 "message": self.ctx_description,
130 "message": self.ctx_description,
131 "parents": self.ctx_parents,
131 "parents": self.ctx_parents,
132 "status": self.ctx_status,
132 "status": self.ctx_status,
133 "obsolete": self.ctx_obsolete,
133 "obsolete": self.ctx_obsolete,
134 "phase": self.ctx_phase,
134 "phase": self.ctx_phase,
135 "hidden": self.ctx_hidden,
135 "hidden": self.ctx_hidden,
136 "_file_paths": self.ctx_list,
136 "_file_paths": self.ctx_list,
137 }
137 }
138
138
139 @reraise_safe_exceptions
139 @reraise_safe_exceptions
140 def discover_hg_version(self):
140 def discover_hg_version(self):
141 from mercurial import util
141 from mercurial import util
142 return util.version()
142 return util.version()
143
143
144 @reraise_safe_exceptions
144 @reraise_safe_exceptions
145 def archive_repo(self, archive_path, mtime, file_info, kind):
145 def archive_repo(self, archive_path, mtime, file_info, kind):
146 if kind == "tgz":
146 if kind == "tgz":
147 archiver = archival.tarit(archive_path, mtime, "gz")
147 archiver = archival.tarit(archive_path, mtime, "gz")
148 elif kind == "tbz2":
148 elif kind == "tbz2":
149 archiver = archival.tarit(archive_path, mtime, "bz2")
149 archiver = archival.tarit(archive_path, mtime, "bz2")
150 elif kind == 'zip':
150 elif kind == 'zip':
151 archiver = archival.zipit(archive_path, mtime)
151 archiver = archival.zipit(archive_path, mtime)
152 else:
152 else:
153 raise exceptions.ArchiveException()(
153 raise exceptions.ArchiveException()(
154 'Remote does not support: "%s".' % kind)
154 'Remote does not support: "%s".' % kind)
155
155
156 for f_path, f_mode, f_is_link, f_content in file_info:
156 for f_path, f_mode, f_is_link, f_content in file_info:
157 archiver.addfile(f_path, f_mode, f_is_link, f_content)
157 archiver.addfile(f_path, f_mode, f_is_link, f_content)
158 archiver.done()
158 archiver.done()
159
159
160 @reraise_safe_exceptions
160 @reraise_safe_exceptions
161 def bookmarks(self, wire):
161 def bookmarks(self, wire):
162 repo = self._factory.repo(wire)
162 repo = self._factory.repo(wire)
163 return dict(repo._bookmarks)
163 return dict(repo._bookmarks)
164
164
165 @reraise_safe_exceptions
165 @reraise_safe_exceptions
166 def branches(self, wire, normal, closed):
166 def branches(self, wire, normal, closed):
167 repo = self._factory.repo(wire)
167 repo = self._factory.repo(wire)
168 iter_branches = repo.branchmap().iterbranches()
168 iter_branches = repo.branchmap().iterbranches()
169 bt = {}
169 bt = {}
170 for branch_name, _heads, tip, is_closed in iter_branches:
170 for branch_name, _heads, tip, is_closed in iter_branches:
171 if normal and not is_closed:
171 if normal and not is_closed:
172 bt[branch_name] = tip
172 bt[branch_name] = tip
173 if closed and is_closed:
173 if closed and is_closed:
174 bt[branch_name] = tip
174 bt[branch_name] = tip
175
175
176 return bt
176 return bt
177
177
178 @reraise_safe_exceptions
178 @reraise_safe_exceptions
179 def bulk_request(self, wire, rev, pre_load):
179 def bulk_request(self, wire, rev, pre_load):
180 result = {}
180 result = {}
181 for attr in pre_load:
181 for attr in pre_load:
182 try:
182 try:
183 method = self._bulk_methods[attr]
183 method = self._bulk_methods[attr]
184 result[attr] = method(wire, rev)
184 result[attr] = method(wire, rev)
185 except KeyError as e:
185 except KeyError as e:
186 raise exceptions.VcsException(e)(
186 raise exceptions.VcsException(e)(
187 'Unknown bulk attribute: "%s"' % attr)
187 'Unknown bulk attribute: "%s"' % attr)
188 return result
188 return result
189
189
190 @reraise_safe_exceptions
190 @reraise_safe_exceptions
191 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
191 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
192 baseui = self._factory._create_config(wire["config"], hooks=hooks)
192 baseui = self._factory._create_config(wire["config"], hooks=hooks)
193 clone(baseui, source, dest, noupdate=not update_after_clone)
193 clone(baseui, source, dest, noupdate=not update_after_clone)
194
194
195 @reraise_safe_exceptions
195 @reraise_safe_exceptions
196 def commitctx(
196 def commitctx(
197 self, wire, message, parents, commit_time, commit_timezone,
197 self, wire, message, parents, commit_time, commit_timezone,
198 user, files, extra, removed, updated):
198 user, files, extra, removed, updated):
199
199
200 def _filectxfn(_repo, memctx, path):
200 def _filectxfn(_repo, memctx, path):
201 """
201 """
202 Marks given path as added/changed/removed in a given _repo. This is
202 Marks given path as added/changed/removed in a given _repo. This is
203 for internal mercurial commit function.
203 for internal mercurial commit function.
204 """
204 """
205
205
206 # check if this path is removed
206 # check if this path is removed
207 if path in removed:
207 if path in removed:
208 # returning None is a way to mark node for removal
208 # returning None is a way to mark node for removal
209 return None
209 return None
210
210
211 # check if this path is added
211 # check if this path is added
212 for node in updated:
212 for node in updated:
213 if node['path'] == path:
213 if node['path'] == path:
214 return memfilectx(
214 return memfilectx(
215 _repo,
215 _repo,
216 changectx=memctx,
216 changectx=memctx,
217 path=node['path'],
217 path=node['path'],
218 data=node['content'],
218 data=node['content'],
219 islink=False,
219 islink=False,
220 isexec=bool(node['mode'] & stat.S_IXUSR),
220 isexec=bool(node['mode'] & stat.S_IXUSR),
221 copied=False)
221 copied=False)
222
222
223 raise exceptions.AbortException()(
223 raise exceptions.AbortException()(
224 "Given path haven't been marked as added, "
224 "Given path haven't been marked as added, "
225 "changed or removed (%s)" % path)
225 "changed or removed (%s)" % path)
226
226
227 repo = self._factory.repo(wire)
227 repo = self._factory.repo(wire)
228
228
229 commit_ctx = memctx(
229 commit_ctx = memctx(
230 repo=repo,
230 repo=repo,
231 parents=parents,
231 parents=parents,
232 text=message,
232 text=message,
233 files=files,
233 files=files,
234 filectxfn=_filectxfn,
234 filectxfn=_filectxfn,
235 user=user,
235 user=user,
236 date=(commit_time, commit_timezone),
236 date=(commit_time, commit_timezone),
237 extra=extra)
237 extra=extra)
238
238
239 n = repo.commitctx(commit_ctx)
239 n = repo.commitctx(commit_ctx)
240 new_id = hex(n)
240 new_id = hex(n)
241
241
242 return new_id
242 return new_id
243
243
244 @reraise_safe_exceptions
244 @reraise_safe_exceptions
245 def ctx_branch(self, wire, revision):
245 def ctx_branch(self, wire, revision):
246 repo = self._factory.repo(wire)
246 repo = self._factory.repo(wire)
247 ctx = repo[revision]
247 ctx = repo[revision]
248 return ctx.branch()
248 return ctx.branch()
249
249
250 @reraise_safe_exceptions
250 @reraise_safe_exceptions
251 def ctx_children(self, wire, revision):
251 def ctx_children(self, wire, revision):
252 repo = self._factory.repo(wire)
252 repo = self._factory.repo(wire)
253 ctx = repo[revision]
253 ctx = repo[revision]
254 return [child.rev() for child in ctx.children()]
254 return [child.rev() for child in ctx.children()]
255
255
256 @reraise_safe_exceptions
256 @reraise_safe_exceptions
257 def ctx_date(self, wire, revision):
257 def ctx_date(self, wire, revision):
258 repo = self._factory.repo(wire)
258 repo = self._factory.repo(wire)
259 ctx = repo[revision]
259 ctx = repo[revision]
260 return ctx.date()
260 return ctx.date()
261
261
262 @reraise_safe_exceptions
262 @reraise_safe_exceptions
263 def ctx_description(self, wire, revision):
263 def ctx_description(self, wire, revision):
264 repo = self._factory.repo(wire)
264 repo = self._factory.repo(wire)
265 ctx = repo[revision]
265 ctx = repo[revision]
266 return ctx.description()
266 return ctx.description()
267
267
268 @reraise_safe_exceptions
268 @reraise_safe_exceptions
269 def ctx_diff(
269 def ctx_diff(
270 self, wire, revision, git=True, ignore_whitespace=True, context=3):
270 self, wire, revision, git=True, ignore_whitespace=True, context=3):
271 repo = self._factory.repo(wire)
271 repo = self._factory.repo(wire)
272 ctx = repo[revision]
272 ctx = repo[revision]
273 result = ctx.diff(
273 result = ctx.diff(
274 git=git, ignore_whitespace=ignore_whitespace, context=context)
274 git=git, ignore_whitespace=ignore_whitespace, context=context)
275 return list(result)
275 return list(result)
276
276
277 @reraise_safe_exceptions
277 @reraise_safe_exceptions
278 def ctx_files(self, wire, revision):
278 def ctx_files(self, wire, revision):
279 repo = self._factory.repo(wire)
279 repo = self._factory.repo(wire)
280 ctx = repo[revision]
280 ctx = repo[revision]
281 return ctx.files()
281 return ctx.files()
282
282
283 @reraise_safe_exceptions
283 @reraise_safe_exceptions
284 def ctx_list(self, path, revision):
284 def ctx_list(self, path, revision):
285 repo = self._factory.repo(path)
285 repo = self._factory.repo(path)
286 ctx = repo[revision]
286 ctx = repo[revision]
287 return list(ctx)
287 return list(ctx)
288
288
289 @reraise_safe_exceptions
289 @reraise_safe_exceptions
290 def ctx_parents(self, wire, revision):
290 def ctx_parents(self, wire, revision):
291 repo = self._factory.repo(wire)
291 repo = self._factory.repo(wire)
292 ctx = repo[revision]
292 ctx = repo[revision]
293 return [parent.rev() for parent in ctx.parents()]
293 return [parent.rev() for parent in ctx.parents()]
294
294
295 @reraise_safe_exceptions
295 @reraise_safe_exceptions
296 def ctx_phase(self, wire, revision):
296 def ctx_phase(self, wire, revision):
297 repo = self._factory.repo(wire)
297 repo = self._factory.repo(wire)
298 ctx = repo[revision]
298 ctx = repo[revision]
299 # public=0, draft=1, secret=3
299 # public=0, draft=1, secret=3
300 return ctx.phase()
300 return ctx.phase()
301
301
302 @reraise_safe_exceptions
302 @reraise_safe_exceptions
303 def ctx_obsolete(self, wire, revision):
303 def ctx_obsolete(self, wire, revision):
304 repo = self._factory.repo(wire)
304 repo = self._factory.repo(wire)
305 ctx = repo[revision]
305 ctx = repo[revision]
306 return ctx.obsolete()
306 return ctx.obsolete()
307
307
308 @reraise_safe_exceptions
308 @reraise_safe_exceptions
309 def ctx_hidden(self, wire, revision):
309 def ctx_hidden(self, wire, revision):
310 repo = self._factory.repo(wire)
310 repo = self._factory.repo(wire)
311 ctx = repo[revision]
311 ctx = repo[revision]
312 return ctx.hidden()
312 return ctx.hidden()
313
313
314 @reraise_safe_exceptions
314 @reraise_safe_exceptions
315 def ctx_substate(self, wire, revision):
315 def ctx_substate(self, wire, revision):
316 repo = self._factory.repo(wire)
316 repo = self._factory.repo(wire)
317 ctx = repo[revision]
317 ctx = repo[revision]
318 return ctx.substate
318 return ctx.substate
319
319
320 @reraise_safe_exceptions
320 @reraise_safe_exceptions
321 def ctx_status(self, wire, revision):
321 def ctx_status(self, wire, revision):
322 repo = self._factory.repo(wire)
322 repo = self._factory.repo(wire)
323 ctx = repo[revision]
323 ctx = repo[revision]
324 status = repo[ctx.p1().node()].status(other=ctx.node())
324 status = repo[ctx.p1().node()].status(other=ctx.node())
325 # object of status (odd, custom named tuple in mercurial) is not
325 # object of status (odd, custom named tuple in mercurial) is not
326 # correctly serializable, we make it a list, as the underling
326 # correctly serializable, we make it a list, as the underling
327 # API expects this to be a list
327 # API expects this to be a list
328 return list(status)
328 return list(status)
329
329
330 @reraise_safe_exceptions
330 @reraise_safe_exceptions
331 def ctx_user(self, wire, revision):
331 def ctx_user(self, wire, revision):
332 repo = self._factory.repo(wire)
332 repo = self._factory.repo(wire)
333 ctx = repo[revision]
333 ctx = repo[revision]
334 return ctx.user()
334 return ctx.user()
335
335
336 @reraise_safe_exceptions
336 @reraise_safe_exceptions
337 def check_url(self, url, config):
337 def check_url(self, url, config):
338 _proto = None
338 _proto = None
339 if '+' in url[:url.find('://')]:
339 if '+' in url[:url.find('://')]:
340 _proto = url[0:url.find('+')]
340 _proto = url[0:url.find('+')]
341 url = url[url.find('+') + 1:]
341 url = url[url.find('+') + 1:]
342 handlers = []
342 handlers = []
343 url_obj = url_parser(url)
343 url_obj = url_parser(url)
344 test_uri, authinfo = url_obj.authinfo()
344 test_uri, authinfo = url_obj.authinfo()
345 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
345 url_obj.passwd = '*****' if url_obj.passwd else url_obj.passwd
346 url_obj.query = obfuscate_qs(url_obj.query)
346 url_obj.query = obfuscate_qs(url_obj.query)
347
347
348 cleaned_uri = str(url_obj)
348 cleaned_uri = str(url_obj)
349 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
349 log.info("Checking URL for remote cloning/import: %s", cleaned_uri)
350
350
351 if authinfo:
351 if authinfo:
352 # create a password manager
352 # create a password manager
353 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
353 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
354 passmgr.add_password(*authinfo)
354 passmgr.add_password(*authinfo)
355
355
356 handlers.extend((httpbasicauthhandler(passmgr),
356 handlers.extend((httpbasicauthhandler(passmgr),
357 httpdigestauthhandler(passmgr)))
357 httpdigestauthhandler(passmgr)))
358
358
359 o = urllib2.build_opener(*handlers)
359 o = urllib2.build_opener(*handlers)
360 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
360 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
361 ('Accept', 'application/mercurial-0.1')]
361 ('Accept', 'application/mercurial-0.1')]
362
362
363 q = {"cmd": 'between'}
363 q = {"cmd": 'between'}
364 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
364 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
365 qs = '?%s' % urllib.urlencode(q)
365 qs = '?%s' % urllib.urlencode(q)
366 cu = "%s%s" % (test_uri, qs)
366 cu = "%s%s" % (test_uri, qs)
367 req = urllib2.Request(cu, None, {})
367 req = urllib2.Request(cu, None, {})
368
368
369 try:
369 try:
370 log.debug("Trying to open URL %s", cleaned_uri)
370 log.debug("Trying to open URL %s", cleaned_uri)
371 resp = o.open(req)
371 resp = o.open(req)
372 if resp.code != 200:
372 if resp.code != 200:
373 raise exceptions.URLError()('Return Code is not 200')
373 raise exceptions.URLError()('Return Code is not 200')
374 except Exception as e:
374 except Exception as e:
375 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
375 log.warning("URL cannot be opened: %s", cleaned_uri, exc_info=True)
376 # means it cannot be cloned
376 # means it cannot be cloned
377 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
377 raise exceptions.URLError(e)("[%s] org_exc: %s" % (cleaned_uri, e))
378
378
379 # now check if it's a proper hg repo, but don't do it for svn
379 # now check if it's a proper hg repo, but don't do it for svn
380 try:
380 try:
381 if _proto == 'svn':
381 if _proto == 'svn':
382 pass
382 pass
383 else:
383 else:
384 # check for pure hg repos
384 # check for pure hg repos
385 log.debug(
385 log.debug(
386 "Verifying if URL is a Mercurial repository: %s",
386 "Verifying if URL is a Mercurial repository: %s",
387 cleaned_uri)
387 cleaned_uri)
388 ui = make_ui_from_config(config)
388 ui = make_ui_from_config(config)
389 peer_checker = makepeer(ui, url)
389 peer_checker = makepeer(ui, url)
390 peer_checker.lookup('tip')
390 peer_checker.lookup('tip')
391 except Exception as e:
391 except Exception as e:
392 log.warning("URL is not a valid Mercurial repository: %s",
392 log.warning("URL is not a valid Mercurial repository: %s",
393 cleaned_uri)
393 cleaned_uri)
394 raise exceptions.URLError(e)(
394 raise exceptions.URLError(e)(
395 "url [%s] does not look like an hg repo org_exc: %s"
395 "url [%s] does not look like an hg repo org_exc: %s"
396 % (cleaned_uri, e))
396 % (cleaned_uri, e))
397
397
398 log.info("URL is a valid Mercurial repository: %s", cleaned_uri)
398 log.info("URL is a valid Mercurial repository: %s", cleaned_uri)
399 return True
399 return True
400
400
401 @reraise_safe_exceptions
401 @reraise_safe_exceptions
402 def diff(
402 def diff(
403 self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews,
403 self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews,
404 context):
404 context):
405 repo = self._factory.repo(wire)
405 repo = self._factory.repo(wire)
406
406
407 if file_filter:
407 if file_filter:
408 match_filter = match(file_filter[0], '', [file_filter[1]])
408 match_filter = match(file_filter[0], '', [file_filter[1]])
409 else:
409 else:
410 match_filter = file_filter
410 match_filter = file_filter
411 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
411 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
412
412
413 try:
413 try:
414 return "".join(patch.diff(
414 return "".join(patch.diff(
415 repo, node1=rev1, node2=rev2, match=match_filter, opts=opts))
415 repo, node1=rev1, node2=rev2, match=match_filter, opts=opts))
416 except RepoLookupError as e:
416 except RepoLookupError as e:
417 raise exceptions.LookupException(e)()
417 raise exceptions.LookupException(e)()
418
418
419 @reraise_safe_exceptions
419 @reraise_safe_exceptions
420 def file_history(self, wire, revision, path, limit):
420 def node_history(self, wire, revision, path, limit):
421 repo = self._factory.repo(wire)
421 repo = self._factory.repo(wire)
422
422
423 ctx = repo[revision]
423 ctx = repo[revision]
424 fctx = ctx.filectx(path)
424 fctx = ctx.filectx(path)
425
425
426 def history_iter():
426 def history_iter():
427 limit_rev = fctx.rev()
427 limit_rev = fctx.rev()
428 for obj in reversed(list(fctx.filelog())):
428 for obj in reversed(list(fctx.filelog())):
429 obj = fctx.filectx(obj)
429 obj = fctx.filectx(obj)
430 if limit_rev >= obj.rev():
430 if limit_rev >= obj.rev():
431 yield obj
431 yield obj
432
432
433 history = []
433 history = []
434 for cnt, obj in enumerate(history_iter()):
434 for cnt, obj in enumerate(history_iter()):
435 if limit and cnt >= limit:
435 if limit and cnt >= limit:
436 break
436 break
437 history.append(hex(obj.node()))
437 history.append(hex(obj.node()))
438
438
439 return [x for x in history]
439 return [x for x in history]
440
440
441 @reraise_safe_exceptions
441 @reraise_safe_exceptions
442 def file_history_untill(self, wire, revision, path, limit):
442 def node_history_untill(self, wire, revision, path, limit):
443 repo = self._factory.repo(wire)
443 repo = self._factory.repo(wire)
444 ctx = repo[revision]
444 ctx = repo[revision]
445 fctx = ctx.filectx(path)
445 fctx = ctx.filectx(path)
446
446
447 file_log = list(fctx.filelog())
447 file_log = list(fctx.filelog())
448 if limit:
448 if limit:
449 # Limit to the last n items
449 # Limit to the last n items
450 file_log = file_log[-limit:]
450 file_log = file_log[-limit:]
451
451
452 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
452 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
453
453
454 @reraise_safe_exceptions
454 @reraise_safe_exceptions
455 def fctx_annotate(self, wire, revision, path):
455 def fctx_annotate(self, wire, revision, path):
456 repo = self._factory.repo(wire)
456 repo = self._factory.repo(wire)
457 ctx = repo[revision]
457 ctx = repo[revision]
458 fctx = ctx.filectx(path)
458 fctx = ctx.filectx(path)
459
459
460 result = []
460 result = []
461 for i, annotate_obj in enumerate(fctx.annotate(), 1):
461 for i, annotate_obj in enumerate(fctx.annotate(), 1):
462 ln_no = i
462 ln_no = i
463 sha = hex(annotate_obj.fctx.node())
463 sha = hex(annotate_obj.fctx.node())
464 content = annotate_obj.text
464 content = annotate_obj.text
465 result.append((ln_no, sha, content))
465 result.append((ln_no, sha, content))
466 return result
466 return result
467
467
468 @reraise_safe_exceptions
468 @reraise_safe_exceptions
469 def fctx_data(self, wire, revision, path):
469 def fctx_data(self, wire, revision, path):
470 repo = self._factory.repo(wire)
470 repo = self._factory.repo(wire)
471 ctx = repo[revision]
471 ctx = repo[revision]
472 fctx = ctx.filectx(path)
472 fctx = ctx.filectx(path)
473 return fctx.data()
473 return fctx.data()
474
474
475 @reraise_safe_exceptions
475 @reraise_safe_exceptions
476 def fctx_flags(self, wire, revision, path):
476 def fctx_flags(self, wire, revision, path):
477 repo = self._factory.repo(wire)
477 repo = self._factory.repo(wire)
478 ctx = repo[revision]
478 ctx = repo[revision]
479 fctx = ctx.filectx(path)
479 fctx = ctx.filectx(path)
480 return fctx.flags()
480 return fctx.flags()
481
481
482 @reraise_safe_exceptions
482 @reraise_safe_exceptions
483 def fctx_size(self, wire, revision, path):
483 def fctx_size(self, wire, revision, path):
484 repo = self._factory.repo(wire)
484 repo = self._factory.repo(wire)
485 ctx = repo[revision]
485 ctx = repo[revision]
486 fctx = ctx.filectx(path)
486 fctx = ctx.filectx(path)
487 return fctx.size()
487 return fctx.size()
488
488
489 @reraise_safe_exceptions
489 @reraise_safe_exceptions
490 def get_all_commit_ids(self, wire, name):
490 def get_all_commit_ids(self, wire, name):
491 repo = self._factory.repo(wire)
491 repo = self._factory.repo(wire)
492 revs = repo.filtered(name).changelog.index
492 revs = repo.filtered(name).changelog.index
493 return map(lambda x: hex(x[7]), revs)[:-1]
493 return map(lambda x: hex(x[7]), revs)[:-1]
494
494
495 @reraise_safe_exceptions
495 @reraise_safe_exceptions
496 def get_config_value(self, wire, section, name, untrusted=False):
496 def get_config_value(self, wire, section, name, untrusted=False):
497 repo = self._factory.repo(wire)
497 repo = self._factory.repo(wire)
498 return repo.ui.config(section, name, untrusted=untrusted)
498 return repo.ui.config(section, name, untrusted=untrusted)
499
499
500 @reraise_safe_exceptions
500 @reraise_safe_exceptions
501 def get_config_bool(self, wire, section, name, untrusted=False):
501 def get_config_bool(self, wire, section, name, untrusted=False):
502 repo = self._factory.repo(wire)
502 repo = self._factory.repo(wire)
503 return repo.ui.configbool(section, name, untrusted=untrusted)
503 return repo.ui.configbool(section, name, untrusted=untrusted)
504
504
505 @reraise_safe_exceptions
505 @reraise_safe_exceptions
506 def get_config_list(self, wire, section, name, untrusted=False):
506 def get_config_list(self, wire, section, name, untrusted=False):
507 repo = self._factory.repo(wire)
507 repo = self._factory.repo(wire)
508 return repo.ui.configlist(section, name, untrusted=untrusted)
508 return repo.ui.configlist(section, name, untrusted=untrusted)
509
509
510 @reraise_safe_exceptions
510 @reraise_safe_exceptions
511 def is_large_file(self, wire, path):
511 def is_large_file(self, wire, path):
512 return largefiles.lfutil.isstandin(path)
512 return largefiles.lfutil.isstandin(path)
513
513
514 @reraise_safe_exceptions
514 @reraise_safe_exceptions
515 def in_largefiles_store(self, wire, sha):
515 def in_largefiles_store(self, wire, sha):
516 repo = self._factory.repo(wire)
516 repo = self._factory.repo(wire)
517 return largefiles.lfutil.instore(repo, sha)
517 return largefiles.lfutil.instore(repo, sha)
518
518
519 @reraise_safe_exceptions
519 @reraise_safe_exceptions
520 def in_user_cache(self, wire, sha):
520 def in_user_cache(self, wire, sha):
521 repo = self._factory.repo(wire)
521 repo = self._factory.repo(wire)
522 return largefiles.lfutil.inusercache(repo.ui, sha)
522 return largefiles.lfutil.inusercache(repo.ui, sha)
523
523
524 @reraise_safe_exceptions
524 @reraise_safe_exceptions
525 def store_path(self, wire, sha):
525 def store_path(self, wire, sha):
526 repo = self._factory.repo(wire)
526 repo = self._factory.repo(wire)
527 return largefiles.lfutil.storepath(repo, sha)
527 return largefiles.lfutil.storepath(repo, sha)
528
528
529 @reraise_safe_exceptions
529 @reraise_safe_exceptions
530 def link(self, wire, sha, path):
530 def link(self, wire, sha, path):
531 repo = self._factory.repo(wire)
531 repo = self._factory.repo(wire)
532 largefiles.lfutil.link(
532 largefiles.lfutil.link(
533 largefiles.lfutil.usercachepath(repo.ui, sha), path)
533 largefiles.lfutil.usercachepath(repo.ui, sha), path)
534
534
535 @reraise_safe_exceptions
535 @reraise_safe_exceptions
536 def localrepository(self, wire, create=False):
536 def localrepository(self, wire, create=False):
537 self._factory.repo(wire, create=create)
537 self._factory.repo(wire, create=create)
538
538
539 @reraise_safe_exceptions
539 @reraise_safe_exceptions
540 def lookup(self, wire, revision, both):
540 def lookup(self, wire, revision, both):
541
541
542 repo = self._factory.repo(wire)
542 repo = self._factory.repo(wire)
543
543
544 if isinstance(revision, int):
544 if isinstance(revision, int):
545 # NOTE(marcink):
545 # NOTE(marcink):
546 # since Mercurial doesn't support indexes properly
546 # since Mercurial doesn't support indexes properly
547 # we need to shift accordingly by one to get proper index, e.g
547 # we need to shift accordingly by one to get proper index, e.g
548 # repo[-1] => repo[-2]
548 # repo[-1] => repo[-2]
549 # repo[0] => repo[-1]
549 # repo[0] => repo[-1]
550 # repo[1] => repo[2] we also never call repo[0] because
550 # repo[1] => repo[2] we also never call repo[0] because
551 # it's actually second commit
551 # it's actually second commit
552 if revision <= 0:
552 if revision <= 0:
553 revision = revision + -1
553 revision = revision + -1
554 else:
554 else:
555 revision = revision + 1
555 revision = revision + 1
556
556
557 try:
557 try:
558 ctx = repo[revision]
558 ctx = repo[revision]
559 except RepoLookupError as e:
559 except RepoLookupError as e:
560 raise exceptions.LookupException(e)(revision)
560 raise exceptions.LookupException(e)(revision)
561 except LookupError as e:
561 except LookupError as e:
562 raise exceptions.LookupException(e)(e.name)
562 raise exceptions.LookupException(e)(e.name)
563
563
564 if not both:
564 if not both:
565 return ctx.hex()
565 return ctx.hex()
566
566
567 ctx = repo[ctx.hex()]
567 ctx = repo[ctx.hex()]
568 return ctx.hex(), ctx.rev()
568 return ctx.hex(), ctx.rev()
569
569
570 @reraise_safe_exceptions
570 @reraise_safe_exceptions
571 def pull(self, wire, url, commit_ids=None):
571 def pull(self, wire, url, commit_ids=None):
572 repo = self._factory.repo(wire)
572 repo = self._factory.repo(wire)
573 # Disable any prompts for this repo
573 # Disable any prompts for this repo
574 repo.ui.setconfig('ui', 'interactive', 'off', '-y')
574 repo.ui.setconfig('ui', 'interactive', 'off', '-y')
575
575
576 remote = peer(repo, {}, url)
576 remote = peer(repo, {}, url)
577 # Disable any prompts for this remote
577 # Disable any prompts for this remote
578 remote.ui.setconfig('ui', 'interactive', 'off', '-y')
578 remote.ui.setconfig('ui', 'interactive', 'off', '-y')
579
579
580 if commit_ids:
580 if commit_ids:
581 commit_ids = [bin(commit_id) for commit_id in commit_ids]
581 commit_ids = [bin(commit_id) for commit_id in commit_ids]
582
582
583 return exchange.pull(
583 return exchange.pull(
584 repo, remote, heads=commit_ids, force=None).cgresult
584 repo, remote, heads=commit_ids, force=None).cgresult
585
585
586 @reraise_safe_exceptions
586 @reraise_safe_exceptions
587 def sync_push(self, wire, url):
587 def sync_push(self, wire, url):
588 if not self.check_url(url, wire['config']):
588 if not self.check_url(url, wire['config']):
589 return
589 return
590
590
591 repo = self._factory.repo(wire)
591 repo = self._factory.repo(wire)
592
592
593 # Disable any prompts for this repo
593 # Disable any prompts for this repo
594 repo.ui.setconfig('ui', 'interactive', 'off', '-y')
594 repo.ui.setconfig('ui', 'interactive', 'off', '-y')
595
595
596 bookmarks = dict(repo._bookmarks).keys()
596 bookmarks = dict(repo._bookmarks).keys()
597 remote = peer(repo, {}, url)
597 remote = peer(repo, {}, url)
598 # Disable any prompts for this remote
598 # Disable any prompts for this remote
599 remote.ui.setconfig('ui', 'interactive', 'off', '-y')
599 remote.ui.setconfig('ui', 'interactive', 'off', '-y')
600
600
601 return exchange.push(
601 return exchange.push(
602 repo, remote, newbranch=True, bookmarks=bookmarks).cgresult
602 repo, remote, newbranch=True, bookmarks=bookmarks).cgresult
603
603
604 @reraise_safe_exceptions
604 @reraise_safe_exceptions
605 def revision(self, wire, rev):
605 def revision(self, wire, rev):
606 repo = self._factory.repo(wire)
606 repo = self._factory.repo(wire)
607 ctx = repo[rev]
607 ctx = repo[rev]
608 return ctx.rev()
608 return ctx.rev()
609
609
610 @reraise_safe_exceptions
610 @reraise_safe_exceptions
611 def rev_range(self, wire, filter):
611 def rev_range(self, wire, filter):
612 repo = self._factory.repo(wire)
612 repo = self._factory.repo(wire)
613 revisions = [rev for rev in revrange(repo, filter)]
613 revisions = [rev for rev in revrange(repo, filter)]
614 return revisions
614 return revisions
615
615
616 @reraise_safe_exceptions
616 @reraise_safe_exceptions
617 def rev_range_hash(self, wire, node):
617 def rev_range_hash(self, wire, node):
618 repo = self._factory.repo(wire)
618 repo = self._factory.repo(wire)
619
619
620 def get_revs(repo, rev_opt):
620 def get_revs(repo, rev_opt):
621 if rev_opt:
621 if rev_opt:
622 revs = revrange(repo, rev_opt)
622 revs = revrange(repo, rev_opt)
623 if len(revs) == 0:
623 if len(revs) == 0:
624 return (nullrev, nullrev)
624 return (nullrev, nullrev)
625 return max(revs), min(revs)
625 return max(revs), min(revs)
626 else:
626 else:
627 return len(repo) - 1, 0
627 return len(repo) - 1, 0
628
628
629 stop, start = get_revs(repo, [node + ':'])
629 stop, start = get_revs(repo, [node + ':'])
630 revs = [hex(repo[r].node()) for r in xrange(start, stop + 1)]
630 revs = [hex(repo[r].node()) for r in xrange(start, stop + 1)]
631 return revs
631 return revs
632
632
633 @reraise_safe_exceptions
633 @reraise_safe_exceptions
634 def revs_from_revspec(self, wire, rev_spec, *args, **kwargs):
634 def revs_from_revspec(self, wire, rev_spec, *args, **kwargs):
635 other_path = kwargs.pop('other_path', None)
635 other_path = kwargs.pop('other_path', None)
636
636
637 # case when we want to compare two independent repositories
637 # case when we want to compare two independent repositories
638 if other_path and other_path != wire["path"]:
638 if other_path and other_path != wire["path"]:
639 baseui = self._factory._create_config(wire["config"])
639 baseui = self._factory._create_config(wire["config"])
640 repo = unionrepo.unionrepository(baseui, other_path, wire["path"])
640 repo = unionrepo.unionrepository(baseui, other_path, wire["path"])
641 else:
641 else:
642 repo = self._factory.repo(wire)
642 repo = self._factory.repo(wire)
643 return list(repo.revs(rev_spec, *args))
643 return list(repo.revs(rev_spec, *args))
644
644
645 @reraise_safe_exceptions
645 @reraise_safe_exceptions
646 def strip(self, wire, revision, update, backup):
646 def strip(self, wire, revision, update, backup):
647 repo = self._factory.repo(wire)
647 repo = self._factory.repo(wire)
648 ctx = repo[revision]
648 ctx = repo[revision]
649 hgext_strip(
649 hgext_strip(
650 repo.baseui, repo, ctx.node(), update=update, backup=backup)
650 repo.baseui, repo, ctx.node(), update=update, backup=backup)
651
651
652 @reraise_safe_exceptions
652 @reraise_safe_exceptions
653 def verify(self, wire,):
653 def verify(self, wire,):
654 repo = self._factory.repo(wire)
654 repo = self._factory.repo(wire)
655 baseui = self._factory._create_config(wire['config'])
655 baseui = self._factory._create_config(wire['config'])
656 baseui.setconfig('ui', 'quiet', 'false')
656 baseui.setconfig('ui', 'quiet', 'false')
657 output = io.BytesIO()
657 output = io.BytesIO()
658
658
659 def write(data, **unused_kwargs):
659 def write(data, **unused_kwargs):
660 output.write(data)
660 output.write(data)
661 baseui.write = write
661 baseui.write = write
662
662
663 repo.ui = baseui
663 repo.ui = baseui
664 verify.verify(repo)
664 verify.verify(repo)
665 return output.getvalue()
665 return output.getvalue()
666
666
667 @reraise_safe_exceptions
667 @reraise_safe_exceptions
668 def tag(self, wire, name, revision, message, local, user,
668 def tag(self, wire, name, revision, message, local, user,
669 tag_time, tag_timezone):
669 tag_time, tag_timezone):
670 repo = self._factory.repo(wire)
670 repo = self._factory.repo(wire)
671 ctx = repo[revision]
671 ctx = repo[revision]
672 node = ctx.node()
672 node = ctx.node()
673
673
674 date = (tag_time, tag_timezone)
674 date = (tag_time, tag_timezone)
675 try:
675 try:
676 hg_tag.tag(repo, name, node, message, local, user, date)
676 hg_tag.tag(repo, name, node, message, local, user, date)
677 except Abort as e:
677 except Abort as e:
678 log.exception("Tag operation aborted")
678 log.exception("Tag operation aborted")
679 # Exception can contain unicode which we convert
679 # Exception can contain unicode which we convert
680 raise exceptions.AbortException(e)(repr(e))
680 raise exceptions.AbortException(e)(repr(e))
681
681
682 @reraise_safe_exceptions
682 @reraise_safe_exceptions
683 def tags(self, wire):
683 def tags(self, wire):
684 repo = self._factory.repo(wire)
684 repo = self._factory.repo(wire)
685 return repo.tags()
685 return repo.tags()
686
686
687 @reraise_safe_exceptions
687 @reraise_safe_exceptions
688 def update(self, wire, node=None, clean=False):
688 def update(self, wire, node=None, clean=False):
689 repo = self._factory.repo(wire)
689 repo = self._factory.repo(wire)
690 baseui = self._factory._create_config(wire['config'])
690 baseui = self._factory._create_config(wire['config'])
691 commands.update(baseui, repo, node=node, clean=clean)
691 commands.update(baseui, repo, node=node, clean=clean)
692
692
693 @reraise_safe_exceptions
693 @reraise_safe_exceptions
694 def identify(self, wire):
694 def identify(self, wire):
695 repo = self._factory.repo(wire)
695 repo = self._factory.repo(wire)
696 baseui = self._factory._create_config(wire['config'])
696 baseui = self._factory._create_config(wire['config'])
697 output = io.BytesIO()
697 output = io.BytesIO()
698 baseui.write = output.write
698 baseui.write = output.write
699 # This is required to get a full node id
699 # This is required to get a full node id
700 baseui.debugflag = True
700 baseui.debugflag = True
701 commands.identify(baseui, repo, id=True)
701 commands.identify(baseui, repo, id=True)
702
702
703 return output.getvalue()
703 return output.getvalue()
704
704
705 @reraise_safe_exceptions
705 @reraise_safe_exceptions
706 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None,
706 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None,
707 hooks=True):
707 hooks=True):
708 repo = self._factory.repo(wire)
708 repo = self._factory.repo(wire)
709 baseui = self._factory._create_config(wire['config'], hooks=hooks)
709 baseui = self._factory._create_config(wire['config'], hooks=hooks)
710
710
711 # Mercurial internally has a lot of logic that checks ONLY if
711 # Mercurial internally has a lot of logic that checks ONLY if
712 # option is defined, we just pass those if they are defined then
712 # option is defined, we just pass those if they are defined then
713 opts = {}
713 opts = {}
714 if bookmark:
714 if bookmark:
715 opts['bookmark'] = bookmark
715 opts['bookmark'] = bookmark
716 if branch:
716 if branch:
717 opts['branch'] = branch
717 opts['branch'] = branch
718 if revision:
718 if revision:
719 opts['rev'] = revision
719 opts['rev'] = revision
720
720
721 commands.pull(baseui, repo, source, **opts)
721 commands.pull(baseui, repo, source, **opts)
722
722
723 @reraise_safe_exceptions
723 @reraise_safe_exceptions
724 def heads(self, wire, branch=None):
724 def heads(self, wire, branch=None):
725 repo = self._factory.repo(wire)
725 repo = self._factory.repo(wire)
726 baseui = self._factory._create_config(wire['config'])
726 baseui = self._factory._create_config(wire['config'])
727 output = io.BytesIO()
727 output = io.BytesIO()
728
728
729 def write(data, **unused_kwargs):
729 def write(data, **unused_kwargs):
730 output.write(data)
730 output.write(data)
731
731
732 baseui.write = write
732 baseui.write = write
733 if branch:
733 if branch:
734 args = [branch]
734 args = [branch]
735 else:
735 else:
736 args = []
736 args = []
737 commands.heads(baseui, repo, template='{node} ', *args)
737 commands.heads(baseui, repo, template='{node} ', *args)
738
738
739 return output.getvalue()
739 return output.getvalue()
740
740
741 @reraise_safe_exceptions
741 @reraise_safe_exceptions
742 def ancestor(self, wire, revision1, revision2):
742 def ancestor(self, wire, revision1, revision2):
743 repo = self._factory.repo(wire)
743 repo = self._factory.repo(wire)
744 changelog = repo.changelog
744 changelog = repo.changelog
745 lookup = repo.lookup
745 lookup = repo.lookup
746 a = changelog.ancestor(lookup(revision1), lookup(revision2))
746 a = changelog.ancestor(lookup(revision1), lookup(revision2))
747 return hex(a)
747 return hex(a)
748
748
749 @reraise_safe_exceptions
749 @reraise_safe_exceptions
750 def push(self, wire, revisions, dest_path, hooks=True,
750 def push(self, wire, revisions, dest_path, hooks=True,
751 push_branches=False):
751 push_branches=False):
752 repo = self._factory.repo(wire)
752 repo = self._factory.repo(wire)
753 baseui = self._factory._create_config(wire['config'], hooks=hooks)
753 baseui = self._factory._create_config(wire['config'], hooks=hooks)
754 commands.push(baseui, repo, dest=dest_path, rev=revisions,
754 commands.push(baseui, repo, dest=dest_path, rev=revisions,
755 new_branch=push_branches)
755 new_branch=push_branches)
756
756
757 @reraise_safe_exceptions
757 @reraise_safe_exceptions
758 def merge(self, wire, revision):
758 def merge(self, wire, revision):
759 repo = self._factory.repo(wire)
759 repo = self._factory.repo(wire)
760 baseui = self._factory._create_config(wire['config'])
760 baseui = self._factory._create_config(wire['config'])
761 repo.ui.setconfig('ui', 'merge', 'internal:dump')
761 repo.ui.setconfig('ui', 'merge', 'internal:dump')
762
762
763 # In case of sub repositories are used mercurial prompts the user in
763 # In case of sub repositories are used mercurial prompts the user in
764 # case of merge conflicts or different sub repository sources. By
764 # case of merge conflicts or different sub repository sources. By
765 # setting the interactive flag to `False` mercurial doesn't prompt the
765 # setting the interactive flag to `False` mercurial doesn't prompt the
766 # used but instead uses a default value.
766 # used but instead uses a default value.
767 repo.ui.setconfig('ui', 'interactive', False)
767 repo.ui.setconfig('ui', 'interactive', False)
768
768
769 commands.merge(baseui, repo, rev=revision)
769 commands.merge(baseui, repo, rev=revision)
770
770
771 @reraise_safe_exceptions
771 @reraise_safe_exceptions
772 def commit(self, wire, message, username, close_branch=False):
772 def commit(self, wire, message, username, close_branch=False):
773 repo = self._factory.repo(wire)
773 repo = self._factory.repo(wire)
774 baseui = self._factory._create_config(wire['config'])
774 baseui = self._factory._create_config(wire['config'])
775 repo.ui.setconfig('ui', 'username', username)
775 repo.ui.setconfig('ui', 'username', username)
776 commands.commit(baseui, repo, message=message, close_branch=close_branch)
776 commands.commit(baseui, repo, message=message, close_branch=close_branch)
777
777
778 @reraise_safe_exceptions
778 @reraise_safe_exceptions
779 def rebase(self, wire, source=None, dest=None, abort=False):
779 def rebase(self, wire, source=None, dest=None, abort=False):
780 repo = self._factory.repo(wire)
780 repo = self._factory.repo(wire)
781 baseui = self._factory._create_config(wire['config'])
781 baseui = self._factory._create_config(wire['config'])
782 repo.ui.setconfig('ui', 'merge', 'internal:dump')
782 repo.ui.setconfig('ui', 'merge', 'internal:dump')
783 rebase.rebase(
783 rebase.rebase(
784 baseui, repo, base=source, dest=dest, abort=abort, keep=not abort)
784 baseui, repo, base=source, dest=dest, abort=abort, keep=not abort)
785
785
786 @reraise_safe_exceptions
786 @reraise_safe_exceptions
787 def bookmark(self, wire, bookmark, revision=None):
787 def bookmark(self, wire, bookmark, revision=None):
788 repo = self._factory.repo(wire)
788 repo = self._factory.repo(wire)
789 baseui = self._factory._create_config(wire['config'])
789 baseui = self._factory._create_config(wire['config'])
790 commands.bookmark(baseui, repo, bookmark, rev=revision, force=True)
790 commands.bookmark(baseui, repo, bookmark, rev=revision, force=True)
791
791
792 @reraise_safe_exceptions
792 @reraise_safe_exceptions
793 def install_hooks(self, wire, force=False):
793 def install_hooks(self, wire, force=False):
794 # we don't need any special hooks for Mercurial
794 # we don't need any special hooks for Mercurial
795 pass
795 pass
@@ -1,154 +1,154 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # RhodeCode VCSServer provides access to different vcs backends via network.
3 # RhodeCode VCSServer provides access to different vcs backends via network.
4 # Copyright (C) 2014-2018 RhodeCode GmbH
4 # Copyright (C) 2014-2018 RhodeCode GmbH
5 #
5 #
6 # This program is free software; you can redistribute it and/or modify
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
19
20 import re
20 import re
21 import os
21 import os
22 import sys
22 import sys
23 import datetime
23 import datetime
24 import logging
24 import logging
25 import pkg_resources
25 import pkg_resources
26
26
27 import vcsserver
27 import vcsserver
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31
31
32 def install_git_hooks(repo_path, bare, executable=None, force_create=False):
32 def install_git_hooks(repo_path, bare, executable=None, force_create=False):
33 """
33 """
34 Creates a RhodeCode hook inside a git repository
34 Creates a RhodeCode hook inside a git repository
35
35
36 :param repo_path: path to repository
36 :param repo_path: path to repository
37 :param executable: binary executable to put in the hooks
37 :param executable: binary executable to put in the hooks
38 :param force_create: Create even if same name hook exists
38 :param force_create: Create even if same name hook exists
39 """
39 """
40 executable = executable or sys.executable
40 executable = executable or sys.executable
41 hooks_path = os.path.join(repo_path, 'hooks')
41 hooks_path = os.path.join(repo_path, 'hooks')
42 if not bare:
42 if not bare:
43 hooks_path = os.path.join(repo_path, '.git', 'hooks')
43 hooks_path = os.path.join(repo_path, '.git', 'hooks')
44 if not os.path.isdir(hooks_path):
44 if not os.path.isdir(hooks_path):
45 os.makedirs(hooks_path, mode=0777)
45 os.makedirs(hooks_path, mode=0o777)
46
46
47 tmpl_post = pkg_resources.resource_string(
47 tmpl_post = pkg_resources.resource_string(
48 'vcsserver', '/'.join(
48 'vcsserver', '/'.join(
49 ('hook_utils', 'hook_templates', 'git_post_receive.py.tmpl')))
49 ('hook_utils', 'hook_templates', 'git_post_receive.py.tmpl')))
50 tmpl_pre = pkg_resources.resource_string(
50 tmpl_pre = pkg_resources.resource_string(
51 'vcsserver', '/'.join(
51 'vcsserver', '/'.join(
52 ('hook_utils', 'hook_templates', 'git_pre_receive.py.tmpl')))
52 ('hook_utils', 'hook_templates', 'git_pre_receive.py.tmpl')))
53
53
54 path = '' # not used for now
54 path = '' # not used for now
55 timestamp = datetime.datetime.utcnow().isoformat()
55 timestamp = datetime.datetime.utcnow().isoformat()
56
56
57 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
57 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
58 log.debug('Installing git hook in repo %s', repo_path)
58 log.debug('Installing git hook in repo %s', repo_path)
59 _hook_file = os.path.join(hooks_path, '%s-receive' % h_type)
59 _hook_file = os.path.join(hooks_path, '%s-receive' % h_type)
60 _rhodecode_hook = check_rhodecode_hook(_hook_file)
60 _rhodecode_hook = check_rhodecode_hook(_hook_file)
61
61
62 if _rhodecode_hook or force_create:
62 if _rhodecode_hook or force_create:
63 log.debug('writing git %s hook file at %s !', h_type, _hook_file)
63 log.debug('writing git %s hook file at %s !', h_type, _hook_file)
64 try:
64 try:
65 with open(_hook_file, 'wb') as f:
65 with open(_hook_file, 'wb') as f:
66 template = template.replace(
66 template = template.replace(
67 '_TMPL_', vcsserver.__version__)
67 '_TMPL_', vcsserver.__version__)
68 template = template.replace('_DATE_', timestamp)
68 template = template.replace('_DATE_', timestamp)
69 template = template.replace('_ENV_', executable)
69 template = template.replace('_ENV_', executable)
70 template = template.replace('_PATH_', path)
70 template = template.replace('_PATH_', path)
71 f.write(template)
71 f.write(template)
72 os.chmod(_hook_file, 0755)
72 os.chmod(_hook_file, 0o755)
73 except IOError:
73 except IOError:
74 log.exception('error writing hook file %s', _hook_file)
74 log.exception('error writing hook file %s', _hook_file)
75 else:
75 else:
76 log.debug('skipping writing hook file')
76 log.debug('skipping writing hook file')
77
77
78 return True
78 return True
79
79
80
80
81 def install_svn_hooks(repo_path, executable=None, force_create=False):
81 def install_svn_hooks(repo_path, executable=None, force_create=False):
82 """
82 """
83 Creates RhodeCode hooks inside a svn repository
83 Creates RhodeCode hooks inside a svn repository
84
84
85 :param repo_path: path to repository
85 :param repo_path: path to repository
86 :param executable: binary executable to put in the hooks
86 :param executable: binary executable to put in the hooks
87 :param force_create: Create even if same name hook exists
87 :param force_create: Create even if same name hook exists
88 """
88 """
89 executable = executable or sys.executable
89 executable = executable or sys.executable
90 hooks_path = os.path.join(repo_path, 'hooks')
90 hooks_path = os.path.join(repo_path, 'hooks')
91 if not os.path.isdir(hooks_path):
91 if not os.path.isdir(hooks_path):
92 os.makedirs(hooks_path, mode=0777)
92 os.makedirs(hooks_path, mode=0o777)
93
93
94 tmpl_post = pkg_resources.resource_string(
94 tmpl_post = pkg_resources.resource_string(
95 'vcsserver', '/'.join(
95 'vcsserver', '/'.join(
96 ('hook_utils', 'hook_templates', 'svn_post_commit_hook.py.tmpl')))
96 ('hook_utils', 'hook_templates', 'svn_post_commit_hook.py.tmpl')))
97 tmpl_pre = pkg_resources.resource_string(
97 tmpl_pre = pkg_resources.resource_string(
98 'vcsserver', '/'.join(
98 'vcsserver', '/'.join(
99 ('hook_utils', 'hook_templates', 'svn_pre_commit_hook.py.tmpl')))
99 ('hook_utils', 'hook_templates', 'svn_pre_commit_hook.py.tmpl')))
100
100
101 path = '' # not used for now
101 path = '' # not used for now
102 timestamp = datetime.datetime.utcnow().isoformat()
102 timestamp = datetime.datetime.utcnow().isoformat()
103
103
104 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
104 for h_type, template in [('pre', tmpl_pre), ('post', tmpl_post)]:
105 log.debug('Installing svn hook in repo %s', repo_path)
105 log.debug('Installing svn hook in repo %s', repo_path)
106 _hook_file = os.path.join(hooks_path, '%s-commit' % h_type)
106 _hook_file = os.path.join(hooks_path, '%s-commit' % h_type)
107 _rhodecode_hook = check_rhodecode_hook(_hook_file)
107 _rhodecode_hook = check_rhodecode_hook(_hook_file)
108
108
109 if _rhodecode_hook or force_create:
109 if _rhodecode_hook or force_create:
110 log.debug('writing svn %s hook file at %s !', h_type, _hook_file)
110 log.debug('writing svn %s hook file at %s !', h_type, _hook_file)
111
111
112 try:
112 try:
113 with open(_hook_file, 'wb') as f:
113 with open(_hook_file, 'wb') as f:
114 template = template.replace(
114 template = template.replace(
115 '_TMPL_', vcsserver.__version__)
115 '_TMPL_', vcsserver.__version__)
116 template = template.replace('_DATE_', timestamp)
116 template = template.replace('_DATE_', timestamp)
117 template = template.replace('_ENV_', executable)
117 template = template.replace('_ENV_', executable)
118 template = template.replace('_PATH_', path)
118 template = template.replace('_PATH_', path)
119
119
120 f.write(template)
120 f.write(template)
121 os.chmod(_hook_file, 0755)
121 os.chmod(_hook_file, 0o755)
122 except IOError:
122 except IOError:
123 log.exception('error writing hook file %s', _hook_file)
123 log.exception('error writing hook file %s', _hook_file)
124 else:
124 else:
125 log.debug('skipping writing hook file')
125 log.debug('skipping writing hook file')
126
126
127 return True
127 return True
128
128
129
129
130 def check_rhodecode_hook(hook_path):
130 def check_rhodecode_hook(hook_path):
131 """
131 """
132 Check if the hook was created by RhodeCode
132 Check if the hook was created by RhodeCode
133 """
133 """
134 if not os.path.exists(hook_path):
134 if not os.path.exists(hook_path):
135 return True
135 return True
136
136
137 log.debug('hook exists, checking if it is from rhodecode')
137 log.debug('hook exists, checking if it is from rhodecode')
138 hook_content = read_hook_content(hook_path)
138 hook_content = read_hook_content(hook_path)
139 matches = re.search(r'(?:RC_HOOK_VER)\s*=\s*(.*)', hook_content)
139 matches = re.search(r'(?:RC_HOOK_VER)\s*=\s*(.*)', hook_content)
140 if matches:
140 if matches:
141 try:
141 try:
142 version = matches.groups()[0]
142 version = matches.groups()[0]
143 log.debug('got version %s from hooks.', version)
143 log.debug('got version %s from hooks.', version)
144 return True
144 return True
145 except Exception:
145 except Exception:
146 log.exception("Exception while reading the hook version.")
146 log.exception("Exception while reading the hook version.")
147
147
148 return False
148 return False
149
149
150
150
151 def read_hook_content(hook_path):
151 def read_hook_content(hook_path):
152 with open(hook_path, 'rb') as f:
152 with open(hook_path, 'rb') as f:
153 content = f.read()
153 content = f.read()
154 return content
154 return content
@@ -1,702 +1,711 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # RhodeCode VCSServer provides access to different vcs backends via network.
3 # RhodeCode VCSServer provides access to different vcs backends via network.
4 # Copyright (C) 2014-2018 RhodeCode GmbH
4 # Copyright (C) 2014-2018 RhodeCode GmbH
5 #
5 #
6 # This program is free software; you can redistribute it and/or modify
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
19
20 import io
20 import io
21 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import importlib
25 import importlib
26 import base64
26 import base64
27
27
28 from httplib import HTTPConnection
28 from httplib import HTTPConnection
29
29
30
30
31 import mercurial.scmutil
31 import mercurial.scmutil
32 import mercurial.node
32 import mercurial.node
33 import simplejson as json
33 import simplejson as json
34
34
35 from vcsserver import exceptions, subprocessio, settings
35 from vcsserver import exceptions, subprocessio, settings
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 class HooksHttpClient(object):
40 class HooksHttpClient(object):
41 connection = None
41 connection = None
42
42
43 def __init__(self, hooks_uri):
43 def __init__(self, hooks_uri):
44 self.hooks_uri = hooks_uri
44 self.hooks_uri = hooks_uri
45
45
46 def __call__(self, method, extras):
46 def __call__(self, method, extras):
47 connection = HTTPConnection(self.hooks_uri)
47 connection = HTTPConnection(self.hooks_uri)
48 body = self._serialize(method, extras)
48 body = self._serialize(method, extras)
49 try:
49 try:
50 connection.request('POST', '/', body)
50 connection.request('POST', '/', body)
51 except Exception:
51 except Exception:
52 log.error('Connection failed on %s', connection)
52 log.error('Connection failed on %s', connection)
53 raise
53 raise
54 response = connection.getresponse()
54 response = connection.getresponse()
55 return json.loads(response.read())
55
56 response_data = response.read()
57
58 try:
59 return json.loads(response_data)
60 except Exception:
61 log.exception('Failed to decode hook response json data. '
62 'response_code:%s, raw_data:%s',
63 response.status, response_data)
64 raise
56
65
57 def _serialize(self, hook_name, extras):
66 def _serialize(self, hook_name, extras):
58 data = {
67 data = {
59 'method': hook_name,
68 'method': hook_name,
60 'extras': extras
69 'extras': extras
61 }
70 }
62 return json.dumps(data)
71 return json.dumps(data)
63
72
64
73
65 class HooksDummyClient(object):
74 class HooksDummyClient(object):
66 def __init__(self, hooks_module):
75 def __init__(self, hooks_module):
67 self._hooks_module = importlib.import_module(hooks_module)
76 self._hooks_module = importlib.import_module(hooks_module)
68
77
69 def __call__(self, hook_name, extras):
78 def __call__(self, hook_name, extras):
70 with self._hooks_module.Hooks() as hooks:
79 with self._hooks_module.Hooks() as hooks:
71 return getattr(hooks, hook_name)(extras)
80 return getattr(hooks, hook_name)(extras)
72
81
73
82
74 class RemoteMessageWriter(object):
83 class RemoteMessageWriter(object):
75 """Writer base class."""
84 """Writer base class."""
76 def write(self, message):
85 def write(self, message):
77 raise NotImplementedError()
86 raise NotImplementedError()
78
87
79
88
80 class HgMessageWriter(RemoteMessageWriter):
89 class HgMessageWriter(RemoteMessageWriter):
81 """Writer that knows how to send messages to mercurial clients."""
90 """Writer that knows how to send messages to mercurial clients."""
82
91
83 def __init__(self, ui):
92 def __init__(self, ui):
84 self.ui = ui
93 self.ui = ui
85
94
86 def write(self, message):
95 def write(self, message):
87 # TODO: Check why the quiet flag is set by default.
96 # TODO: Check why the quiet flag is set by default.
88 old = self.ui.quiet
97 old = self.ui.quiet
89 self.ui.quiet = False
98 self.ui.quiet = False
90 self.ui.status(message.encode('utf-8'))
99 self.ui.status(message.encode('utf-8'))
91 self.ui.quiet = old
100 self.ui.quiet = old
92
101
93
102
94 class GitMessageWriter(RemoteMessageWriter):
103 class GitMessageWriter(RemoteMessageWriter):
95 """Writer that knows how to send messages to git clients."""
104 """Writer that knows how to send messages to git clients."""
96
105
97 def __init__(self, stdout=None):
106 def __init__(self, stdout=None):
98 self.stdout = stdout or sys.stdout
107 self.stdout = stdout or sys.stdout
99
108
100 def write(self, message):
109 def write(self, message):
101 self.stdout.write(message.encode('utf-8'))
110 self.stdout.write(message.encode('utf-8'))
102
111
103
112
104 class SvnMessageWriter(RemoteMessageWriter):
113 class SvnMessageWriter(RemoteMessageWriter):
105 """Writer that knows how to send messages to svn clients."""
114 """Writer that knows how to send messages to svn clients."""
106
115
107 def __init__(self, stderr=None):
116 def __init__(self, stderr=None):
108 # SVN needs data sent to stderr for back-to-client messaging
117 # SVN needs data sent to stderr for back-to-client messaging
109 self.stderr = stderr or sys.stderr
118 self.stderr = stderr or sys.stderr
110
119
111 def write(self, message):
120 def write(self, message):
112 self.stderr.write(message.encode('utf-8'))
121 self.stderr.write(message.encode('utf-8'))
113
122
114
123
115 def _handle_exception(result):
124 def _handle_exception(result):
116 exception_class = result.get('exception')
125 exception_class = result.get('exception')
117 exception_traceback = result.get('exception_traceback')
126 exception_traceback = result.get('exception_traceback')
118
127
119 if exception_traceback:
128 if exception_traceback:
120 log.error('Got traceback from remote call:%s', exception_traceback)
129 log.error('Got traceback from remote call:%s', exception_traceback)
121
130
122 if exception_class == 'HTTPLockedRC':
131 if exception_class == 'HTTPLockedRC':
123 raise exceptions.RepositoryLockedException()(*result['exception_args'])
132 raise exceptions.RepositoryLockedException()(*result['exception_args'])
124 elif exception_class == 'HTTPBranchProtected':
133 elif exception_class == 'HTTPBranchProtected':
125 raise exceptions.RepositoryBranchProtectedException()(*result['exception_args'])
134 raise exceptions.RepositoryBranchProtectedException()(*result['exception_args'])
126 elif exception_class == 'RepositoryError':
135 elif exception_class == 'RepositoryError':
127 raise exceptions.VcsException()(*result['exception_args'])
136 raise exceptions.VcsException()(*result['exception_args'])
128 elif exception_class:
137 elif exception_class:
129 raise Exception('Got remote exception "%s" with args "%s"' %
138 raise Exception('Got remote exception "%s" with args "%s"' %
130 (exception_class, result['exception_args']))
139 (exception_class, result['exception_args']))
131
140
132
141
133 def _get_hooks_client(extras):
142 def _get_hooks_client(extras):
134 if 'hooks_uri' in extras:
143 if 'hooks_uri' in extras:
135 protocol = extras.get('hooks_protocol')
144 protocol = extras.get('hooks_protocol')
136 return HooksHttpClient(extras['hooks_uri'])
145 return HooksHttpClient(extras['hooks_uri'])
137 else:
146 else:
138 return HooksDummyClient(extras['hooks_module'])
147 return HooksDummyClient(extras['hooks_module'])
139
148
140
149
141 def _call_hook(hook_name, extras, writer):
150 def _call_hook(hook_name, extras, writer):
142 hooks_client = _get_hooks_client(extras)
151 hooks_client = _get_hooks_client(extras)
143 log.debug('Hooks, using client:%s', hooks_client)
152 log.debug('Hooks, using client:%s', hooks_client)
144 result = hooks_client(hook_name, extras)
153 result = hooks_client(hook_name, extras)
145 log.debug('Hooks got result: %s', result)
154 log.debug('Hooks got result: %s', result)
146
155
147 _handle_exception(result)
156 _handle_exception(result)
148 writer.write(result['output'])
157 writer.write(result['output'])
149
158
150 return result['status']
159 return result['status']
151
160
152
161
153 def _extras_from_ui(ui):
162 def _extras_from_ui(ui):
154 hook_data = ui.config('rhodecode', 'RC_SCM_DATA')
163 hook_data = ui.config('rhodecode', 'RC_SCM_DATA')
155 if not hook_data:
164 if not hook_data:
156 # maybe it's inside environ ?
165 # maybe it's inside environ ?
157 env_hook_data = os.environ.get('RC_SCM_DATA')
166 env_hook_data = os.environ.get('RC_SCM_DATA')
158 if env_hook_data:
167 if env_hook_data:
159 hook_data = env_hook_data
168 hook_data = env_hook_data
160
169
161 extras = {}
170 extras = {}
162 if hook_data:
171 if hook_data:
163 extras = json.loads(hook_data)
172 extras = json.loads(hook_data)
164 return extras
173 return extras
165
174
166
175
167 def _rev_range_hash(repo, node, check_heads=False):
176 def _rev_range_hash(repo, node, check_heads=False):
168
177
169 commits = []
178 commits = []
170 revs = []
179 revs = []
171 start = repo[node].rev()
180 start = repo[node].rev()
172 end = len(repo)
181 end = len(repo)
173 for rev in range(start, end):
182 for rev in range(start, end):
174 revs.append(rev)
183 revs.append(rev)
175 ctx = repo[rev]
184 ctx = repo[rev]
176 commit_id = mercurial.node.hex(ctx.node())
185 commit_id = mercurial.node.hex(ctx.node())
177 branch = ctx.branch()
186 branch = ctx.branch()
178 commits.append((commit_id, branch))
187 commits.append((commit_id, branch))
179
188
180 parent_heads = []
189 parent_heads = []
181 if check_heads:
190 if check_heads:
182 parent_heads = _check_heads(repo, start, end, revs)
191 parent_heads = _check_heads(repo, start, end, revs)
183 return commits, parent_heads
192 return commits, parent_heads
184
193
185
194
186 def _check_heads(repo, start, end, commits):
195 def _check_heads(repo, start, end, commits):
187 changelog = repo.changelog
196 changelog = repo.changelog
188 parents = set()
197 parents = set()
189
198
190 for new_rev in commits:
199 for new_rev in commits:
191 for p in changelog.parentrevs(new_rev):
200 for p in changelog.parentrevs(new_rev):
192 if p == mercurial.node.nullrev:
201 if p == mercurial.node.nullrev:
193 continue
202 continue
194 if p < start:
203 if p < start:
195 parents.add(p)
204 parents.add(p)
196
205
197 for p in parents:
206 for p in parents:
198 branch = repo[p].branch()
207 branch = repo[p].branch()
199 # The heads descending from that parent, on the same branch
208 # The heads descending from that parent, on the same branch
200 parent_heads = set([p])
209 parent_heads = set([p])
201 reachable = set([p])
210 reachable = set([p])
202 for x in xrange(p + 1, end):
211 for x in xrange(p + 1, end):
203 if repo[x].branch() != branch:
212 if repo[x].branch() != branch:
204 continue
213 continue
205 for pp in changelog.parentrevs(x):
214 for pp in changelog.parentrevs(x):
206 if pp in reachable:
215 if pp in reachable:
207 reachable.add(x)
216 reachable.add(x)
208 parent_heads.discard(pp)
217 parent_heads.discard(pp)
209 parent_heads.add(x)
218 parent_heads.add(x)
210 # More than one head? Suggest merging
219 # More than one head? Suggest merging
211 if len(parent_heads) > 1:
220 if len(parent_heads) > 1:
212 return list(parent_heads)
221 return list(parent_heads)
213
222
214 return []
223 return []
215
224
216
225
217 def _get_git_env():
226 def _get_git_env():
218 env = {}
227 env = {}
219 for k, v in os.environ.items():
228 for k, v in os.environ.items():
220 if k.startswith('GIT'):
229 if k.startswith('GIT'):
221 env[k] = v
230 env[k] = v
222
231
223 # serialized version
232 # serialized version
224 return [(k, v) for k, v in env.items()]
233 return [(k, v) for k, v in env.items()]
225
234
226
235
227 def _get_hg_env(old_rev, new_rev, txnid, repo_path):
236 def _get_hg_env(old_rev, new_rev, txnid, repo_path):
228 env = {}
237 env = {}
229 for k, v in os.environ.items():
238 for k, v in os.environ.items():
230 if k.startswith('HG'):
239 if k.startswith('HG'):
231 env[k] = v
240 env[k] = v
232
241
233 env['HG_NODE'] = old_rev
242 env['HG_NODE'] = old_rev
234 env['HG_NODE_LAST'] = new_rev
243 env['HG_NODE_LAST'] = new_rev
235 env['HG_TXNID'] = txnid
244 env['HG_TXNID'] = txnid
236 env['HG_PENDING'] = repo_path
245 env['HG_PENDING'] = repo_path
237
246
238 return [(k, v) for k, v in env.items()]
247 return [(k, v) for k, v in env.items()]
239
248
240
249
241 def repo_size(ui, repo, **kwargs):
250 def repo_size(ui, repo, **kwargs):
242 extras = _extras_from_ui(ui)
251 extras = _extras_from_ui(ui)
243 return _call_hook('repo_size', extras, HgMessageWriter(ui))
252 return _call_hook('repo_size', extras, HgMessageWriter(ui))
244
253
245
254
246 def pre_pull(ui, repo, **kwargs):
255 def pre_pull(ui, repo, **kwargs):
247 extras = _extras_from_ui(ui)
256 extras = _extras_from_ui(ui)
248 return _call_hook('pre_pull', extras, HgMessageWriter(ui))
257 return _call_hook('pre_pull', extras, HgMessageWriter(ui))
249
258
250
259
251 def pre_pull_ssh(ui, repo, **kwargs):
260 def pre_pull_ssh(ui, repo, **kwargs):
252 extras = _extras_from_ui(ui)
261 extras = _extras_from_ui(ui)
253 if extras and extras.get('SSH'):
262 if extras and extras.get('SSH'):
254 return pre_pull(ui, repo, **kwargs)
263 return pre_pull(ui, repo, **kwargs)
255 return 0
264 return 0
256
265
257
266
258 def post_pull(ui, repo, **kwargs):
267 def post_pull(ui, repo, **kwargs):
259 extras = _extras_from_ui(ui)
268 extras = _extras_from_ui(ui)
260 return _call_hook('post_pull', extras, HgMessageWriter(ui))
269 return _call_hook('post_pull', extras, HgMessageWriter(ui))
261
270
262
271
263 def post_pull_ssh(ui, repo, **kwargs):
272 def post_pull_ssh(ui, repo, **kwargs):
264 extras = _extras_from_ui(ui)
273 extras = _extras_from_ui(ui)
265 if extras and extras.get('SSH'):
274 if extras and extras.get('SSH'):
266 return post_pull(ui, repo, **kwargs)
275 return post_pull(ui, repo, **kwargs)
267 return 0
276 return 0
268
277
269
278
270 def pre_push(ui, repo, node=None, **kwargs):
279 def pre_push(ui, repo, node=None, **kwargs):
271 """
280 """
272 Mercurial pre_push hook
281 Mercurial pre_push hook
273 """
282 """
274 extras = _extras_from_ui(ui)
283 extras = _extras_from_ui(ui)
275 detect_force_push = extras.get('detect_force_push')
284 detect_force_push = extras.get('detect_force_push')
276
285
277 rev_data = []
286 rev_data = []
278 if node and kwargs.get('hooktype') == 'pretxnchangegroup':
287 if node and kwargs.get('hooktype') == 'pretxnchangegroup':
279 branches = collections.defaultdict(list)
288 branches = collections.defaultdict(list)
280 commits, _heads = _rev_range_hash(repo, node, check_heads=detect_force_push)
289 commits, _heads = _rev_range_hash(repo, node, check_heads=detect_force_push)
281 for commit_id, branch in commits:
290 for commit_id, branch in commits:
282 branches[branch].append(commit_id)
291 branches[branch].append(commit_id)
283
292
284 for branch, commits in branches.items():
293 for branch, commits in branches.items():
285 old_rev = kwargs.get('node_last') or commits[0]
294 old_rev = kwargs.get('node_last') or commits[0]
286 rev_data.append({
295 rev_data.append({
287 'total_commits': len(commits),
296 'total_commits': len(commits),
288 'old_rev': old_rev,
297 'old_rev': old_rev,
289 'new_rev': commits[-1],
298 'new_rev': commits[-1],
290 'ref': '',
299 'ref': '',
291 'type': 'branch',
300 'type': 'branch',
292 'name': branch,
301 'name': branch,
293 })
302 })
294
303
295 for push_ref in rev_data:
304 for push_ref in rev_data:
296 push_ref['multiple_heads'] = _heads
305 push_ref['multiple_heads'] = _heads
297
306
298 repo_path = os.path.join(
307 repo_path = os.path.join(
299 extras.get('repo_store', ''), extras.get('repository', ''))
308 extras.get('repo_store', ''), extras.get('repository', ''))
300 push_ref['hg_env'] = _get_hg_env(
309 push_ref['hg_env'] = _get_hg_env(
301 old_rev=push_ref['old_rev'],
310 old_rev=push_ref['old_rev'],
302 new_rev=push_ref['new_rev'], txnid=kwargs.get('txnid'),
311 new_rev=push_ref['new_rev'], txnid=kwargs.get('txnid'),
303 repo_path=repo_path)
312 repo_path=repo_path)
304
313
305 extras['hook_type'] = kwargs.get('hooktype', 'pre_push')
314 extras['hook_type'] = kwargs.get('hooktype', 'pre_push')
306 extras['commit_ids'] = rev_data
315 extras['commit_ids'] = rev_data
307
316
308 return _call_hook('pre_push', extras, HgMessageWriter(ui))
317 return _call_hook('pre_push', extras, HgMessageWriter(ui))
309
318
310
319
311 def pre_push_ssh(ui, repo, node=None, **kwargs):
320 def pre_push_ssh(ui, repo, node=None, **kwargs):
312 extras = _extras_from_ui(ui)
321 extras = _extras_from_ui(ui)
313 if extras.get('SSH'):
322 if extras.get('SSH'):
314 return pre_push(ui, repo, node, **kwargs)
323 return pre_push(ui, repo, node, **kwargs)
315
324
316 return 0
325 return 0
317
326
318
327
319 def pre_push_ssh_auth(ui, repo, node=None, **kwargs):
328 def pre_push_ssh_auth(ui, repo, node=None, **kwargs):
320 """
329 """
321 Mercurial pre_push hook for SSH
330 Mercurial pre_push hook for SSH
322 """
331 """
323 extras = _extras_from_ui(ui)
332 extras = _extras_from_ui(ui)
324 if extras.get('SSH'):
333 if extras.get('SSH'):
325 permission = extras['SSH_PERMISSIONS']
334 permission = extras['SSH_PERMISSIONS']
326
335
327 if 'repository.write' == permission or 'repository.admin' == permission:
336 if 'repository.write' == permission or 'repository.admin' == permission:
328 return 0
337 return 0
329
338
330 # non-zero ret code
339 # non-zero ret code
331 return 1
340 return 1
332
341
333 return 0
342 return 0
334
343
335
344
336 def post_push(ui, repo, node, **kwargs):
345 def post_push(ui, repo, node, **kwargs):
337 """
346 """
338 Mercurial post_push hook
347 Mercurial post_push hook
339 """
348 """
340 extras = _extras_from_ui(ui)
349 extras = _extras_from_ui(ui)
341
350
342 commit_ids = []
351 commit_ids = []
343 branches = []
352 branches = []
344 bookmarks = []
353 bookmarks = []
345 tags = []
354 tags = []
346
355
347 commits, _heads = _rev_range_hash(repo, node)
356 commits, _heads = _rev_range_hash(repo, node)
348 for commit_id, branch in commits:
357 for commit_id, branch in commits:
349 commit_ids.append(commit_id)
358 commit_ids.append(commit_id)
350 if branch not in branches:
359 if branch not in branches:
351 branches.append(branch)
360 branches.append(branch)
352
361
353 if hasattr(ui, '_rc_pushkey_branches'):
362 if hasattr(ui, '_rc_pushkey_branches'):
354 bookmarks = ui._rc_pushkey_branches
363 bookmarks = ui._rc_pushkey_branches
355
364
356 extras['hook_type'] = kwargs.get('hooktype', 'post_push')
365 extras['hook_type'] = kwargs.get('hooktype', 'post_push')
357 extras['commit_ids'] = commit_ids
366 extras['commit_ids'] = commit_ids
358 extras['new_refs'] = {
367 extras['new_refs'] = {
359 'branches': branches,
368 'branches': branches,
360 'bookmarks': bookmarks,
369 'bookmarks': bookmarks,
361 'tags': tags
370 'tags': tags
362 }
371 }
363
372
364 return _call_hook('post_push', extras, HgMessageWriter(ui))
373 return _call_hook('post_push', extras, HgMessageWriter(ui))
365
374
366
375
367 def post_push_ssh(ui, repo, node, **kwargs):
376 def post_push_ssh(ui, repo, node, **kwargs):
368 """
377 """
369 Mercurial post_push hook for SSH
378 Mercurial post_push hook for SSH
370 """
379 """
371 if _extras_from_ui(ui).get('SSH'):
380 if _extras_from_ui(ui).get('SSH'):
372 return post_push(ui, repo, node, **kwargs)
381 return post_push(ui, repo, node, **kwargs)
373 return 0
382 return 0
374
383
375
384
376 def key_push(ui, repo, **kwargs):
385 def key_push(ui, repo, **kwargs):
377 if kwargs['new'] != '0' and kwargs['namespace'] == 'bookmarks':
386 if kwargs['new'] != '0' and kwargs['namespace'] == 'bookmarks':
378 # store new bookmarks in our UI object propagated later to post_push
387 # store new bookmarks in our UI object propagated later to post_push
379 ui._rc_pushkey_branches = repo[kwargs['key']].bookmarks()
388 ui._rc_pushkey_branches = repo[kwargs['key']].bookmarks()
380 return
389 return
381
390
382
391
383 # backward compat
392 # backward compat
384 log_pull_action = post_pull
393 log_pull_action = post_pull
385
394
386 # backward compat
395 # backward compat
387 log_push_action = post_push
396 log_push_action = post_push
388
397
389
398
390 def handle_git_pre_receive(unused_repo_path, unused_revs, unused_env):
399 def handle_git_pre_receive(unused_repo_path, unused_revs, unused_env):
391 """
400 """
392 Old hook name: keep here for backward compatibility.
401 Old hook name: keep here for backward compatibility.
393
402
394 This is only required when the installed git hooks are not upgraded.
403 This is only required when the installed git hooks are not upgraded.
395 """
404 """
396 pass
405 pass
397
406
398
407
399 def handle_git_post_receive(unused_repo_path, unused_revs, unused_env):
408 def handle_git_post_receive(unused_repo_path, unused_revs, unused_env):
400 """
409 """
401 Old hook name: keep here for backward compatibility.
410 Old hook name: keep here for backward compatibility.
402
411
403 This is only required when the installed git hooks are not upgraded.
412 This is only required when the installed git hooks are not upgraded.
404 """
413 """
405 pass
414 pass
406
415
407
416
408 HookResponse = collections.namedtuple('HookResponse', ('status', 'output'))
417 HookResponse = collections.namedtuple('HookResponse', ('status', 'output'))
409
418
410
419
411 def git_pre_pull(extras):
420 def git_pre_pull(extras):
412 """
421 """
413 Pre pull hook.
422 Pre pull hook.
414
423
415 :param extras: dictionary containing the keys defined in simplevcs
424 :param extras: dictionary containing the keys defined in simplevcs
416 :type extras: dict
425 :type extras: dict
417
426
418 :return: status code of the hook. 0 for success.
427 :return: status code of the hook. 0 for success.
419 :rtype: int
428 :rtype: int
420 """
429 """
421 if 'pull' not in extras['hooks']:
430 if 'pull' not in extras['hooks']:
422 return HookResponse(0, '')
431 return HookResponse(0, '')
423
432
424 stdout = io.BytesIO()
433 stdout = io.BytesIO()
425 try:
434 try:
426 status = _call_hook('pre_pull', extras, GitMessageWriter(stdout))
435 status = _call_hook('pre_pull', extras, GitMessageWriter(stdout))
427 except Exception as error:
436 except Exception as error:
428 status = 128
437 status = 128
429 stdout.write('ERROR: %s\n' % str(error))
438 stdout.write('ERROR: %s\n' % str(error))
430
439
431 return HookResponse(status, stdout.getvalue())
440 return HookResponse(status, stdout.getvalue())
432
441
433
442
434 def git_post_pull(extras):
443 def git_post_pull(extras):
435 """
444 """
436 Post pull hook.
445 Post pull hook.
437
446
438 :param extras: dictionary containing the keys defined in simplevcs
447 :param extras: dictionary containing the keys defined in simplevcs
439 :type extras: dict
448 :type extras: dict
440
449
441 :return: status code of the hook. 0 for success.
450 :return: status code of the hook. 0 for success.
442 :rtype: int
451 :rtype: int
443 """
452 """
444 if 'pull' not in extras['hooks']:
453 if 'pull' not in extras['hooks']:
445 return HookResponse(0, '')
454 return HookResponse(0, '')
446
455
447 stdout = io.BytesIO()
456 stdout = io.BytesIO()
448 try:
457 try:
449 status = _call_hook('post_pull', extras, GitMessageWriter(stdout))
458 status = _call_hook('post_pull', extras, GitMessageWriter(stdout))
450 except Exception as error:
459 except Exception as error:
451 status = 128
460 status = 128
452 stdout.write('ERROR: %s\n' % error)
461 stdout.write('ERROR: %s\n' % error)
453
462
454 return HookResponse(status, stdout.getvalue())
463 return HookResponse(status, stdout.getvalue())
455
464
456
465
457 def _parse_git_ref_lines(revision_lines):
466 def _parse_git_ref_lines(revision_lines):
458 rev_data = []
467 rev_data = []
459 for revision_line in revision_lines or []:
468 for revision_line in revision_lines or []:
460 old_rev, new_rev, ref = revision_line.strip().split(' ')
469 old_rev, new_rev, ref = revision_line.strip().split(' ')
461 ref_data = ref.split('/', 2)
470 ref_data = ref.split('/', 2)
462 if ref_data[1] in ('tags', 'heads'):
471 if ref_data[1] in ('tags', 'heads'):
463 rev_data.append({
472 rev_data.append({
464 # NOTE(marcink):
473 # NOTE(marcink):
465 # we're unable to tell total_commits for git at this point
474 # we're unable to tell total_commits for git at this point
466 # but we set the variable for consistency with GIT
475 # but we set the variable for consistency with GIT
467 'total_commits': -1,
476 'total_commits': -1,
468 'old_rev': old_rev,
477 'old_rev': old_rev,
469 'new_rev': new_rev,
478 'new_rev': new_rev,
470 'ref': ref,
479 'ref': ref,
471 'type': ref_data[1],
480 'type': ref_data[1],
472 'name': ref_data[2],
481 'name': ref_data[2],
473 })
482 })
474 return rev_data
483 return rev_data
475
484
476
485
477 def git_pre_receive(unused_repo_path, revision_lines, env):
486 def git_pre_receive(unused_repo_path, revision_lines, env):
478 """
487 """
479 Pre push hook.
488 Pre push hook.
480
489
481 :param extras: dictionary containing the keys defined in simplevcs
490 :param extras: dictionary containing the keys defined in simplevcs
482 :type extras: dict
491 :type extras: dict
483
492
484 :return: status code of the hook. 0 for success.
493 :return: status code of the hook. 0 for success.
485 :rtype: int
494 :rtype: int
486 """
495 """
487 extras = json.loads(env['RC_SCM_DATA'])
496 extras = json.loads(env['RC_SCM_DATA'])
488 rev_data = _parse_git_ref_lines(revision_lines)
497 rev_data = _parse_git_ref_lines(revision_lines)
489 if 'push' not in extras['hooks']:
498 if 'push' not in extras['hooks']:
490 return 0
499 return 0
491 empty_commit_id = '0' * 40
500 empty_commit_id = '0' * 40
492
501
493 detect_force_push = extras.get('detect_force_push')
502 detect_force_push = extras.get('detect_force_push')
494
503
495 for push_ref in rev_data:
504 for push_ref in rev_data:
496 # store our git-env which holds the temp store
505 # store our git-env which holds the temp store
497 push_ref['git_env'] = _get_git_env()
506 push_ref['git_env'] = _get_git_env()
498 push_ref['pruned_sha'] = ''
507 push_ref['pruned_sha'] = ''
499 if not detect_force_push:
508 if not detect_force_push:
500 # don't check for forced-push when we don't need to
509 # don't check for forced-push when we don't need to
501 continue
510 continue
502
511
503 type_ = push_ref['type']
512 type_ = push_ref['type']
504 new_branch = push_ref['old_rev'] == empty_commit_id
513 new_branch = push_ref['old_rev'] == empty_commit_id
505 if type_ == 'heads' and not new_branch:
514 if type_ == 'heads' and not new_branch:
506 old_rev = push_ref['old_rev']
515 old_rev = push_ref['old_rev']
507 new_rev = push_ref['new_rev']
516 new_rev = push_ref['new_rev']
508 cmd = [settings.GIT_EXECUTABLE, 'rev-list',
517 cmd = [settings.GIT_EXECUTABLE, 'rev-list',
509 old_rev, '^{}'.format(new_rev)]
518 old_rev, '^{}'.format(new_rev)]
510 stdout, stderr = subprocessio.run_command(
519 stdout, stderr = subprocessio.run_command(
511 cmd, env=os.environ.copy())
520 cmd, env=os.environ.copy())
512 # means we're having some non-reachable objects, this forced push
521 # means we're having some non-reachable objects, this forced push
513 # was used
522 # was used
514 if stdout:
523 if stdout:
515 push_ref['pruned_sha'] = stdout.splitlines()
524 push_ref['pruned_sha'] = stdout.splitlines()
516
525
517 extras['hook_type'] = 'pre_receive'
526 extras['hook_type'] = 'pre_receive'
518 extras['commit_ids'] = rev_data
527 extras['commit_ids'] = rev_data
519 return _call_hook('pre_push', extras, GitMessageWriter())
528 return _call_hook('pre_push', extras, GitMessageWriter())
520
529
521
530
522 def git_post_receive(unused_repo_path, revision_lines, env):
531 def git_post_receive(unused_repo_path, revision_lines, env):
523 """
532 """
524 Post push hook.
533 Post push hook.
525
534
526 :param extras: dictionary containing the keys defined in simplevcs
535 :param extras: dictionary containing the keys defined in simplevcs
527 :type extras: dict
536 :type extras: dict
528
537
529 :return: status code of the hook. 0 for success.
538 :return: status code of the hook. 0 for success.
530 :rtype: int
539 :rtype: int
531 """
540 """
532 extras = json.loads(env['RC_SCM_DATA'])
541 extras = json.loads(env['RC_SCM_DATA'])
533 if 'push' not in extras['hooks']:
542 if 'push' not in extras['hooks']:
534 return 0
543 return 0
535
544
536 rev_data = _parse_git_ref_lines(revision_lines)
545 rev_data = _parse_git_ref_lines(revision_lines)
537
546
538 git_revs = []
547 git_revs = []
539
548
540 # N.B.(skreft): it is ok to just call git, as git before calling a
549 # N.B.(skreft): it is ok to just call git, as git before calling a
541 # subcommand sets the PATH environment variable so that it point to the
550 # subcommand sets the PATH environment variable so that it point to the
542 # correct version of the git executable.
551 # correct version of the git executable.
543 empty_commit_id = '0' * 40
552 empty_commit_id = '0' * 40
544 branches = []
553 branches = []
545 tags = []
554 tags = []
546 for push_ref in rev_data:
555 for push_ref in rev_data:
547 type_ = push_ref['type']
556 type_ = push_ref['type']
548
557
549 if type_ == 'heads':
558 if type_ == 'heads':
550 if push_ref['old_rev'] == empty_commit_id:
559 if push_ref['old_rev'] == empty_commit_id:
551 # starting new branch case
560 # starting new branch case
552 if push_ref['name'] not in branches:
561 if push_ref['name'] not in branches:
553 branches.append(push_ref['name'])
562 branches.append(push_ref['name'])
554
563
555 # Fix up head revision if needed
564 # Fix up head revision if needed
556 cmd = [settings.GIT_EXECUTABLE, 'show', 'HEAD']
565 cmd = [settings.GIT_EXECUTABLE, 'show', 'HEAD']
557 try:
566 try:
558 subprocessio.run_command(cmd, env=os.environ.copy())
567 subprocessio.run_command(cmd, env=os.environ.copy())
559 except Exception:
568 except Exception:
560 cmd = [settings.GIT_EXECUTABLE, 'symbolic-ref', 'HEAD',
569 cmd = [settings.GIT_EXECUTABLE, 'symbolic-ref', 'HEAD',
561 'refs/heads/%s' % push_ref['name']]
570 'refs/heads/%s' % push_ref['name']]
562 print("Setting default branch to %s" % push_ref['name'])
571 print("Setting default branch to %s" % push_ref['name'])
563 subprocessio.run_command(cmd, env=os.environ.copy())
572 subprocessio.run_command(cmd, env=os.environ.copy())
564
573
565 cmd = [settings.GIT_EXECUTABLE, 'for-each-ref',
574 cmd = [settings.GIT_EXECUTABLE, 'for-each-ref',
566 '--format=%(refname)', 'refs/heads/*']
575 '--format=%(refname)', 'refs/heads/*']
567 stdout, stderr = subprocessio.run_command(
576 stdout, stderr = subprocessio.run_command(
568 cmd, env=os.environ.copy())
577 cmd, env=os.environ.copy())
569 heads = stdout
578 heads = stdout
570 heads = heads.replace(push_ref['ref'], '')
579 heads = heads.replace(push_ref['ref'], '')
571 heads = ' '.join(head for head
580 heads = ' '.join(head for head
572 in heads.splitlines() if head) or '.'
581 in heads.splitlines() if head) or '.'
573 cmd = [settings.GIT_EXECUTABLE, 'log', '--reverse',
582 cmd = [settings.GIT_EXECUTABLE, 'log', '--reverse',
574 '--pretty=format:%H', '--', push_ref['new_rev'],
583 '--pretty=format:%H', '--', push_ref['new_rev'],
575 '--not', heads]
584 '--not', heads]
576 stdout, stderr = subprocessio.run_command(
585 stdout, stderr = subprocessio.run_command(
577 cmd, env=os.environ.copy())
586 cmd, env=os.environ.copy())
578 git_revs.extend(stdout.splitlines())
587 git_revs.extend(stdout.splitlines())
579 elif push_ref['new_rev'] == empty_commit_id:
588 elif push_ref['new_rev'] == empty_commit_id:
580 # delete branch case
589 # delete branch case
581 git_revs.append('delete_branch=>%s' % push_ref['name'])
590 git_revs.append('delete_branch=>%s' % push_ref['name'])
582 else:
591 else:
583 if push_ref['name'] not in branches:
592 if push_ref['name'] not in branches:
584 branches.append(push_ref['name'])
593 branches.append(push_ref['name'])
585
594
586 cmd = [settings.GIT_EXECUTABLE, 'log',
595 cmd = [settings.GIT_EXECUTABLE, 'log',
587 '{old_rev}..{new_rev}'.format(**push_ref),
596 '{old_rev}..{new_rev}'.format(**push_ref),
588 '--reverse', '--pretty=format:%H']
597 '--reverse', '--pretty=format:%H']
589 stdout, stderr = subprocessio.run_command(
598 stdout, stderr = subprocessio.run_command(
590 cmd, env=os.environ.copy())
599 cmd, env=os.environ.copy())
591 git_revs.extend(stdout.splitlines())
600 git_revs.extend(stdout.splitlines())
592 elif type_ == 'tags':
601 elif type_ == 'tags':
593 if push_ref['name'] not in tags:
602 if push_ref['name'] not in tags:
594 tags.append(push_ref['name'])
603 tags.append(push_ref['name'])
595 git_revs.append('tag=>%s' % push_ref['name'])
604 git_revs.append('tag=>%s' % push_ref['name'])
596
605
597 extras['hook_type'] = 'post_receive'
606 extras['hook_type'] = 'post_receive'
598 extras['commit_ids'] = git_revs
607 extras['commit_ids'] = git_revs
599 extras['new_refs'] = {
608 extras['new_refs'] = {
600 'branches': branches,
609 'branches': branches,
601 'bookmarks': [],
610 'bookmarks': [],
602 'tags': tags,
611 'tags': tags,
603 }
612 }
604
613
605 if 'repo_size' in extras['hooks']:
614 if 'repo_size' in extras['hooks']:
606 try:
615 try:
607 _call_hook('repo_size', extras, GitMessageWriter())
616 _call_hook('repo_size', extras, GitMessageWriter())
608 except:
617 except:
609 pass
618 pass
610
619
611 return _call_hook('post_push', extras, GitMessageWriter())
620 return _call_hook('post_push', extras, GitMessageWriter())
612
621
613
622
614 def _get_extras_from_txn_id(path, txn_id):
623 def _get_extras_from_txn_id(path, txn_id):
615 extras = {}
624 extras = {}
616 try:
625 try:
617 cmd = ['svnlook', 'pget',
626 cmd = ['svnlook', 'pget',
618 '-t', txn_id,
627 '-t', txn_id,
619 '--revprop', path, 'rc-scm-extras']
628 '--revprop', path, 'rc-scm-extras']
620 stdout, stderr = subprocessio.run_command(
629 stdout, stderr = subprocessio.run_command(
621 cmd, env=os.environ.copy())
630 cmd, env=os.environ.copy())
622 extras = json.loads(base64.urlsafe_b64decode(stdout))
631 extras = json.loads(base64.urlsafe_b64decode(stdout))
623 except Exception:
632 except Exception:
624 log.exception('Failed to extract extras info from txn_id')
633 log.exception('Failed to extract extras info from txn_id')
625
634
626 return extras
635 return extras
627
636
628
637
629 def _get_extras_from_commit_id(commit_id, path):
638 def _get_extras_from_commit_id(commit_id, path):
630 extras = {}
639 extras = {}
631 try:
640 try:
632 cmd = ['svnlook', 'pget',
641 cmd = ['svnlook', 'pget',
633 '-r', commit_id,
642 '-r', commit_id,
634 '--revprop', path, 'rc-scm-extras']
643 '--revprop', path, 'rc-scm-extras']
635 stdout, stderr = subprocessio.run_command(
644 stdout, stderr = subprocessio.run_command(
636 cmd, env=os.environ.copy())
645 cmd, env=os.environ.copy())
637 extras = json.loads(base64.urlsafe_b64decode(stdout))
646 extras = json.loads(base64.urlsafe_b64decode(stdout))
638 except Exception:
647 except Exception:
639 log.exception('Failed to extract extras info from commit_id')
648 log.exception('Failed to extract extras info from commit_id')
640
649
641 return extras
650 return extras
642
651
643
652
644 def svn_pre_commit(repo_path, commit_data, env):
653 def svn_pre_commit(repo_path, commit_data, env):
645 path, txn_id = commit_data
654 path, txn_id = commit_data
646 branches = []
655 branches = []
647 tags = []
656 tags = []
648
657
649 if env.get('RC_SCM_DATA'):
658 if env.get('RC_SCM_DATA'):
650 extras = json.loads(env['RC_SCM_DATA'])
659 extras = json.loads(env['RC_SCM_DATA'])
651 else:
660 else:
652 # fallback method to read from TXN-ID stored data
661 # fallback method to read from TXN-ID stored data
653 extras = _get_extras_from_txn_id(path, txn_id)
662 extras = _get_extras_from_txn_id(path, txn_id)
654 if not extras:
663 if not extras:
655 return 0
664 return 0
656
665
657 extras['hook_type'] = 'pre_commit'
666 extras['hook_type'] = 'pre_commit'
658 extras['commit_ids'] = []
667 extras['commit_ids'] = []
659 extras['txn_id'] = txn_id
668 extras['txn_id'] = txn_id
660 extras['new_refs'] = {
669 extras['new_refs'] = {
661 'total_commits': 1,
670 'total_commits': 1,
662 'branches': branches,
671 'branches': branches,
663 'bookmarks': [],
672 'bookmarks': [],
664 'tags': tags,
673 'tags': tags,
665 }
674 }
666
675
667 return _call_hook('pre_push', extras, SvnMessageWriter())
676 return _call_hook('pre_push', extras, SvnMessageWriter())
668
677
669
678
670 def svn_post_commit(repo_path, commit_data, env):
679 def svn_post_commit(repo_path, commit_data, env):
671 """
680 """
672 commit_data is path, rev, txn_id
681 commit_data is path, rev, txn_id
673 """
682 """
674 path, commit_id, txn_id = commit_data
683 path, commit_id, txn_id = commit_data
675 branches = []
684 branches = []
676 tags = []
685 tags = []
677
686
678 if env.get('RC_SCM_DATA'):
687 if env.get('RC_SCM_DATA'):
679 extras = json.loads(env['RC_SCM_DATA'])
688 extras = json.loads(env['RC_SCM_DATA'])
680 else:
689 else:
681 # fallback method to read from TXN-ID stored data
690 # fallback method to read from TXN-ID stored data
682 extras = _get_extras_from_commit_id(commit_id, path)
691 extras = _get_extras_from_commit_id(commit_id, path)
683 if not extras:
692 if not extras:
684 return 0
693 return 0
685
694
686 extras['hook_type'] = 'post_commit'
695 extras['hook_type'] = 'post_commit'
687 extras['commit_ids'] = [commit_id]
696 extras['commit_ids'] = [commit_id]
688 extras['txn_id'] = txn_id
697 extras['txn_id'] = txn_id
689 extras['new_refs'] = {
698 extras['new_refs'] = {
690 'branches': branches,
699 'branches': branches,
691 'bookmarks': [],
700 'bookmarks': [],
692 'tags': tags,
701 'tags': tags,
693 'total_commits': 1,
702 'total_commits': 1,
694 }
703 }
695
704
696 if 'repo_size' in extras['hooks']:
705 if 'repo_size' in extras['hooks']:
697 try:
706 try:
698 _call_hook('repo_size', extras, SvnMessageWriter())
707 _call_hook('repo_size', extras, SvnMessageWriter())
699 except Exception:
708 except Exception:
700 pass
709 pass
701
710
702 return _call_hook('post_push', extras, SvnMessageWriter())
711 return _call_hook('post_push', extras, SvnMessageWriter())
@@ -1,598 +1,607 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2018 RhodeCode GmbH
2 # Copyright (C) 2014-2018 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import os
18 import os
19 import sys
19 import sys
20 import base64
20 import base64
21 import locale
21 import locale
22 import logging
22 import logging
23 import uuid
23 import uuid
24 import wsgiref.util
24 import wsgiref.util
25 import traceback
25 import traceback
26 import tempfile
26 import tempfile
27 from itertools import chain
27 from itertools import chain
28
28
29 import simplejson as json
29 import simplejson as json
30 import msgpack
30 import msgpack
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.compat import configparser
34 from pyramid.compat import configparser
35
35
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
39 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
40 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
40 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
41
41
42 try:
42 try:
43 locale.setlocale(locale.LC_ALL, '')
43 locale.setlocale(locale.LC_ALL, '')
44 except locale.Error as e:
44 except locale.Error as e:
45 log.error(
45 log.error(
46 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
46 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
47 os.environ['LC_ALL'] = 'C'
47 os.environ['LC_ALL'] = 'C'
48
48
49 import vcsserver
49 import vcsserver
50 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
50 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
51 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
51 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
52 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
52 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
53 from vcsserver.echo_stub.echo_app import EchoApp
53 from vcsserver.echo_stub.echo_app import EchoApp
54 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
54 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
55 from vcsserver.lib.exc_tracking import store_exception
55 from vcsserver.lib.exc_tracking import store_exception
56 from vcsserver.server import VcsServer
56 from vcsserver.server import VcsServer
57
57
58 try:
58 try:
59 from vcsserver.git import GitFactory, GitRemote
59 from vcsserver.git import GitFactory, GitRemote
60 except ImportError:
60 except ImportError:
61 GitFactory = None
61 GitFactory = None
62 GitRemote = None
62 GitRemote = None
63
63
64 try:
64 try:
65 from vcsserver.hg import MercurialFactory, HgRemote
65 from vcsserver.hg import MercurialFactory, HgRemote
66 except ImportError:
66 except ImportError:
67 MercurialFactory = None
67 MercurialFactory = None
68 HgRemote = None
68 HgRemote = None
69
69
70 try:
70 try:
71 from vcsserver.svn import SubversionFactory, SvnRemote
71 from vcsserver.svn import SubversionFactory, SvnRemote
72 except ImportError:
72 except ImportError:
73 SubversionFactory = None
73 SubversionFactory = None
74 SvnRemote = None
74 SvnRemote = None
75
75
76
76
77 def _is_request_chunked(environ):
77 def _is_request_chunked(environ):
78 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
78 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
79 return stream
79 return stream
80
80
81
81
82 def _int_setting(settings, name, default):
82 def _int_setting(settings, name, default):
83 settings[name] = int(settings.get(name, default))
83 settings[name] = int(settings.get(name, default))
84 return settings[name]
84 return settings[name]
85
85
86
86
87 def _bool_setting(settings, name, default):
87 def _bool_setting(settings, name, default):
88 input_val = settings.get(name, default)
88 input_val = settings.get(name, default)
89 if isinstance(input_val, unicode):
89 if isinstance(input_val, unicode):
90 input_val = input_val.encode('utf8')
90 input_val = input_val.encode('utf8')
91 settings[name] = asbool(input_val)
91 settings[name] = asbool(input_val)
92 return settings[name]
92 return settings[name]
93
93
94
94
95 def _list_setting(settings, name, default):
95 def _list_setting(settings, name, default):
96 raw_value = settings.get(name, default)
96 raw_value = settings.get(name, default)
97
97
98 # Otherwise we assume it uses pyramids space/newline separation.
98 # Otherwise we assume it uses pyramids space/newline separation.
99 settings[name] = aslist(raw_value)
99 settings[name] = aslist(raw_value)
100 return settings[name]
100 return settings[name]
101
101
102
102
103 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
103 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
104 value = settings.get(name, default)
104 value = settings.get(name, default)
105
105
106 if default_when_empty and not value:
106 if default_when_empty and not value:
107 # use default value when value is empty
107 # use default value when value is empty
108 value = default
108 value = default
109
109
110 if lower:
110 if lower:
111 value = value.lower()
111 value = value.lower()
112 settings[name] = value
112 settings[name] = value
113 return settings[name]
113 return settings[name]
114
114
115
115
116 class VCS(object):
116 class VCS(object):
117 def __init__(self, locale=None, cache_config=None):
117 def __init__(self, locale=None, cache_config=None):
118 self.locale = locale
118 self.locale = locale
119 self.cache_config = cache_config
119 self.cache_config = cache_config
120 self._configure_locale()
120 self._configure_locale()
121
121
122 if GitFactory and GitRemote:
122 if GitFactory and GitRemote:
123 git_factory = GitFactory()
123 git_factory = GitFactory()
124 self._git_remote = GitRemote(git_factory)
124 self._git_remote = GitRemote(git_factory)
125 else:
125 else:
126 log.info("Git client import failed")
126 log.info("Git client import failed")
127
127
128 if MercurialFactory and HgRemote:
128 if MercurialFactory and HgRemote:
129 hg_factory = MercurialFactory()
129 hg_factory = MercurialFactory()
130 self._hg_remote = HgRemote(hg_factory)
130 self._hg_remote = HgRemote(hg_factory)
131 else:
131 else:
132 log.info("Mercurial client import failed")
132 log.info("Mercurial client import failed")
133
133
134 if SubversionFactory and SvnRemote:
134 if SubversionFactory and SvnRemote:
135 svn_factory = SubversionFactory()
135 svn_factory = SubversionFactory()
136
136
137 # hg factory is used for svn url validation
137 # hg factory is used for svn url validation
138 hg_factory = MercurialFactory()
138 hg_factory = MercurialFactory()
139 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
139 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
140 else:
140 else:
141 log.info("Subversion client import failed")
141 log.info("Subversion client import failed")
142
142
143 self._vcsserver = VcsServer()
143 self._vcsserver = VcsServer()
144
144
145 def _configure_locale(self):
145 def _configure_locale(self):
146 if self.locale:
146 if self.locale:
147 log.info('Settings locale: `LC_ALL` to %s', self.locale)
147 log.info('Settings locale: `LC_ALL` to %s', self.locale)
148 else:
148 else:
149 log.info(
149 log.info(
150 'Configuring locale subsystem based on environment variables')
150 'Configuring locale subsystem based on environment variables')
151 try:
151 try:
152 # If self.locale is the empty string, then the locale
152 # If self.locale is the empty string, then the locale
153 # module will use the environment variables. See the
153 # module will use the environment variables. See the
154 # documentation of the package `locale`.
154 # documentation of the package `locale`.
155 locale.setlocale(locale.LC_ALL, self.locale)
155 locale.setlocale(locale.LC_ALL, self.locale)
156
156
157 language_code, encoding = locale.getlocale()
157 language_code, encoding = locale.getlocale()
158 log.info(
158 log.info(
159 'Locale set to language code "%s" with encoding "%s".',
159 'Locale set to language code "%s" with encoding "%s".',
160 language_code, encoding)
160 language_code, encoding)
161 except locale.Error:
161 except locale.Error:
162 log.exception(
162 log.exception(
163 'Cannot set locale, not configuring the locale system')
163 'Cannot set locale, not configuring the locale system')
164
164
165
165
166 class WsgiProxy(object):
166 class WsgiProxy(object):
167 def __init__(self, wsgi):
167 def __init__(self, wsgi):
168 self.wsgi = wsgi
168 self.wsgi = wsgi
169
169
170 def __call__(self, environ, start_response):
170 def __call__(self, environ, start_response):
171 input_data = environ['wsgi.input'].read()
171 input_data = environ['wsgi.input'].read()
172 input_data = msgpack.unpackb(input_data)
172 input_data = msgpack.unpackb(input_data)
173
173
174 error = None
174 error = None
175 try:
175 try:
176 data, status, headers = self.wsgi.handle(
176 data, status, headers = self.wsgi.handle(
177 input_data['environment'], input_data['input_data'],
177 input_data['environment'], input_data['input_data'],
178 *input_data['args'], **input_data['kwargs'])
178 *input_data['args'], **input_data['kwargs'])
179 except Exception as e:
179 except Exception as e:
180 data, status, headers = [], None, None
180 data, status, headers = [], None, None
181 error = {
181 error = {
182 'message': str(e),
182 'message': str(e),
183 '_vcs_kind': getattr(e, '_vcs_kind', None)
183 '_vcs_kind': getattr(e, '_vcs_kind', None)
184 }
184 }
185
185
186 start_response(200, {})
186 start_response(200, {})
187 return self._iterator(error, status, headers, data)
187 return self._iterator(error, status, headers, data)
188
188
189 def _iterator(self, error, status, headers, data):
189 def _iterator(self, error, status, headers, data):
190 initial_data = [
190 initial_data = [
191 error,
191 error,
192 status,
192 status,
193 headers,
193 headers,
194 ]
194 ]
195
195
196 for d in chain(initial_data, data):
196 for d in chain(initial_data, data):
197 yield msgpack.packb(d)
197 yield msgpack.packb(d)
198
198
199
199
200 def not_found(request):
201 return {'status': '404 NOT FOUND'}
202
203
204 class VCSViewPredicate(object):
205 def __init__(self, val, config):
206 self.remotes = val
207
208 def text(self):
209 return 'vcs view method = %s' % (self.remotes.keys(),)
210
211 phash = text
212
213 def __call__(self, context, request):
214 """
215 View predicate that returns true if given backend is supported by
216 defined remotes.
217 """
218 backend = request.matchdict.get('backend')
219 return backend in self.remotes
220
221
200 class HTTPApplication(object):
222 class HTTPApplication(object):
201 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
223 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
202
224
203 remote_wsgi = remote_wsgi
225 remote_wsgi = remote_wsgi
204 _use_echo_app = False
226 _use_echo_app = False
205
227
206 def __init__(self, settings=None, global_config=None):
228 def __init__(self, settings=None, global_config=None):
207 self._sanitize_settings_and_apply_defaults(settings)
229 self._sanitize_settings_and_apply_defaults(settings)
208
230
209 self.config = Configurator(settings=settings)
231 self.config = Configurator(settings=settings)
210 self.global_config = global_config
232 self.global_config = global_config
211 self.config.include('vcsserver.lib.rc_cache')
233 self.config.include('vcsserver.lib.rc_cache')
212
234
213 locale = settings.get('locale', '') or 'en_US.UTF-8'
235 locale = settings.get('locale', '') or 'en_US.UTF-8'
214 vcs = VCS(locale=locale, cache_config=settings)
236 vcs = VCS(locale=locale, cache_config=settings)
215 self._remotes = {
237 self._remotes = {
216 'hg': vcs._hg_remote,
238 'hg': vcs._hg_remote,
217 'git': vcs._git_remote,
239 'git': vcs._git_remote,
218 'svn': vcs._svn_remote,
240 'svn': vcs._svn_remote,
219 'server': vcs._vcsserver,
241 'server': vcs._vcsserver,
220 }
242 }
221 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
243 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
222 self._use_echo_app = True
244 self._use_echo_app = True
223 log.warning("Using EchoApp for VCS operations.")
245 log.warning("Using EchoApp for VCS operations.")
224 self.remote_wsgi = remote_wsgi_stub
246 self.remote_wsgi = remote_wsgi_stub
225
247
226 self._configure_settings(global_config, settings)
248 self._configure_settings(global_config, settings)
227 self._configure()
249 self._configure()
228
250
229 def _configure_settings(self, global_config, app_settings):
251 def _configure_settings(self, global_config, app_settings):
230 """
252 """
231 Configure the settings module.
253 Configure the settings module.
232 """
254 """
233 settings_merged = global_config.copy()
255 settings_merged = global_config.copy()
234 settings_merged.update(app_settings)
256 settings_merged.update(app_settings)
235
257
236 git_path = app_settings.get('git_path', None)
258 git_path = app_settings.get('git_path', None)
237 if git_path:
259 if git_path:
238 settings.GIT_EXECUTABLE = git_path
260 settings.GIT_EXECUTABLE = git_path
239 binary_dir = app_settings.get('core.binary_dir', None)
261 binary_dir = app_settings.get('core.binary_dir', None)
240 if binary_dir:
262 if binary_dir:
241 settings.BINARY_DIR = binary_dir
263 settings.BINARY_DIR = binary_dir
242
264
243 # Store the settings to make them available to other modules.
265 # Store the settings to make them available to other modules.
244 vcsserver.PYRAMID_SETTINGS = settings_merged
266 vcsserver.PYRAMID_SETTINGS = settings_merged
245 vcsserver.CONFIG = settings_merged
267 vcsserver.CONFIG = settings_merged
246
268
247 def _sanitize_settings_and_apply_defaults(self, settings):
269 def _sanitize_settings_and_apply_defaults(self, settings):
248 temp_store = tempfile.gettempdir()
270 temp_store = tempfile.gettempdir()
249 default_cache_dir = os.path.join(temp_store, 'rc_cache')
271 default_cache_dir = os.path.join(temp_store, 'rc_cache')
250
272
251 # save default, cache dir, and use it for all backends later.
273 # save default, cache dir, and use it for all backends later.
252 default_cache_dir = _string_setting(
274 default_cache_dir = _string_setting(
253 settings,
275 settings,
254 'cache_dir',
276 'cache_dir',
255 default_cache_dir, lower=False, default_when_empty=True)
277 default_cache_dir, lower=False, default_when_empty=True)
256
278
257 # ensure we have our dir created
279 # ensure we have our dir created
258 if not os.path.isdir(default_cache_dir):
280 if not os.path.isdir(default_cache_dir):
259 os.makedirs(default_cache_dir, mode=0755)
281 os.makedirs(default_cache_dir, mode=0o755)
260
282
261 # exception store cache
283 # exception store cache
262 _string_setting(
284 _string_setting(
263 settings,
285 settings,
264 'exception_tracker.store_path',
286 'exception_tracker.store_path',
265 temp_store, lower=False, default_when_empty=True)
287 temp_store, lower=False, default_when_empty=True)
266
288
267 # repo_object cache
289 # repo_object cache
268 _string_setting(
290 _string_setting(
269 settings,
291 settings,
270 'rc_cache.repo_object.backend',
292 'rc_cache.repo_object.backend',
271 'dogpile.cache.rc.memory_lru')
293 'dogpile.cache.rc.memory_lru')
272 _int_setting(
294 _int_setting(
273 settings,
295 settings,
274 'rc_cache.repo_object.expiration_time',
296 'rc_cache.repo_object.expiration_time',
275 300)
297 300)
276 _int_setting(
298 _int_setting(
277 settings,
299 settings,
278 'rc_cache.repo_object.max_size',
300 'rc_cache.repo_object.max_size',
279 1024)
301 1024)
280
302
281 def _configure(self):
303 def _configure(self):
282 self.config.add_renderer(
304 self.config.add_renderer(name='msgpack', factory=self._msgpack_renderer_factory)
283 name='msgpack',
284 factory=self._msgpack_renderer_factory)
285
305
286 self.config.add_route('service', '/_service')
306 self.config.add_route('service', '/_service')
287 self.config.add_route('status', '/status')
307 self.config.add_route('status', '/status')
288 self.config.add_route('hg_proxy', '/proxy/hg')
308 self.config.add_route('hg_proxy', '/proxy/hg')
289 self.config.add_route('git_proxy', '/proxy/git')
309 self.config.add_route('git_proxy', '/proxy/git')
290 self.config.add_route('vcs', '/{backend}')
310 self.config.add_route('vcs', '/{backend}')
291 self.config.add_route('stream_git', '/stream/git/*repo_name')
311 self.config.add_route('stream_git', '/stream/git/*repo_name')
292 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
312 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
293
313
294 self.config.add_view(
314 self.config.add_view(self.status_view, route_name='status', renderer='json')
295 self.status_view, route_name='status', renderer='json')
315 self.config.add_view(self.service_view, route_name='service', renderer='msgpack')
296 self.config.add_view(
297 self.service_view, route_name='service', renderer='msgpack')
298
316
299 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
317 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
300 self.config.add_view(self.git_proxy(), route_name='git_proxy')
318 self.config.add_view(self.git_proxy(), route_name='git_proxy')
301 self.config.add_view(
319 self.config.add_view(self.vcs_view, route_name='vcs', renderer='msgpack',
302 self.vcs_view, route_name='vcs', renderer='msgpack',
320 vcs_view=self._remotes)
303 custom_predicates=[self.is_vcs_view])
304
321
305 self.config.add_view(self.hg_stream(), route_name='stream_hg')
322 self.config.add_view(self.hg_stream(), route_name='stream_hg')
306 self.config.add_view(self.git_stream(), route_name='stream_git')
323 self.config.add_view(self.git_stream(), route_name='stream_git')
307
324
308 def notfound(request):
325 self.config.add_view_predicate('vcs_view', VCSViewPredicate)
309 return {'status': '404 NOT FOUND'}
326
310 self.config.add_notfound_view(notfound, renderer='json')
327 self.config.add_notfound_view(not_found, renderer='json')
311
328
312 self.config.add_view(self.handle_vcs_exception, context=Exception)
329 self.config.add_view(self.handle_vcs_exception, context=Exception)
313
330
314 self.config.add_tween(
331 self.config.add_tween(
315 'vcsserver.tweens.RequestWrapperTween',
332 'vcsserver.tweens.RequestWrapperTween',
316 )
333 )
317
334
318 def wsgi_app(self):
335 def wsgi_app(self):
319 return self.config.make_wsgi_app()
336 return self.config.make_wsgi_app()
320
337
321 def vcs_view(self, request):
338 def vcs_view(self, request):
322 remote = self._remotes[request.matchdict['backend']]
339 remote = self._remotes[request.matchdict['backend']]
323 payload = msgpack.unpackb(request.body, use_list=True)
340 payload = msgpack.unpackb(request.body, use_list=True)
324 method = payload.get('method')
341 method = payload.get('method')
325 params = payload.get('params')
342 params = payload.get('params')
326 wire = params.get('wire')
343 wire = params.get('wire')
327 args = params.get('args')
344 args = params.get('args')
328 kwargs = params.get('kwargs')
345 kwargs = params.get('kwargs')
329 context_uid = None
346 context_uid = None
330
347
331 if wire:
348 if wire:
332 try:
349 try:
333 wire['context'] = context_uid = uuid.UUID(wire['context'])
350 wire['context'] = context_uid = uuid.UUID(wire['context'])
334 except KeyError:
351 except KeyError:
335 pass
352 pass
336 args.insert(0, wire)
353 args.insert(0, wire)
337
354
338 log.debug('method called:%s with kwargs:%s context_uid: %s',
355 log.debug('method called:%s with kwargs:%s context_uid: %s',
339 method, kwargs, context_uid)
356 method, kwargs, context_uid)
340 try:
357 try:
341 resp = getattr(remote, method)(*args, **kwargs)
358 resp = getattr(remote, method)(*args, **kwargs)
342 except Exception as e:
359 except Exception as e:
343 exc_info = list(sys.exc_info())
360 exc_info = list(sys.exc_info())
344 exc_type, exc_value, exc_traceback = exc_info
361 exc_type, exc_value, exc_traceback = exc_info
345
362
346 org_exc = getattr(e, '_org_exc', None)
363 org_exc = getattr(e, '_org_exc', None)
347 org_exc_name = None
364 org_exc_name = None
348 if org_exc:
365 if org_exc:
349 org_exc_name = org_exc.__class__.__name__
366 org_exc_name = org_exc.__class__.__name__
350 # replace our "faked" exception with our org
367 # replace our "faked" exception with our org
351 exc_info[0] = org_exc.__class__
368 exc_info[0] = org_exc.__class__
352 exc_info[1] = org_exc
369 exc_info[1] = org_exc
353
370
354 store_exception(id(exc_info), exc_info)
371 store_exception(id(exc_info), exc_info)
355
372
356 tb_info = ''.join(
373 tb_info = ''.join(
357 traceback.format_exception(exc_type, exc_value, exc_traceback))
374 traceback.format_exception(exc_type, exc_value, exc_traceback))
358
375
359 type_ = e.__class__.__name__
376 type_ = e.__class__.__name__
360 if type_ not in self.ALLOWED_EXCEPTIONS:
377 if type_ not in self.ALLOWED_EXCEPTIONS:
361 type_ = None
378 type_ = None
362
379
363 resp = {
380 resp = {
364 'id': payload.get('id'),
381 'id': payload.get('id'),
365 'error': {
382 'error': {
366 'message': e.message,
383 'message': e.message,
367 'traceback': tb_info,
384 'traceback': tb_info,
368 'org_exc': org_exc_name,
385 'org_exc': org_exc_name,
369 'type': type_
386 'type': type_
370 }
387 }
371 }
388 }
372 try:
389 try:
373 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
390 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
374 except AttributeError:
391 except AttributeError:
375 pass
392 pass
376 else:
393 else:
377 resp = {
394 resp = {
378 'id': payload.get('id'),
395 'id': payload.get('id'),
379 'result': resp
396 'result': resp
380 }
397 }
381
398
382 return resp
399 return resp
383
400
384 def status_view(self, request):
401 def status_view(self, request):
385 import vcsserver
402 import vcsserver
386 return {'status': 'OK', 'vcsserver_version': vcsserver.__version__,
403 return {'status': 'OK', 'vcsserver_version': vcsserver.__version__,
387 'pid': os.getpid()}
404 'pid': os.getpid()}
388
405
389 def service_view(self, request):
406 def service_view(self, request):
390 import vcsserver
407 import vcsserver
391
408
392 payload = msgpack.unpackb(request.body, use_list=True)
409 payload = msgpack.unpackb(request.body, use_list=True)
393
410
394 try:
411 try:
395 path = self.global_config['__file__']
412 path = self.global_config['__file__']
396 config = configparser.ConfigParser()
413 config = configparser.ConfigParser()
397 config.read(path)
414 config.read(path)
398 parsed_ini = config
415 parsed_ini = config
399 if parsed_ini.has_section('server:main'):
416 if parsed_ini.has_section('server:main'):
400 parsed_ini = dict(parsed_ini.items('server:main'))
417 parsed_ini = dict(parsed_ini.items('server:main'))
401 except Exception:
418 except Exception:
402 log.exception('Failed to read .ini file for display')
419 log.exception('Failed to read .ini file for display')
403 parsed_ini = {}
420 parsed_ini = {}
404
421
405 resp = {
422 resp = {
406 'id': payload.get('id'),
423 'id': payload.get('id'),
407 'result': dict(
424 'result': dict(
408 version=vcsserver.__version__,
425 version=vcsserver.__version__,
409 config=parsed_ini,
426 config=parsed_ini,
410 payload=payload,
427 payload=payload,
411 )
428 )
412 }
429 }
413 return resp
430 return resp
414
431
415 def _msgpack_renderer_factory(self, info):
432 def _msgpack_renderer_factory(self, info):
416 def _render(value, system):
433 def _render(value, system):
417 value = msgpack.packb(value)
434 value = msgpack.packb(value)
418 request = system.get('request')
435 request = system.get('request')
419 if request is not None:
436 if request is not None:
420 response = request.response
437 response = request.response
421 ct = response.content_type
438 ct = response.content_type
422 if ct == response.default_content_type:
439 if ct == response.default_content_type:
423 response.content_type = 'application/x-msgpack'
440 response.content_type = 'application/x-msgpack'
424 return value
441 return value
425 return _render
442 return _render
426
443
427 def set_env_from_config(self, environ, config):
444 def set_env_from_config(self, environ, config):
428 dict_conf = {}
445 dict_conf = {}
429 try:
446 try:
430 for elem in config:
447 for elem in config:
431 if elem[0] == 'rhodecode':
448 if elem[0] == 'rhodecode':
432 dict_conf = json.loads(elem[2])
449 dict_conf = json.loads(elem[2])
433 break
450 break
434 except Exception:
451 except Exception:
435 log.exception('Failed to fetch SCM CONFIG')
452 log.exception('Failed to fetch SCM CONFIG')
436 return
453 return
437
454
438 username = dict_conf.get('username')
455 username = dict_conf.get('username')
439 if username:
456 if username:
440 environ['REMOTE_USER'] = username
457 environ['REMOTE_USER'] = username
441 # mercurial specific, some extension api rely on this
458 # mercurial specific, some extension api rely on this
442 environ['HGUSER'] = username
459 environ['HGUSER'] = username
443
460
444 ip = dict_conf.get('ip')
461 ip = dict_conf.get('ip')
445 if ip:
462 if ip:
446 environ['REMOTE_HOST'] = ip
463 environ['REMOTE_HOST'] = ip
447
464
448 if _is_request_chunked(environ):
465 if _is_request_chunked(environ):
449 # set the compatibility flag for webob
466 # set the compatibility flag for webob
450 environ['wsgi.input_terminated'] = True
467 environ['wsgi.input_terminated'] = True
451
468
452 def hg_proxy(self):
469 def hg_proxy(self):
453 @wsgiapp
470 @wsgiapp
454 def _hg_proxy(environ, start_response):
471 def _hg_proxy(environ, start_response):
455 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
472 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
456 return app(environ, start_response)
473 return app(environ, start_response)
457 return _hg_proxy
474 return _hg_proxy
458
475
459 def git_proxy(self):
476 def git_proxy(self):
460 @wsgiapp
477 @wsgiapp
461 def _git_proxy(environ, start_response):
478 def _git_proxy(environ, start_response):
462 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
479 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
463 return app(environ, start_response)
480 return app(environ, start_response)
464 return _git_proxy
481 return _git_proxy
465
482
466 def hg_stream(self):
483 def hg_stream(self):
467 if self._use_echo_app:
484 if self._use_echo_app:
468 @wsgiapp
485 @wsgiapp
469 def _hg_stream(environ, start_response):
486 def _hg_stream(environ, start_response):
470 app = EchoApp('fake_path', 'fake_name', None)
487 app = EchoApp('fake_path', 'fake_name', None)
471 return app(environ, start_response)
488 return app(environ, start_response)
472 return _hg_stream
489 return _hg_stream
473 else:
490 else:
474 @wsgiapp
491 @wsgiapp
475 def _hg_stream(environ, start_response):
492 def _hg_stream(environ, start_response):
476 log.debug('http-app: handling hg stream')
493 log.debug('http-app: handling hg stream')
477 repo_path = environ['HTTP_X_RC_REPO_PATH']
494 repo_path = environ['HTTP_X_RC_REPO_PATH']
478 repo_name = environ['HTTP_X_RC_REPO_NAME']
495 repo_name = environ['HTTP_X_RC_REPO_NAME']
479 packed_config = base64.b64decode(
496 packed_config = base64.b64decode(
480 environ['HTTP_X_RC_REPO_CONFIG'])
497 environ['HTTP_X_RC_REPO_CONFIG'])
481 config = msgpack.unpackb(packed_config)
498 config = msgpack.unpackb(packed_config)
482 app = scm_app.create_hg_wsgi_app(
499 app = scm_app.create_hg_wsgi_app(
483 repo_path, repo_name, config)
500 repo_path, repo_name, config)
484
501
485 # Consistent path information for hgweb
502 # Consistent path information for hgweb
486 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
503 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
487 environ['REPO_NAME'] = repo_name
504 environ['REPO_NAME'] = repo_name
488 self.set_env_from_config(environ, config)
505 self.set_env_from_config(environ, config)
489
506
490 log.debug('http-app: starting app handler '
507 log.debug('http-app: starting app handler '
491 'with %s and process request', app)
508 'with %s and process request', app)
492 return app(environ, ResponseFilter(start_response))
509 return app(environ, ResponseFilter(start_response))
493 return _hg_stream
510 return _hg_stream
494
511
495 def git_stream(self):
512 def git_stream(self):
496 if self._use_echo_app:
513 if self._use_echo_app:
497 @wsgiapp
514 @wsgiapp
498 def _git_stream(environ, start_response):
515 def _git_stream(environ, start_response):
499 app = EchoApp('fake_path', 'fake_name', None)
516 app = EchoApp('fake_path', 'fake_name', None)
500 return app(environ, start_response)
517 return app(environ, start_response)
501 return _git_stream
518 return _git_stream
502 else:
519 else:
503 @wsgiapp
520 @wsgiapp
504 def _git_stream(environ, start_response):
521 def _git_stream(environ, start_response):
505 log.debug('http-app: handling git stream')
522 log.debug('http-app: handling git stream')
506 repo_path = environ['HTTP_X_RC_REPO_PATH']
523 repo_path = environ['HTTP_X_RC_REPO_PATH']
507 repo_name = environ['HTTP_X_RC_REPO_NAME']
524 repo_name = environ['HTTP_X_RC_REPO_NAME']
508 packed_config = base64.b64decode(
525 packed_config = base64.b64decode(
509 environ['HTTP_X_RC_REPO_CONFIG'])
526 environ['HTTP_X_RC_REPO_CONFIG'])
510 config = msgpack.unpackb(packed_config)
527 config = msgpack.unpackb(packed_config)
511
528
512 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
529 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
513 self.set_env_from_config(environ, config)
530 self.set_env_from_config(environ, config)
514
531
515 content_type = environ.get('CONTENT_TYPE', '')
532 content_type = environ.get('CONTENT_TYPE', '')
516
533
517 path = environ['PATH_INFO']
534 path = environ['PATH_INFO']
518 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
535 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
519 log.debug(
536 log.debug(
520 'LFS: Detecting if request `%s` is LFS server path based '
537 'LFS: Detecting if request `%s` is LFS server path based '
521 'on content type:`%s`, is_lfs:%s',
538 'on content type:`%s`, is_lfs:%s',
522 path, content_type, is_lfs_request)
539 path, content_type, is_lfs_request)
523
540
524 if not is_lfs_request:
541 if not is_lfs_request:
525 # fallback detection by path
542 # fallback detection by path
526 if GIT_LFS_PROTO_PAT.match(path):
543 if GIT_LFS_PROTO_PAT.match(path):
527 is_lfs_request = True
544 is_lfs_request = True
528 log.debug(
545 log.debug(
529 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
546 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
530 path, is_lfs_request)
547 path, is_lfs_request)
531
548
532 if is_lfs_request:
549 if is_lfs_request:
533 app = scm_app.create_git_lfs_wsgi_app(
550 app = scm_app.create_git_lfs_wsgi_app(
534 repo_path, repo_name, config)
551 repo_path, repo_name, config)
535 else:
552 else:
536 app = scm_app.create_git_wsgi_app(
553 app = scm_app.create_git_wsgi_app(
537 repo_path, repo_name, config)
554 repo_path, repo_name, config)
538
555
539 log.debug('http-app: starting app handler '
556 log.debug('http-app: starting app handler '
540 'with %s and process request', app)
557 'with %s and process request', app)
541
558
542 return app(environ, start_response)
559 return app(environ, start_response)
543
560
544 return _git_stream
561 return _git_stream
545
562
546 def is_vcs_view(self, context, request):
547 """
548 View predicate that returns true if given backend is supported by
549 defined remotes.
550 """
551 backend = request.matchdict.get('backend')
552 return backend in self._remotes
553
554 def handle_vcs_exception(self, exception, request):
563 def handle_vcs_exception(self, exception, request):
555 _vcs_kind = getattr(exception, '_vcs_kind', '')
564 _vcs_kind = getattr(exception, '_vcs_kind', '')
556 if _vcs_kind == 'repo_locked':
565 if _vcs_kind == 'repo_locked':
557 # Get custom repo-locked status code if present.
566 # Get custom repo-locked status code if present.
558 status_code = request.headers.get('X-RC-Locked-Status-Code')
567 status_code = request.headers.get('X-RC-Locked-Status-Code')
559 return HTTPRepoLocked(
568 return HTTPRepoLocked(
560 title=exception.message, status_code=status_code)
569 title=exception.message, status_code=status_code)
561
570
562 elif _vcs_kind == 'repo_branch_protected':
571 elif _vcs_kind == 'repo_branch_protected':
563 # Get custom repo-branch-protected status code if present.
572 # Get custom repo-branch-protected status code if present.
564 return HTTPRepoBranchProtected(title=exception.message)
573 return HTTPRepoBranchProtected(title=exception.message)
565
574
566 exc_info = request.exc_info
575 exc_info = request.exc_info
567 store_exception(id(exc_info), exc_info)
576 store_exception(id(exc_info), exc_info)
568
577
569 traceback_info = 'unavailable'
578 traceback_info = 'unavailable'
570 if request.exc_info:
579 if request.exc_info:
571 exc_type, exc_value, exc_tb = request.exc_info
580 exc_type, exc_value, exc_tb = request.exc_info
572 traceback_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
581 traceback_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
573
582
574 log.error(
583 log.error(
575 'error occurred handling this request for path: %s, \n tb: %s',
584 'error occurred handling this request for path: %s, \n tb: %s',
576 request.path, traceback_info)
585 request.path, traceback_info)
577 raise exception
586 raise exception
578
587
579
588
580 class ResponseFilter(object):
589 class ResponseFilter(object):
581
590
582 def __init__(self, start_response):
591 def __init__(self, start_response):
583 self._start_response = start_response
592 self._start_response = start_response
584
593
585 def __call__(self, status, response_headers, exc_info=None):
594 def __call__(self, status, response_headers, exc_info=None):
586 headers = tuple(
595 headers = tuple(
587 (h, v) for h, v in response_headers
596 (h, v) for h, v in response_headers
588 if not wsgiref.util.is_hop_by_hop(h))
597 if not wsgiref.util.is_hop_by_hop(h))
589 return self._start_response(status, headers, exc_info)
598 return self._start_response(status, headers, exc_info)
590
599
591
600
592 def main(global_config, **settings):
601 def main(global_config, **settings):
593 if MercurialFactory:
602 if MercurialFactory:
594 hgpatches.patch_largefiles_capabilities()
603 hgpatches.patch_largefiles_capabilities()
595 hgpatches.patch_subrepo_type_mapping()
604 hgpatches.patch_subrepo_type_mapping()
596
605
597 app = HTTPApplication(settings=settings, global_config=global_config)
606 app = HTTPApplication(settings=settings, global_config=global_config)
598 return app.wsgi_app()
607 return app.wsgi_app()
@@ -1,122 +1,132 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2018 RhodeCode GmbH
2 # Copyright (C) 2014-2018 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import io
18 import io
19 import os
19 import os
20 import sys
20 import sys
21
21
22 import pytest
22 import pytest
23
23
24 from vcsserver import subprocessio
24 from vcsserver import subprocessio
25
25
26
26
27 @pytest.fixture(scope='module')
27 @pytest.fixture(scope='module')
28 def environ():
28 def environ():
29 """Delete coverage variables, as they make the tests fail."""
29 """Delete coverage variables, as they make the tests fail."""
30 env = dict(os.environ)
30 env = dict(os.environ)
31 for key in env.keys():
31 for key in env.keys():
32 if key.startswith('COV_CORE_'):
32 if key.startswith('COV_CORE_'):
33 del env[key]
33 del env[key]
34
34
35 return env
35 return env
36
36
37
37
38 def _get_python_args(script):
38 def _get_python_args(script):
39 return [sys.executable, '-c',
39 return [sys.executable, '-c', 'import sys; import time; import shutil; ' + script]
40 'import sys; import time; import shutil; ' + script]
41
40
42
41
43 def test_raise_exception_on_non_zero_return_code(environ):
42 def test_raise_exception_on_non_zero_return_code(environ):
44 args = _get_python_args('sys.exit(1)')
43 args = _get_python_args('sys.exit(1)')
45 with pytest.raises(EnvironmentError):
44 with pytest.raises(EnvironmentError):
46 list(subprocessio.SubprocessIOChunker(args, shell=False, env=environ))
45 list(subprocessio.SubprocessIOChunker(args, shell=False, env=environ))
47
46
48
47
49 def test_does_not_fail_on_non_zero_return_code(environ):
48 def test_does_not_fail_on_non_zero_return_code(environ):
50 args = _get_python_args('sys.exit(1)')
49 args = _get_python_args('sys.exit(1)')
51 output = ''.join(subprocessio.SubprocessIOChunker(
50 output = ''.join(
52 args, shell=False, fail_on_return_code=False, env=environ))
51 subprocessio.SubprocessIOChunker(
52 args, shell=False, fail_on_return_code=False, env=environ
53 )
54 )
53
55
54 assert output == ''
56 assert output == ''
55
57
56
58
57 def test_raise_exception_on_stderr(environ):
59 def test_raise_exception_on_stderr(environ):
58 args = _get_python_args('sys.stderr.write("X"); time.sleep(1);')
60 args = _get_python_args('sys.stderr.write("X"); time.sleep(1);')
59 with pytest.raises(EnvironmentError) as excinfo:
61 with pytest.raises(EnvironmentError) as excinfo:
60 list(subprocessio.SubprocessIOChunker(args, shell=False, env=environ))
62 list(subprocessio.SubprocessIOChunker(args, shell=False, env=environ))
61
63
62 assert 'exited due to an error:\nX' in str(excinfo.value)
64 assert 'exited due to an error:\nX' in str(excinfo.value)
63
65
64
66
65 def test_does_not_fail_on_stderr(environ):
67 def test_does_not_fail_on_stderr(environ):
66 args = _get_python_args('sys.stderr.write("X"); time.sleep(1);')
68 args = _get_python_args('sys.stderr.write("X"); time.sleep(1);')
67 output = ''.join(subprocessio.SubprocessIOChunker(
69 output = ''.join(
68 args, shell=False, fail_on_stderr=False, env=environ))
70 subprocessio.SubprocessIOChunker(
71 args, shell=False, fail_on_stderr=False, env=environ
72 )
73 )
69
74
70 assert output == ''
75 assert output == ''
71
76
72
77
73 @pytest.mark.parametrize('size', [1, 10**5])
78 @pytest.mark.parametrize('size', [1, 10 ** 5])
74 def test_output_with_no_input(size, environ):
79 def test_output_with_no_input(size, environ):
75 print type(environ)
80 print(type(environ))
76 data = 'X'
81 data = 'X'
77 args = _get_python_args('sys.stdout.write("%s" * %d)' % (data, size))
82 args = _get_python_args('sys.stdout.write("%s" * %d)' % (data, size))
78 output = ''.join(subprocessio.SubprocessIOChunker(
83 output = ''.join(subprocessio.SubprocessIOChunker(args, shell=False, env=environ))
79 args, shell=False, env=environ))
80
84
81 assert output == data * size
85 assert output == data * size
82
86
83
87
84 @pytest.mark.parametrize('size', [1, 10**5])
88 @pytest.mark.parametrize('size', [1, 10 ** 5])
85 def test_output_with_no_input_does_not_fail(size, environ):
89 def test_output_with_no_input_does_not_fail(size, environ):
86 data = 'X'
90 data = 'X'
87 args = _get_python_args(
91 args = _get_python_args('sys.stdout.write("%s" * %d); sys.exit(1)' % (data, size))
88 'sys.stdout.write("%s" * %d); sys.exit(1)' % (data, size))
92 output = ''.join(
89 output = ''.join(subprocessio.SubprocessIOChunker(
93 subprocessio.SubprocessIOChunker(
90 args, shell=False, fail_on_return_code=False, env=environ))
94 args, shell=False, fail_on_return_code=False, env=environ
95 )
96 )
91
97
92 print len(data * size), len(output)
98 print("{} {}".format(len(data * size), len(output)))
93 assert output == data * size
99 assert output == data * size
94
100
95
101
96 @pytest.mark.parametrize('size', [1, 10**5])
102 @pytest.mark.parametrize('size', [1, 10 ** 5])
97 def test_output_with_input(size, environ):
103 def test_output_with_input(size, environ):
98 data = 'X' * size
104 data = 'X' * size
99 inputstream = io.BytesIO(data)
105 inputstream = io.BytesIO(data)
100 # This acts like the cat command.
106 # This acts like the cat command.
101 args = _get_python_args('shutil.copyfileobj(sys.stdin, sys.stdout)')
107 args = _get_python_args('shutil.copyfileobj(sys.stdin, sys.stdout)')
102 output = ''.join(subprocessio.SubprocessIOChunker(
108 output = ''.join(
103 args, shell=False, inputstream=inputstream, env=environ))
109 subprocessio.SubprocessIOChunker(
110 args, shell=False, inputstream=inputstream, env=environ
111 )
112 )
104
113
105 print len(data), len(output)
114 print("{} {}".format(len(data * size), len(output)))
106 assert output == data
115 assert output == data
107
116
108
117
109 @pytest.mark.parametrize('size', [1, 10**5])
118 @pytest.mark.parametrize('size', [1, 10 ** 5])
110 def test_output_with_input_skipping_iterator(size, environ):
119 def test_output_with_input_skipping_iterator(size, environ):
111 data = 'X' * size
120 data = 'X' * size
112 inputstream = io.BytesIO(data)
121 inputstream = io.BytesIO(data)
113 # This acts like the cat command.
122 # This acts like the cat command.
114 args = _get_python_args('shutil.copyfileobj(sys.stdin, sys.stdout)')
123 args = _get_python_args('shutil.copyfileobj(sys.stdin, sys.stdout)')
115
124
116 # Note: assigning the chunker makes sure that it is not deleted too early
125 # Note: assigning the chunker makes sure that it is not deleted too early
117 chunker = subprocessio.SubprocessIOChunker(
126 chunker = subprocessio.SubprocessIOChunker(
118 args, shell=False, inputstream=inputstream, env=environ)
127 args, shell=False, inputstream=inputstream, env=environ
128 )
119 output = ''.join(chunker.output)
129 output = ''.join(chunker.output)
120
130
121 print len(data), len(output)
131 print("{} {}".format(len(data * size), len(output)))
122 assert output == data
132 assert output == data
General Comments 0
You need to be logged in to leave comments. Login now