##// END OF EJS Templates
release: Merge default into stable for release preparation
marcink -
r37:897dca31 merge stable
parent child Browse files
Show More
@@ -1,6 +1,6 b''
1 1 [bumpversion]
2 current_version = 4.1.2
2 current_version = 4.2.0
3 3 message = release: Bump version {current_version} to {new_version}
4 4
5 5 [bumpversion:file:vcsserver/VERSION]
6 6
@@ -1,16 +1,14 b''
1 1 [DEFAULT]
2 2 done = false
3 3
4 4 [task:bump_version]
5 5 done = true
6 6
7 7 [task:fixes_on_stable]
8 done = true
9 8
10 9 [task:pip2nix_generated]
11 done = true
12 10
13 11 [release]
14 state = prepared
15 version = 4.1.2
12 state = in_progress
13 version = 4.2.0
16 14
@@ -1,144 +1,145 b''
1 1 # Nix environment for the community edition
2 2 #
3 3 # This shall be as lean as possible, just producing the rhodecode-vcsserver
4 4 # derivation. For advanced tweaks to pimp up the development environment we use
5 5 # "shell.nix" so that it does not have to clutter this file.
6 6
7 7 { pkgs ? (import <nixpkgs> {})
8 8 , pythonPackages ? "python27Packages"
9 9 , pythonExternalOverrides ? self: super: {}
10 10 , doCheck ? true
11 11 }:
12 12
13 13 let pkgs_ = pkgs; in
14 14
15 15 let
16 16 pkgs = pkgs_.overridePackages (self: super: {
17 17 # Override subversion derivation to
18 18 # - activate python bindings
19 19 # - set version to 1.8
20 20 subversion = super.subversion18.override {
21 21 httpSupport = true;
22 22 pythonBindings = true;
23 23 python = self.python27Packages.python;
24 24 };
25 25 });
26 26
27 27 inherit (pkgs.lib) fix extends;
28 28
29 29 basePythonPackages = with builtins; if isAttrs pythonPackages
30 30 then pythonPackages
31 31 else getAttr pythonPackages pkgs;
32 32
33 33 elem = builtins.elem;
34 34 basename = path: with pkgs.lib; last (splitString "/" path);
35 35 startsWith = prefix: full: let
36 36 actualPrefix = builtins.substring 0 (builtins.stringLength prefix) full;
37 37 in actualPrefix == prefix;
38 38
39 39 src-filter = path: type: with pkgs.lib;
40 40 let
41 41 ext = last (splitString "." path);
42 42 in
43 43 !elem (basename path) [
44 44 ".git" ".hg" "__pycache__" ".eggs" "node_modules"
45 45 "build" "data" "tmp"] &&
46 46 !elem ext ["egg-info" "pyc"] &&
47 47 !startsWith "result" path;
48 48
49 49 rhodecode-vcsserver-src = builtins.filterSource src-filter ./.;
50 50
51 51 pythonGeneratedPackages = self: basePythonPackages.override (a: {
52 52 inherit self;
53 53 })
54 54 // (scopedImport {
55 55 self = self;
56 56 super = basePythonPackages;
57 57 inherit pkgs;
58 58 inherit (pkgs) fetchurl fetchgit;
59 59 } ./pkgs/python-packages.nix);
60 60
61 61 pythonOverrides = import ./pkgs/python-packages-overrides.nix {
62 62 inherit
63 63 basePythonPackages
64 64 pkgs;
65 65 };
66 66
67 67 version = builtins.readFile ./vcsserver/VERSION;
68 68
69 69 pythonLocalOverrides = self: super: {
70 70 rhodecode-vcsserver = super.rhodecode-vcsserver.override (attrs: {
71 71 inherit
72 72 doCheck
73 73 version;
74 74 name = "rhodecode-vcsserver-${version}";
75 releaseName = "RhodeCodeVCSServer-${version}";
75 76 src = rhodecode-vcsserver-src;
76 77
77 78 propagatedBuildInputs = attrs.propagatedBuildInputs ++ ([
78 79 pkgs.git
79 80 pkgs.subversion
80 81 ]);
81 82
82 83 # TODO: johbo: Make a nicer way to expose the parts. Maybe
83 84 # pkgs/default.nix?
84 85 passthru = {
85 86 pythonPackages = self;
86 87 };
87 88
88 89 # Somewhat snappier setup of the development environment
89 90 # TODO: move into shell.nix
90 91 # TODO: think of supporting a stable path again, so that multiple shells
91 92 # can share it.
92 93 shellHook = ''
93 94 # Set locale
94 95 export LC_ALL="en_US.UTF-8"
95 96
96 97 tmp_path=$(mktemp -d)
97 98 export PATH="$tmp_path/bin:$PATH"
98 99 export PYTHONPATH="$tmp_path/${self.python.sitePackages}:$PYTHONPATH"
99 100 mkdir -p $tmp_path/${self.python.sitePackages}
100 101 python setup.py develop --prefix $tmp_path --allow-hosts ""
101 102 '';
102 103
103 104 # Add VCSServer bin directory to path so that tests can find 'vcsserver'.
104 105 preCheck = ''
105 106 export PATH="$out/bin:$PATH"
106 107 '';
107 108
108 109 postInstall = ''
109 110 echo "Writing meta information for rccontrol to nix-support/rccontrol"
110 111 mkdir -p $out/nix-support/rccontrol
111 112 cp -v vcsserver/VERSION $out/nix-support/rccontrol/version
112 113 echo "DONE: Meta information for rccontrol written"
113 114
114 115 ln -s ${self.pyramid}/bin/* $out/bin #*/
115 116 ln -s ${self.gunicorn}/bin/gunicorn $out/bin/
116 117
117 118 # Symlink version control utilities
118 119 #
119 120 # We ensure that always the correct version is available as a symlink.
120 121 # So that users calling them via the profile path will always use the
121 122 # correct version.
122 123 ln -s ${pkgs.git}/bin/git $out/bin
123 124 ln -s ${self.mercurial}/bin/hg $out/bin
124 125 ln -s ${pkgs.subversion}/bin/svn* $out/bin
125 126
126 127 for file in $out/bin/*; do #*/
127 128 wrapProgram $file \
128 129 --prefix PYTHONPATH : $PYTHONPATH \
129 130 --set PYTHONHASHSEED random
130 131 done
131 132 '';
132 133
133 134 });
134 135 };
135 136
136 137 # Apply all overrides and fix the final package set
137 138 myPythonPackages =
138 139 (fix
139 140 (extends pythonExternalOverrides
140 141 (extends pythonLocalOverrides
141 142 (extends pythonOverrides
142 143 pythonGeneratedPackages))));
143 144
144 145 in myPythonPackages.rhodecode-vcsserver
@@ -1,363 +1,363 b''
1 1 {
2 2 Beaker = super.buildPythonPackage {
3 3 name = "Beaker-1.7.0";
4 4 buildInputs = with self; [];
5 5 doCheck = false;
6 6 propagatedBuildInputs = with self; [];
7 7 src = fetchurl {
8 8 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
9 9 md5 = "386be3f7fe427358881eee4622b428b3";
10 10 };
11 11 };
12 12 Jinja2 = super.buildPythonPackage {
13 13 name = "Jinja2-2.8";
14 14 buildInputs = with self; [];
15 15 doCheck = false;
16 16 propagatedBuildInputs = with self; [MarkupSafe];
17 17 src = fetchurl {
18 18 url = "https://pypi.python.org/packages/f2/2f/0b98b06a345a761bec91a079ccae392d282690c2d8272e708f4d10829e22/Jinja2-2.8.tar.gz";
19 19 md5 = "edb51693fe22c53cee5403775c71a99e";
20 20 };
21 21 };
22 22 Mako = super.buildPythonPackage {
23 23 name = "Mako-1.0.4";
24 24 buildInputs = with self; [];
25 25 doCheck = false;
26 26 propagatedBuildInputs = with self; [MarkupSafe];
27 27 src = fetchurl {
28 28 url = "https://pypi.python.org/packages/7a/ae/925434246ee90b42e8ef57d3b30a0ab7caf9a2de3e449b876c56dcb48155/Mako-1.0.4.tar.gz";
29 29 md5 = "c5fc31a323dd4990683d2f2da02d4e20";
30 30 };
31 31 };
32 32 MarkupSafe = super.buildPythonPackage {
33 33 name = "MarkupSafe-0.23";
34 34 buildInputs = with self; [];
35 35 doCheck = false;
36 36 propagatedBuildInputs = with self; [];
37 37 src = fetchurl {
38 38 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
39 39 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
40 40 };
41 41 };
42 42 PasteDeploy = super.buildPythonPackage {
43 43 name = "PasteDeploy-1.5.2";
44 44 buildInputs = with self; [];
45 45 doCheck = false;
46 46 propagatedBuildInputs = with self; [];
47 47 src = fetchurl {
48 48 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
49 49 md5 = "352b7205c78c8de4987578d19431af3b";
50 50 };
51 51 };
52 52 Pyro4 = super.buildPythonPackage {
53 53 name = "Pyro4-4.41";
54 54 buildInputs = with self; [];
55 55 doCheck = false;
56 56 propagatedBuildInputs = with self; [serpent];
57 57 src = fetchurl {
58 58 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
59 59 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
60 60 };
61 61 };
62 62 WebOb = super.buildPythonPackage {
63 63 name = "WebOb-1.3.1";
64 64 buildInputs = with self; [];
65 65 doCheck = false;
66 66 propagatedBuildInputs = with self; [];
67 67 src = fetchurl {
68 68 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
69 69 md5 = "20918251c5726956ba8fef22d1556177";
70 70 };
71 71 };
72 72 WebTest = super.buildPythonPackage {
73 73 name = "WebTest-1.4.3";
74 74 buildInputs = with self; [];
75 75 doCheck = false;
76 76 propagatedBuildInputs = with self; [WebOb];
77 77 src = fetchurl {
78 78 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
79 79 md5 = "631ce728bed92c681a4020a36adbc353";
80 80 };
81 81 };
82 82 configobj = super.buildPythonPackage {
83 83 name = "configobj-5.0.6";
84 84 buildInputs = with self; [];
85 85 doCheck = false;
86 86 propagatedBuildInputs = with self; [six];
87 87 src = fetchurl {
88 88 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
89 89 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
90 90 };
91 91 };
92 92 dulwich = super.buildPythonPackage {
93 name = "dulwich-0.12.0";
93 name = "dulwich-0.13.0";
94 94 buildInputs = with self; [];
95 95 doCheck = false;
96 96 propagatedBuildInputs = with self; [];
97 97 src = fetchurl {
98 url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz";
99 md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa";
98 url = "https://pypi.python.org/packages/84/95/732d280eee829dacc954e8109f97b47abcadcca472c2ab013e1635eb4792/dulwich-0.13.0.tar.gz";
99 md5 = "6dede0626657c2bd08f48ca1221eea91";
100 100 };
101 101 };
102 102 greenlet = super.buildPythonPackage {
103 103 name = "greenlet-0.4.7";
104 104 buildInputs = with self; [];
105 105 doCheck = false;
106 106 propagatedBuildInputs = with self; [];
107 107 src = fetchurl {
108 108 url = "https://pypi.python.org/packages/7a/9f/a1a0d9bdf3203ae1502c5a8434fe89d323599d78a106985bc327351a69d4/greenlet-0.4.7.zip";
109 109 md5 = "c2333a8ff30fa75c5d5ec0e67b461086";
110 110 };
111 111 };
112 112 gunicorn = super.buildPythonPackage {
113 113 name = "gunicorn-19.6.0";
114 114 buildInputs = with self; [];
115 115 doCheck = false;
116 116 propagatedBuildInputs = with self; [];
117 117 src = fetchurl {
118 118 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
119 119 md5 = "338e5e8a83ea0f0625f768dba4597530";
120 120 };
121 121 };
122 122 hgsubversion = super.buildPythonPackage {
123 123 name = "hgsubversion-1.8.6";
124 124 buildInputs = with self; [];
125 125 doCheck = false;
126 126 propagatedBuildInputs = with self; [mercurial subvertpy];
127 127 src = fetchurl {
128 128 url = "https://pypi.python.org/packages/ce/97/032e5093ad250e9908cea04395cbddb6902d587f712a79b53b2d778bdfdd/hgsubversion-1.8.6.tar.gz";
129 129 md5 = "9310cb266031cf8d0779885782a84a5b";
130 130 };
131 131 };
132 132 infrae.cache = super.buildPythonPackage {
133 133 name = "infrae.cache-1.0.1";
134 134 buildInputs = with self; [];
135 135 doCheck = false;
136 136 propagatedBuildInputs = with self; [Beaker repoze.lru];
137 137 src = fetchurl {
138 138 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
139 139 md5 = "b09076a766747e6ed2a755cc62088e32";
140 140 };
141 141 };
142 142 mercurial = super.buildPythonPackage {
143 143 name = "mercurial-3.8.3";
144 144 buildInputs = with self; [];
145 145 doCheck = false;
146 146 propagatedBuildInputs = with self; [];
147 147 src = fetchurl {
148 148 url = "https://pypi.python.org/packages/56/bc/af1561195d43638d44bc3ac286c21f187430966234bee1f235711d80dfb6/mercurial-3.8.3.tar.gz";
149 149 md5 = "97aced7018614eeccc9621a3dea35fda";
150 150 };
151 151 };
152 152 mock = super.buildPythonPackage {
153 153 name = "mock-1.0.1";
154 154 buildInputs = with self; [];
155 155 doCheck = false;
156 156 propagatedBuildInputs = with self; [];
157 157 src = fetchurl {
158 158 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
159 159 md5 = "869f08d003c289a97c1a6610faf5e913";
160 160 };
161 161 };
162 162 msgpack-python = super.buildPythonPackage {
163 163 name = "msgpack-python-0.4.6";
164 164 buildInputs = with self; [];
165 165 doCheck = false;
166 166 propagatedBuildInputs = with self; [];
167 167 src = fetchurl {
168 168 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
169 169 md5 = "8b317669314cf1bc881716cccdaccb30";
170 170 };
171 171 };
172 172 py = super.buildPythonPackage {
173 173 name = "py-1.4.29";
174 174 buildInputs = with self; [];
175 175 doCheck = false;
176 176 propagatedBuildInputs = with self; [];
177 177 src = fetchurl {
178 178 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
179 179 md5 = "c28e0accba523a29b35a48bb703fb96c";
180 180 };
181 181 };
182 182 pyramid = super.buildPythonPackage {
183 183 name = "pyramid-1.6.1";
184 184 buildInputs = with self; [];
185 185 doCheck = false;
186 186 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
187 187 src = fetchurl {
188 188 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
189 189 md5 = "b18688ff3cc33efdbb098a35b45dd122";
190 190 };
191 191 };
192 192 pyramid-jinja2 = super.buildPythonPackage {
193 193 name = "pyramid-jinja2-2.5";
194 194 buildInputs = with self; [];
195 195 doCheck = false;
196 196 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
197 197 src = fetchurl {
198 198 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
199 199 md5 = "07cb6547204ac5e6f0b22a954ccee928";
200 200 };
201 201 };
202 202 pyramid-mako = super.buildPythonPackage {
203 203 name = "pyramid-mako-1.0.2";
204 204 buildInputs = with self; [];
205 205 doCheck = false;
206 206 propagatedBuildInputs = with self; [pyramid Mako];
207 207 src = fetchurl {
208 208 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
209 209 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
210 210 };
211 211 };
212 212 pytest = super.buildPythonPackage {
213 213 name = "pytest-2.8.5";
214 214 buildInputs = with self; [];
215 215 doCheck = false;
216 216 propagatedBuildInputs = with self; [py];
217 217 src = fetchurl {
218 218 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
219 219 md5 = "8493b06f700862f1294298d6c1b715a9";
220 220 };
221 221 };
222 222 repoze.lru = super.buildPythonPackage {
223 223 name = "repoze.lru-0.6";
224 224 buildInputs = with self; [];
225 225 doCheck = false;
226 226 propagatedBuildInputs = with self; [];
227 227 src = fetchurl {
228 228 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
229 229 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
230 230 };
231 231 };
232 232 rhodecode-vcsserver = super.buildPythonPackage {
233 233 name = "rhodecode-vcsserver-4.1.2";
234 234 buildInputs = with self; [mock pytest WebTest];
235 235 doCheck = true;
236 236 propagatedBuildInputs = with self; [configobj dulwich hgsubversion infrae.cache mercurial msgpack-python pyramid Pyro4 simplejson subprocess32 waitress WebOb];
237 237 src = ./.;
238 238 };
239 239 serpent = super.buildPythonPackage {
240 240 name = "serpent-1.12";
241 241 buildInputs = with self; [];
242 242 doCheck = false;
243 243 propagatedBuildInputs = with self; [];
244 244 src = fetchurl {
245 245 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
246 246 md5 = "05869ac7b062828b34f8f927f0457b65";
247 247 };
248 248 };
249 249 setuptools = super.buildPythonPackage {
250 250 name = "setuptools-20.8.1";
251 251 buildInputs = with self; [];
252 252 doCheck = false;
253 253 propagatedBuildInputs = with self; [];
254 254 src = fetchurl {
255 255 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
256 256 md5 = "fe58a5cac0df20bb83942b252a4b0543";
257 257 };
258 258 };
259 259 simplejson = super.buildPythonPackage {
260 260 name = "simplejson-3.7.2";
261 261 buildInputs = with self; [];
262 262 doCheck = false;
263 263 propagatedBuildInputs = with self; [];
264 264 src = fetchurl {
265 265 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
266 266 md5 = "a5fc7d05d4cb38492285553def5d4b46";
267 267 };
268 268 };
269 269 six = super.buildPythonPackage {
270 270 name = "six-1.9.0";
271 271 buildInputs = with self; [];
272 272 doCheck = false;
273 273 propagatedBuildInputs = with self; [];
274 274 src = fetchurl {
275 275 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
276 276 md5 = "476881ef4012262dfc8adc645ee786c4";
277 277 };
278 278 };
279 279 subprocess32 = super.buildPythonPackage {
280 280 name = "subprocess32-3.2.6";
281 281 buildInputs = with self; [];
282 282 doCheck = false;
283 283 propagatedBuildInputs = with self; [];
284 284 src = fetchurl {
285 285 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
286 286 md5 = "754c5ab9f533e764f931136974b618f1";
287 287 };
288 288 };
289 289 subvertpy = super.buildPythonPackage {
290 290 name = "subvertpy-0.9.3";
291 291 buildInputs = with self; [];
292 292 doCheck = false;
293 293 propagatedBuildInputs = with self; [];
294 294 src = fetchurl {
295 295 url = "https://github.com/jelmer/subvertpy/archive/subvertpy-0.9.3.tar.gz";
296 296 md5 = "7b745a47128050ea5a73efcd913ec1cf";
297 297 };
298 298 };
299 299 translationstring = super.buildPythonPackage {
300 300 name = "translationstring-1.3";
301 301 buildInputs = with self; [];
302 302 doCheck = false;
303 303 propagatedBuildInputs = with self; [];
304 304 src = fetchurl {
305 305 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
306 306 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
307 307 };
308 308 };
309 309 venusian = super.buildPythonPackage {
310 310 name = "venusian-1.0";
311 311 buildInputs = with self; [];
312 312 doCheck = false;
313 313 propagatedBuildInputs = with self; [];
314 314 src = fetchurl {
315 315 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
316 316 md5 = "dccf2eafb7113759d60c86faf5538756";
317 317 };
318 318 };
319 319 waitress = super.buildPythonPackage {
320 320 name = "waitress-0.8.9";
321 321 buildInputs = with self; [];
322 322 doCheck = false;
323 323 propagatedBuildInputs = with self; [setuptools];
324 324 src = fetchurl {
325 325 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
326 326 md5 = "da3f2e62b3676be5dd630703a68e2a04";
327 327 };
328 328 };
329 329 wheel = super.buildPythonPackage {
330 330 name = "wheel-0.29.0";
331 331 buildInputs = with self; [];
332 332 doCheck = false;
333 333 propagatedBuildInputs = with self; [];
334 334 src = fetchurl {
335 335 url = "https://pypi.python.org/packages/c9/1d/bd19e691fd4cfe908c76c429fe6e4436c9e83583c4414b54f6c85471954a/wheel-0.29.0.tar.gz";
336 336 md5 = "555a67e4507cedee23a0deb9651e452f";
337 337 };
338 338 };
339 339 zope.deprecation = super.buildPythonPackage {
340 340 name = "zope.deprecation-4.1.1";
341 341 buildInputs = with self; [];
342 342 doCheck = false;
343 343 propagatedBuildInputs = with self; [setuptools];
344 344 src = fetchurl {
345 345 url = "https://pypi.python.org/packages/c5/c9/e760f131fcde817da6c186a3f4952b8f206b7eeb269bb6f0836c715c5f20/zope.deprecation-4.1.1.tar.gz";
346 346 md5 = "ce261b9384066f7e13b63525778430cb";
347 347 };
348 348 };
349 349 zope.interface = super.buildPythonPackage {
350 350 name = "zope.interface-4.1.3";
351 351 buildInputs = with self; [];
352 352 doCheck = false;
353 353 propagatedBuildInputs = with self; [setuptools];
354 354 src = fetchurl {
355 355 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
356 356 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
357 357 };
358 358 };
359 359
360 360 ### Test requirements
361 361
362 362
363 363 }
@@ -1,13 +1,15 b''
1 1 { pkgs ? import <nixpkgs> {}
2 , doCheck ? true
2 3 }:
3 4
4 5 let
5 6
6 7 vcsserver = import ./default.nix {
7 8 inherit
9 doCheck
8 10 pkgs;
9 11 };
10 12
11 13 in {
12 14 build = vcsserver;
13 15 }
@@ -1,34 +1,34 b''
1 1 Beaker==1.7.0
2 2 configobj==5.0.6
3 dulwich==0.12.0
3 dulwich==0.13.0
4 4 hgsubversion==1.8.6
5 5 infrae.cache==1.0.1
6 6 mercurial==3.8.3
7 7 msgpack-python==0.4.6
8 8 py==1.4.29
9 9 pyramid==1.6.1
10 10 pyramid-jinja2==2.5
11 11 pyramid-mako==1.0.2
12 12 Pyro4==4.41
13 13 pytest==2.8.5
14 14 repoze.lru==0.6
15 15 serpent==1.12
16 16 setuptools==20.8.1
17 17 simplejson==3.7.2
18 18 subprocess32==3.2.6
19 19 # TODO: johbo: This version is not in source on PyPI currently,
20 20 # change back once this or a future version is available
21 21 https://github.com/jelmer/subvertpy/archive/subvertpy-0.9.3.tar.gz#md5=7b745a47128050ea5a73efcd913ec1cf
22 22 six==1.9.0
23 23 translationstring==1.3
24 24 waitress==0.8.9
25 25 WebOb==1.3.1
26 26 wheel==0.29.0
27 27 zope.deprecation==4.1.1
28 28 zope.interface==4.1.3
29 29 greenlet==0.4.7
30 30 gunicorn==19.6.0
31 31
32 32 # Test related requirements
33 33 mock==1.0.1
34 34 WebTest==1.4.3
@@ -1,13 +1,18 b''
1 { pkgs ? (import <nixpkgs> {})
1 { pkgs ? import <nixpkgs> {}
2 , doCheck ? false
2 3 }:
3 4
4 5 let
5 vcsserver = import ./default.nix {inherit pkgs;};
6 vcsserver = import ./default.nix {
7 inherit
8 doCheck
9 pkgs;
10 };
6 11
7 12 in vcsserver.override (attrs: {
8 13
9 14 # Avoid that we dump any sources into the store when entering the shell and
10 15 # make development a little bit more convenient.
11 16 src = null;
12 17
13 18 })
@@ -1,1 +1,1 b''
1 4.1.2 No newline at end of file
1 4.2.0 No newline at end of file
@@ -1,692 +1,700 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2016 RodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import io
19 19 import logging
20 20 import stat
21 21 import sys
22 22 import urllib
23 23 import urllib2
24 24
25 25 from hgext import largefiles, rebase
26 26 from hgext.strip import strip as hgext_strip
27 27 from mercurial import commands
28 28 from mercurial import unionrepo
29 29
30 30 from vcsserver import exceptions
31 31 from vcsserver.base import RepoFactory
32 32 from vcsserver.hgcompat import (
33 33 archival, bin, clone, config as hgconfig, diffopts, hex, hg_url,
34 34 httpbasicauthhandler, httpdigestauthhandler, httppeer, localrepository,
35 35 match, memctx, exchange, memfilectx, nullrev, patch, peer, revrange, ui,
36 36 Abort, LookupError, RepoError, RepoLookupError, InterventionRequired,
37 37 RequirementError)
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 def make_ui_from_config(repo_config):
43 43 baseui = ui.ui()
44 44
45 45 # clean the baseui object
46 46 baseui._ocfg = hgconfig.config()
47 47 baseui._ucfg = hgconfig.config()
48 48 baseui._tcfg = hgconfig.config()
49 49
50 50 for section, option, value in repo_config:
51 51 baseui.setconfig(section, option, value)
52 52
53 53 # make our hgweb quiet so it doesn't print output
54 54 baseui.setconfig('ui', 'quiet', 'true')
55 55
56 56 # force mercurial to only use 1 thread, otherwise it may try to set a
57 57 # signal in a non-main thread, thus generating a ValueError.
58 58 baseui.setconfig('worker', 'numcpus', 1)
59 59
60 # If there is no config for the largefiles extension, we explicitly disable
61 # it here. This overrides settings from repositories hgrc file. Recent
62 # mercurial versions enable largefiles in hgrc on clone from largefile
63 # repo.
64 if not baseui.hasconfig('extensions', 'largefiles'):
65 log.debug('Explicitly disable largefiles extension for repo.')
66 baseui.setconfig('extensions', 'largefiles', '!')
67
60 68 return baseui
61 69
62 70
63 71 def reraise_safe_exceptions(func):
64 72 """Decorator for converting mercurial exceptions to something neutral."""
65 73 def wrapper(*args, **kwargs):
66 74 try:
67 75 return func(*args, **kwargs)
68 76 except (Abort, InterventionRequired):
69 77 raise_from_original(exceptions.AbortException)
70 78 except RepoLookupError:
71 79 raise_from_original(exceptions.LookupException)
72 80 except RequirementError:
73 81 raise_from_original(exceptions.RequirementException)
74 82 except RepoError:
75 83 raise_from_original(exceptions.VcsException)
76 84 except LookupError:
77 85 raise_from_original(exceptions.LookupException)
78 86 except Exception as e:
79 87 if not hasattr(e, '_vcs_kind'):
80 88 log.exception("Unhandled exception in hg remote call")
81 89 raise_from_original(exceptions.UnhandledException)
82 90 raise
83 91 return wrapper
84 92
85 93
86 94 def raise_from_original(new_type):
87 95 """
88 96 Raise a new exception type with original args and traceback.
89 97 """
90 98 _, original, traceback = sys.exc_info()
91 99 try:
92 100 raise new_type(*original.args), None, traceback
93 101 finally:
94 102 del traceback
95 103
96 104
97 105 class MercurialFactory(RepoFactory):
98 106
99 107 def _create_config(self, config, hooks=True):
100 108 if not hooks:
101 109 hooks_to_clean = frozenset((
102 110 'changegroup.repo_size', 'preoutgoing.pre_pull',
103 111 'outgoing.pull_logger', 'prechangegroup.pre_push'))
104 112 new_config = []
105 113 for section, option, value in config:
106 114 if section == 'hooks' and option in hooks_to_clean:
107 115 continue
108 116 new_config.append((section, option, value))
109 117 config = new_config
110 118
111 119 baseui = make_ui_from_config(config)
112 120 return baseui
113 121
114 122 def _create_repo(self, wire, create):
115 123 baseui = self._create_config(wire["config"])
116 124 return localrepository(baseui, wire["path"], create)
117 125
118 126
119 127 class HgRemote(object):
120 128
121 129 def __init__(self, factory):
122 130 self._factory = factory
123 131
124 132 self._bulk_methods = {
125 133 "affected_files": self.ctx_files,
126 134 "author": self.ctx_user,
127 135 "branch": self.ctx_branch,
128 136 "children": self.ctx_children,
129 137 "date": self.ctx_date,
130 138 "message": self.ctx_description,
131 139 "parents": self.ctx_parents,
132 140 "status": self.ctx_status,
133 141 "_file_paths": self.ctx_list,
134 142 }
135 143
136 144 @reraise_safe_exceptions
137 145 def archive_repo(self, archive_path, mtime, file_info, kind):
138 146 if kind == "tgz":
139 147 archiver = archival.tarit(archive_path, mtime, "gz")
140 148 elif kind == "tbz2":
141 149 archiver = archival.tarit(archive_path, mtime, "bz2")
142 150 elif kind == 'zip':
143 151 archiver = archival.zipit(archive_path, mtime)
144 152 else:
145 153 raise exceptions.ArchiveException(
146 154 'Remote does not support: "%s".' % kind)
147 155
148 156 for f_path, f_mode, f_is_link, f_content in file_info:
149 157 archiver.addfile(f_path, f_mode, f_is_link, f_content)
150 158 archiver.done()
151 159
152 160 @reraise_safe_exceptions
153 161 def bookmarks(self, wire):
154 162 repo = self._factory.repo(wire)
155 163 return dict(repo._bookmarks)
156 164
157 165 @reraise_safe_exceptions
158 166 def branches(self, wire, normal, closed):
159 167 repo = self._factory.repo(wire)
160 168 iter_branches = repo.branchmap().iterbranches()
161 169 bt = {}
162 170 for branch_name, _heads, tip, is_closed in iter_branches:
163 171 if normal and not is_closed:
164 172 bt[branch_name] = tip
165 173 if closed and is_closed:
166 174 bt[branch_name] = tip
167 175
168 176 return bt
169 177
170 178 @reraise_safe_exceptions
171 179 def bulk_request(self, wire, rev, pre_load):
172 180 result = {}
173 181 for attr in pre_load:
174 182 try:
175 183 method = self._bulk_methods[attr]
176 184 result[attr] = method(wire, rev)
177 185 except KeyError:
178 186 raise exceptions.VcsException(
179 187 'Unknown bulk attribute: "%s"' % attr)
180 188 return result
181 189
182 190 @reraise_safe_exceptions
183 191 def clone(self, wire, source, dest, update_after_clone=False, hooks=True):
184 192 baseui = self._factory._create_config(wire["config"], hooks=hooks)
185 193 clone(baseui, source, dest, noupdate=not update_after_clone)
186 194
187 195 @reraise_safe_exceptions
188 196 def commitctx(
189 197 self, wire, message, parents, commit_time, commit_timezone,
190 198 user, files, extra, removed, updated):
191 199
192 200 def _filectxfn(_repo, memctx, path):
193 201 """
194 202 Marks given path as added/changed/removed in a given _repo. This is
195 203 for internal mercurial commit function.
196 204 """
197 205
198 206 # check if this path is removed
199 207 if path in removed:
200 208 # returning None is a way to mark node for removal
201 209 return None
202 210
203 211 # check if this path is added
204 212 for node in updated:
205 213 if node['path'] == path:
206 214 return memfilectx(
207 215 _repo,
208 216 path=node['path'],
209 217 data=node['content'],
210 218 islink=False,
211 219 isexec=bool(node['mode'] & stat.S_IXUSR),
212 220 copied=False,
213 221 memctx=memctx)
214 222
215 223 raise exceptions.AbortException(
216 224 "Given path haven't been marked as added, "
217 225 "changed or removed (%s)" % path)
218 226
219 227 repo = self._factory.repo(wire)
220 228
221 229 commit_ctx = memctx(
222 230 repo=repo,
223 231 parents=parents,
224 232 text=message,
225 233 files=files,
226 234 filectxfn=_filectxfn,
227 235 user=user,
228 236 date=(commit_time, commit_timezone),
229 237 extra=extra)
230 238
231 239 n = repo.commitctx(commit_ctx)
232 240 new_id = hex(n)
233 241
234 242 return new_id
235 243
236 244 @reraise_safe_exceptions
237 245 def ctx_branch(self, wire, revision):
238 246 repo = self._factory.repo(wire)
239 247 ctx = repo[revision]
240 248 return ctx.branch()
241 249
242 250 @reraise_safe_exceptions
243 251 def ctx_children(self, wire, revision):
244 252 repo = self._factory.repo(wire)
245 253 ctx = repo[revision]
246 254 return [child.rev() for child in ctx.children()]
247 255
248 256 @reraise_safe_exceptions
249 257 def ctx_date(self, wire, revision):
250 258 repo = self._factory.repo(wire)
251 259 ctx = repo[revision]
252 260 return ctx.date()
253 261
254 262 @reraise_safe_exceptions
255 263 def ctx_description(self, wire, revision):
256 264 repo = self._factory.repo(wire)
257 265 ctx = repo[revision]
258 266 return ctx.description()
259 267
260 268 @reraise_safe_exceptions
261 269 def ctx_diff(
262 270 self, wire, revision, git=True, ignore_whitespace=True, context=3):
263 271 repo = self._factory.repo(wire)
264 272 ctx = repo[revision]
265 273 result = ctx.diff(
266 274 git=git, ignore_whitespace=ignore_whitespace, context=context)
267 275 return list(result)
268 276
269 277 @reraise_safe_exceptions
270 278 def ctx_files(self, wire, revision):
271 279 repo = self._factory.repo(wire)
272 280 ctx = repo[revision]
273 281 return ctx.files()
274 282
275 283 @reraise_safe_exceptions
276 284 def ctx_list(self, path, revision):
277 285 repo = self._factory.repo(path)
278 286 ctx = repo[revision]
279 287 return list(ctx)
280 288
281 289 @reraise_safe_exceptions
282 290 def ctx_parents(self, wire, revision):
283 291 repo = self._factory.repo(wire)
284 292 ctx = repo[revision]
285 293 return [parent.rev() for parent in ctx.parents()]
286 294
287 295 @reraise_safe_exceptions
288 296 def ctx_substate(self, wire, revision):
289 297 repo = self._factory.repo(wire)
290 298 ctx = repo[revision]
291 299 return ctx.substate
292 300
293 301 @reraise_safe_exceptions
294 302 def ctx_status(self, wire, revision):
295 303 repo = self._factory.repo(wire)
296 304 ctx = repo[revision]
297 305 status = repo[ctx.p1().node()].status(other=ctx.node())
298 306 # object of status (odd, custom named tuple in mercurial) is not
299 307 # correctly serializable via Pyro, we make it a list, as the underling
300 308 # API expects this to be a list
301 309 return list(status)
302 310
303 311 @reraise_safe_exceptions
304 312 def ctx_user(self, wire, revision):
305 313 repo = self._factory.repo(wire)
306 314 ctx = repo[revision]
307 315 return ctx.user()
308 316
309 317 @reraise_safe_exceptions
310 318 def check_url(self, url, config):
311 319 _proto = None
312 320 if '+' in url[:url.find('://')]:
313 321 _proto = url[0:url.find('+')]
314 322 url = url[url.find('+') + 1:]
315 323 handlers = []
316 324 url_obj = hg_url(url)
317 325 test_uri, authinfo = url_obj.authinfo()
318 326 url_obj.passwd = '*****'
319 327 cleaned_uri = str(url_obj)
320 328
321 329 if authinfo:
322 330 # create a password manager
323 331 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
324 332 passmgr.add_password(*authinfo)
325 333
326 334 handlers.extend((httpbasicauthhandler(passmgr),
327 335 httpdigestauthhandler(passmgr)))
328 336
329 337 o = urllib2.build_opener(*handlers)
330 338 o.addheaders = [('Content-Type', 'application/mercurial-0.1'),
331 339 ('Accept', 'application/mercurial-0.1')]
332 340
333 341 q = {"cmd": 'between'}
334 342 q.update({'pairs': "%s-%s" % ('0' * 40, '0' * 40)})
335 343 qs = '?%s' % urllib.urlencode(q)
336 344 cu = "%s%s" % (test_uri, qs)
337 345 req = urllib2.Request(cu, None, {})
338 346
339 347 try:
340 348 resp = o.open(req)
341 349 if resp.code != 200:
342 350 raise exceptions.URLError('Return Code is not 200')
343 351 except Exception as e:
344 352 # means it cannot be cloned
345 353 raise exceptions.URLError("[%s] org_exc: %s" % (cleaned_uri, e))
346 354
347 355 # now check if it's a proper hg repo, but don't do it for svn
348 356 try:
349 357 if _proto == 'svn':
350 358 pass
351 359 else:
352 360 # check for pure hg repos
353 361 httppeer(make_ui_from_config(config), url).lookup('tip')
354 362 except Exception as e:
355 363 raise exceptions.URLError(
356 364 "url [%s] does not look like an hg repo org_exc: %s"
357 365 % (cleaned_uri, e))
358 366
359 367 return True
360 368
361 369 @reraise_safe_exceptions
362 370 def diff(
363 371 self, wire, rev1, rev2, file_filter, opt_git, opt_ignorews,
364 372 context):
365 373 repo = self._factory.repo(wire)
366 374
367 375 if file_filter:
368 376 filter = match(file_filter[0], '', [file_filter[1]])
369 377 else:
370 378 filter = file_filter
371 379 opts = diffopts(git=opt_git, ignorews=opt_ignorews, context=context)
372 380
373 381 try:
374 382 return "".join(patch.diff(
375 383 repo, node1=rev1, node2=rev2, match=filter, opts=opts))
376 384 except RepoLookupError:
377 385 raise exceptions.LookupException()
378 386
379 387 @reraise_safe_exceptions
380 388 def file_history(self, wire, revision, path, limit):
381 389 repo = self._factory.repo(wire)
382 390
383 391 ctx = repo[revision]
384 392 fctx = ctx.filectx(path)
385 393
386 394 def history_iter():
387 395 limit_rev = fctx.rev()
388 396 for obj in reversed(list(fctx.filelog())):
389 397 obj = fctx.filectx(obj)
390 398 if limit_rev >= obj.rev():
391 399 yield obj
392 400
393 401 history = []
394 402 for cnt, obj in enumerate(history_iter()):
395 403 if limit and cnt >= limit:
396 404 break
397 405 history.append(hex(obj.node()))
398 406
399 407 return [x for x in history]
400 408
401 409 @reraise_safe_exceptions
402 410 def file_history_untill(self, wire, revision, path, limit):
403 411 repo = self._factory.repo(wire)
404 412 ctx = repo[revision]
405 413 fctx = ctx.filectx(path)
406 414
407 415 file_log = list(fctx.filelog())
408 416 if limit:
409 417 # Limit to the last n items
410 418 file_log = file_log[-limit:]
411 419
412 420 return [hex(fctx.filectx(cs).node()) for cs in reversed(file_log)]
413 421
414 422 @reraise_safe_exceptions
415 423 def fctx_annotate(self, wire, revision, path):
416 424 repo = self._factory.repo(wire)
417 425 ctx = repo[revision]
418 426 fctx = ctx.filectx(path)
419 427
420 428 result = []
421 429 for i, annotate_data in enumerate(fctx.annotate()):
422 430 ln_no = i + 1
423 431 sha = hex(annotate_data[0].node())
424 432 result.append((ln_no, sha, annotate_data[1]))
425 433 return result
426 434
427 435 @reraise_safe_exceptions
428 436 def fctx_data(self, wire, revision, path):
429 437 repo = self._factory.repo(wire)
430 438 ctx = repo[revision]
431 439 fctx = ctx.filectx(path)
432 440 return fctx.data()
433 441
434 442 @reraise_safe_exceptions
435 443 def fctx_flags(self, wire, revision, path):
436 444 repo = self._factory.repo(wire)
437 445 ctx = repo[revision]
438 446 fctx = ctx.filectx(path)
439 447 return fctx.flags()
440 448
441 449 @reraise_safe_exceptions
442 450 def fctx_size(self, wire, revision, path):
443 451 repo = self._factory.repo(wire)
444 452 ctx = repo[revision]
445 453 fctx = ctx.filectx(path)
446 454 return fctx.size()
447 455
448 456 @reraise_safe_exceptions
449 457 def get_all_commit_ids(self, wire, name):
450 458 repo = self._factory.repo(wire)
451 459 revs = repo.filtered(name).changelog.index
452 460 return map(lambda x: hex(x[7]), revs)[:-1]
453 461
454 462 @reraise_safe_exceptions
455 463 def get_config_value(self, wire, section, name, untrusted=False):
456 464 repo = self._factory.repo(wire)
457 465 return repo.ui.config(section, name, untrusted=untrusted)
458 466
459 467 @reraise_safe_exceptions
460 468 def get_config_bool(self, wire, section, name, untrusted=False):
461 469 repo = self._factory.repo(wire)
462 470 return repo.ui.configbool(section, name, untrusted=untrusted)
463 471
464 472 @reraise_safe_exceptions
465 473 def get_config_list(self, wire, section, name, untrusted=False):
466 474 repo = self._factory.repo(wire)
467 475 return repo.ui.configlist(section, name, untrusted=untrusted)
468 476
469 477 @reraise_safe_exceptions
470 478 def is_large_file(self, wire, path):
471 479 return largefiles.lfutil.isstandin(path)
472 480
473 481 @reraise_safe_exceptions
474 482 def in_store(self, wire, sha):
475 483 repo = self._factory.repo(wire)
476 484 return largefiles.lfutil.instore(repo, sha)
477 485
478 486 @reraise_safe_exceptions
479 487 def in_user_cache(self, wire, sha):
480 488 repo = self._factory.repo(wire)
481 489 return largefiles.lfutil.inusercache(repo.ui, sha)
482 490
483 491 @reraise_safe_exceptions
484 492 def store_path(self, wire, sha):
485 493 repo = self._factory.repo(wire)
486 494 return largefiles.lfutil.storepath(repo, sha)
487 495
488 496 @reraise_safe_exceptions
489 497 def link(self, wire, sha, path):
490 498 repo = self._factory.repo(wire)
491 499 largefiles.lfutil.link(
492 500 largefiles.lfutil.usercachepath(repo.ui, sha), path)
493 501
494 502 @reraise_safe_exceptions
495 503 def localrepository(self, wire, create=False):
496 504 self._factory.repo(wire, create=create)
497 505
498 506 @reraise_safe_exceptions
499 507 def lookup(self, wire, revision, both):
500 508 # TODO Paris: Ugly hack to "deserialize" long for msgpack
501 509 if isinstance(revision, float):
502 510 revision = long(revision)
503 511 repo = self._factory.repo(wire)
504 512 try:
505 513 ctx = repo[revision]
506 514 except RepoLookupError:
507 515 raise exceptions.LookupException(revision)
508 516 except LookupError as e:
509 517 raise exceptions.LookupException(e.name)
510 518
511 519 if not both:
512 520 return ctx.hex()
513 521
514 522 ctx = repo[ctx.hex()]
515 523 return ctx.hex(), ctx.rev()
516 524
517 525 @reraise_safe_exceptions
518 526 def pull(self, wire, url, commit_ids=None):
519 527 repo = self._factory.repo(wire)
520 528 remote = peer(repo, {}, url)
521 529 if commit_ids:
522 530 commit_ids = [bin(commit_id) for commit_id in commit_ids]
523 531
524 532 return exchange.pull(
525 533 repo, remote, heads=commit_ids, force=None).cgresult
526 534
527 535 @reraise_safe_exceptions
528 536 def revision(self, wire, rev):
529 537 repo = self._factory.repo(wire)
530 538 ctx = repo[rev]
531 539 return ctx.rev()
532 540
533 541 @reraise_safe_exceptions
534 542 def rev_range(self, wire, filter):
535 543 repo = self._factory.repo(wire)
536 544 revisions = [rev for rev in revrange(repo, filter)]
537 545 return revisions
538 546
539 547 @reraise_safe_exceptions
540 548 def rev_range_hash(self, wire, node):
541 549 repo = self._factory.repo(wire)
542 550
543 551 def get_revs(repo, rev_opt):
544 552 if rev_opt:
545 553 revs = revrange(repo, rev_opt)
546 554 if len(revs) == 0:
547 555 return (nullrev, nullrev)
548 556 return max(revs), min(revs)
549 557 else:
550 558 return len(repo) - 1, 0
551 559
552 560 stop, start = get_revs(repo, [node + ':'])
553 561 revs = [hex(repo[r].node()) for r in xrange(start, stop + 1)]
554 562 return revs
555 563
556 564 @reraise_safe_exceptions
557 565 def revs_from_revspec(self, wire, rev_spec, *args, **kwargs):
558 566 other_path = kwargs.pop('other_path', None)
559 567
560 568 # case when we want to compare two independent repositories
561 569 if other_path and other_path != wire["path"]:
562 570 baseui = self._factory._create_config(wire["config"])
563 571 repo = unionrepo.unionrepository(baseui, other_path, wire["path"])
564 572 else:
565 573 repo = self._factory.repo(wire)
566 574 return list(repo.revs(rev_spec, *args))
567 575
568 576 @reraise_safe_exceptions
569 577 def strip(self, wire, revision, update, backup):
570 578 repo = self._factory.repo(wire)
571 579 ctx = repo[revision]
572 580 hgext_strip(
573 581 repo.baseui, repo, ctx.node(), update=update, backup=backup)
574 582
575 583 @reraise_safe_exceptions
576 584 def tag(self, wire, name, revision, message, local, user,
577 585 tag_time, tag_timezone):
578 586 repo = self._factory.repo(wire)
579 587 ctx = repo[revision]
580 588 node = ctx.node()
581 589
582 590 date = (tag_time, tag_timezone)
583 591 try:
584 592 repo.tag(name, node, message, local, user, date)
585 593 except Abort:
586 594 log.exception("Tag operation aborted")
587 595 raise exceptions.AbortException()
588 596
589 597 @reraise_safe_exceptions
590 598 def tags(self, wire):
591 599 repo = self._factory.repo(wire)
592 600 return repo.tags()
593 601
594 602 @reraise_safe_exceptions
595 603 def update(self, wire, node=None, clean=False):
596 604 repo = self._factory.repo(wire)
597 605 baseui = self._factory._create_config(wire['config'])
598 606 commands.update(baseui, repo, node=node, clean=clean)
599 607
600 608 @reraise_safe_exceptions
601 609 def identify(self, wire):
602 610 repo = self._factory.repo(wire)
603 611 baseui = self._factory._create_config(wire['config'])
604 612 output = io.BytesIO()
605 613 baseui.write = output.write
606 614 # This is required to get a full node id
607 615 baseui.debugflag = True
608 616 commands.identify(baseui, repo, id=True)
609 617
610 618 return output.getvalue()
611 619
612 620 @reraise_safe_exceptions
613 621 def pull_cmd(self, wire, source, bookmark=None, branch=None, revision=None,
614 622 hooks=True):
615 623 repo = self._factory.repo(wire)
616 624 baseui = self._factory._create_config(wire['config'], hooks=hooks)
617 625
618 626 # Mercurial internally has a lot of logic that checks ONLY if
619 627 # option is defined, we just pass those if they are defined then
620 628 opts = {}
621 629 if bookmark:
622 630 opts['bookmark'] = bookmark
623 631 if branch:
624 632 opts['branch'] = branch
625 633 if revision:
626 634 opts['rev'] = revision
627 635
628 636 commands.pull(baseui, repo, source, **opts)
629 637
630 638 @reraise_safe_exceptions
631 639 def heads(self, wire, branch=None):
632 640 repo = self._factory.repo(wire)
633 641 baseui = self._factory._create_config(wire['config'])
634 642 output = io.BytesIO()
635 643
636 644 def write(data, **unused_kwargs):
637 645 output.write(data)
638 646
639 647 baseui.write = write
640 648 if branch:
641 649 args = [branch]
642 650 else:
643 651 args = []
644 652 commands.heads(baseui, repo, template='{node} ', *args)
645 653
646 654 return output.getvalue()
647 655
648 656 @reraise_safe_exceptions
649 657 def ancestor(self, wire, revision1, revision2):
650 658 repo = self._factory.repo(wire)
651 659 baseui = self._factory._create_config(wire['config'])
652 660 output = io.BytesIO()
653 661 baseui.write = output.write
654 662 commands.debugancestor(baseui, repo, revision1, revision2)
655 663
656 664 return output.getvalue()
657 665
658 666 @reraise_safe_exceptions
659 667 def push(self, wire, revisions, dest_path, hooks=True,
660 668 push_branches=False):
661 669 repo = self._factory.repo(wire)
662 670 baseui = self._factory._create_config(wire['config'], hooks=hooks)
663 671 commands.push(baseui, repo, dest=dest_path, rev=revisions,
664 672 new_branch=push_branches)
665 673
666 674 @reraise_safe_exceptions
667 675 def merge(self, wire, revision):
668 676 repo = self._factory.repo(wire)
669 677 baseui = self._factory._create_config(wire['config'])
670 678 repo.ui.setconfig('ui', 'merge', 'internal:dump')
671 679 commands.merge(baseui, repo, rev=revision)
672 680
673 681 @reraise_safe_exceptions
674 682 def commit(self, wire, message, username):
675 683 repo = self._factory.repo(wire)
676 684 baseui = self._factory._create_config(wire['config'])
677 685 repo.ui.setconfig('ui', 'username', username)
678 686 commands.commit(baseui, repo, message=message)
679 687
680 688 @reraise_safe_exceptions
681 689 def rebase(self, wire, source=None, dest=None, abort=False):
682 690 repo = self._factory.repo(wire)
683 691 baseui = self._factory._create_config(wire['config'])
684 692 repo.ui.setconfig('ui', 'merge', 'internal:dump')
685 693 rebase.rebase(
686 694 baseui, repo, base=source, dest=dest, abort=abort, keep=not abort)
687 695
688 696 @reraise_safe_exceptions
689 697 def bookmark(self, wire, bookmark, revision=None):
690 698 repo = self._factory.repo(wire)
691 699 baseui = self._factory._create_config(wire['config'])
692 700 commands.bookmark(baseui, repo, bookmark, rev=revision, force=True)
@@ -1,335 +1,337 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2016 RodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import base64
19 19 import locale
20 20 import logging
21 21 import uuid
22 22 import wsgiref.util
23 23 from itertools import chain
24 24
25 25 import msgpack
26 26 from beaker.cache import CacheManager
27 27 from beaker.util import parse_cache_config_options
28 28 from pyramid.config import Configurator
29 29 from pyramid.wsgi import wsgiapp
30 30
31 from vcsserver import remote_wsgi, scm_app, settings
31 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
32 32 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
33 33 from vcsserver.echo_stub.echo_app import EchoApp
34 34 from vcsserver.server import VcsServer
35 35
36 36 try:
37 37 from vcsserver.git import GitFactory, GitRemote
38 38 except ImportError:
39 39 GitFactory = None
40 40 GitRemote = None
41 41 try:
42 42 from vcsserver.hg import MercurialFactory, HgRemote
43 43 except ImportError:
44 44 MercurialFactory = None
45 45 HgRemote = None
46 46 try:
47 47 from vcsserver.svn import SubversionFactory, SvnRemote
48 48 except ImportError:
49 49 SubversionFactory = None
50 50 SvnRemote = None
51 51
52 52 log = logging.getLogger(__name__)
53 53
54 54
55 55 class VCS(object):
56 56 def __init__(self, locale=None, cache_config=None):
57 57 self.locale = locale
58 58 self.cache_config = cache_config
59 59 self._configure_locale()
60 60 self._initialize_cache()
61 61
62 62 if GitFactory and GitRemote:
63 63 git_repo_cache = self.cache.get_cache_region(
64 64 'git', region='repo_object')
65 65 git_factory = GitFactory(git_repo_cache)
66 66 self._git_remote = GitRemote(git_factory)
67 67 else:
68 68 log.info("Git client import failed")
69 69
70 70 if MercurialFactory and HgRemote:
71 71 hg_repo_cache = self.cache.get_cache_region(
72 72 'hg', region='repo_object')
73 73 hg_factory = MercurialFactory(hg_repo_cache)
74 74 self._hg_remote = HgRemote(hg_factory)
75 75 else:
76 76 log.info("Mercurial client import failed")
77 77
78 78 if SubversionFactory and SvnRemote:
79 79 svn_repo_cache = self.cache.get_cache_region(
80 80 'svn', region='repo_object')
81 81 svn_factory = SubversionFactory(svn_repo_cache)
82 82 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
83 83 else:
84 84 log.info("Subversion client import failed")
85 85
86 86 self._vcsserver = VcsServer()
87 87
88 88 def _initialize_cache(self):
89 89 cache_config = parse_cache_config_options(self.cache_config)
90 90 log.info('Initializing beaker cache: %s' % cache_config)
91 91 self.cache = CacheManager(**cache_config)
92 92
93 93 def _configure_locale(self):
94 94 if self.locale:
95 95 log.info('Settings locale: `LC_ALL` to %s' % self.locale)
96 96 else:
97 97 log.info(
98 98 'Configuring locale subsystem based on environment variables')
99 99 try:
100 100 # If self.locale is the empty string, then the locale
101 101 # module will use the environment variables. See the
102 102 # documentation of the package `locale`.
103 103 locale.setlocale(locale.LC_ALL, self.locale)
104 104
105 105 language_code, encoding = locale.getlocale()
106 106 log.info(
107 107 'Locale set to language code "%s" with encoding "%s".',
108 108 language_code, encoding)
109 109 except locale.Error:
110 110 log.exception(
111 111 'Cannot set locale, not configuring the locale system')
112 112
113 113
114 114 class WsgiProxy(object):
115 115 def __init__(self, wsgi):
116 116 self.wsgi = wsgi
117 117
118 118 def __call__(self, environ, start_response):
119 119 input_data = environ['wsgi.input'].read()
120 120 input_data = msgpack.unpackb(input_data)
121 121
122 122 error = None
123 123 try:
124 124 data, status, headers = self.wsgi.handle(
125 125 input_data['environment'], input_data['input_data'],
126 126 *input_data['args'], **input_data['kwargs'])
127 127 except Exception as e:
128 128 data, status, headers = [], None, None
129 129 error = {
130 130 'message': str(e),
131 131 '_vcs_kind': getattr(e, '_vcs_kind', None)
132 132 }
133 133
134 134 start_response(200, {})
135 135 return self._iterator(error, status, headers, data)
136 136
137 137 def _iterator(self, error, status, headers, data):
138 138 initial_data = [
139 139 error,
140 140 status,
141 141 headers,
142 142 ]
143 143
144 144 for d in chain(initial_data, data):
145 145 yield msgpack.packb(d)
146 146
147 147
148 148 class HTTPApplication(object):
149 149 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
150 150
151 151 remote_wsgi = remote_wsgi
152 152 _use_echo_app = False
153 153
154 154 def __init__(self, settings=None):
155 155 self.config = Configurator(settings=settings)
156 156 locale = settings.get('', 'en_US.UTF-8')
157 157 vcs = VCS(locale=locale, cache_config=settings)
158 158 self._remotes = {
159 159 'hg': vcs._hg_remote,
160 160 'git': vcs._git_remote,
161 161 'svn': vcs._svn_remote,
162 162 'server': vcs._vcsserver,
163 163 }
164 164 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
165 165 self._use_echo_app = True
166 166 log.warning("Using EchoApp for VCS operations.")
167 167 self.remote_wsgi = remote_wsgi_stub
168 168 self._configure_settings(settings)
169 169 self._configure()
170 170
171 171 def _configure_settings(self, app_settings):
172 172 """
173 173 Configure the settings module.
174 174 """
175 175 git_path = app_settings.get('git_path', None)
176 176 if git_path:
177 177 settings.GIT_EXECUTABLE = git_path
178 178
179 179 def _configure(self):
180 180 self.config.add_renderer(
181 181 name='msgpack',
182 182 factory=self._msgpack_renderer_factory)
183 183
184 184 self.config.add_route('status', '/status')
185 185 self.config.add_route('hg_proxy', '/proxy/hg')
186 186 self.config.add_route('git_proxy', '/proxy/git')
187 187 self.config.add_route('vcs', '/{backend}')
188 188 self.config.add_route('stream_git', '/stream/git/*repo_name')
189 189 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
190 190
191 191 self.config.add_view(
192 192 self.status_view, route_name='status', renderer='json')
193 193 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
194 194 self.config.add_view(self.git_proxy(), route_name='git_proxy')
195 195 self.config.add_view(
196 196 self.vcs_view, route_name='vcs', renderer='msgpack')
197 197
198 198 self.config.add_view(self.hg_stream(), route_name='stream_hg')
199 199 self.config.add_view(self.git_stream(), route_name='stream_git')
200 200
201 201 def wsgi_app(self):
202 202 return self.config.make_wsgi_app()
203 203
204 204 def vcs_view(self, request):
205 205 remote = self._remotes[request.matchdict['backend']]
206 206 payload = msgpack.unpackb(request.body, use_list=True)
207 207 method = payload.get('method')
208 208 params = payload.get('params')
209 209 wire = params.get('wire')
210 210 args = params.get('args')
211 211 kwargs = params.get('kwargs')
212 212 if wire:
213 213 try:
214 214 wire['context'] = uuid.UUID(wire['context'])
215 215 except KeyError:
216 216 pass
217 217 args.insert(0, wire)
218 218
219 219 try:
220 220 resp = getattr(remote, method)(*args, **kwargs)
221 221 except Exception as e:
222 222 type_ = e.__class__.__name__
223 223 if type_ not in self.ALLOWED_EXCEPTIONS:
224 224 type_ = None
225 225
226 226 resp = {
227 227 'id': payload.get('id'),
228 228 'error': {
229 229 'message': e.message,
230 230 'type': type_
231 231 }
232 232 }
233 233 try:
234 234 resp['error']['_vcs_kind'] = e._vcs_kind
235 235 except AttributeError:
236 236 pass
237 237 else:
238 238 resp = {
239 239 'id': payload.get('id'),
240 240 'result': resp
241 241 }
242 242
243 243 return resp
244 244
245 245 def status_view(self, request):
246 246 return {'status': 'OK'}
247 247
248 248 def _msgpack_renderer_factory(self, info):
249 249 def _render(value, system):
250 250 value = msgpack.packb(value)
251 251 request = system.get('request')
252 252 if request is not None:
253 253 response = request.response
254 254 ct = response.content_type
255 255 if ct == response.default_content_type:
256 256 response.content_type = 'application/x-msgpack'
257 257 return value
258 258 return _render
259 259
260 260 def hg_proxy(self):
261 261 @wsgiapp
262 262 def _hg_proxy(environ, start_response):
263 263 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
264 264 return app(environ, start_response)
265 265 return _hg_proxy
266 266
267 267 def git_proxy(self):
268 268 @wsgiapp
269 269 def _git_proxy(environ, start_response):
270 270 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
271 271 return app(environ, start_response)
272 272 return _git_proxy
273 273
274 274 def hg_stream(self):
275 275 if self._use_echo_app:
276 276 @wsgiapp
277 277 def _hg_stream(environ, start_response):
278 278 app = EchoApp('fake_path', 'fake_name', None)
279 279 return app(environ, start_response)
280 280 return _hg_stream
281 281 else:
282 282 @wsgiapp
283 283 def _hg_stream(environ, start_response):
284 284 repo_path = environ['HTTP_X_RC_REPO_PATH']
285 285 repo_name = environ['HTTP_X_RC_REPO_NAME']
286 286 packed_config = base64.b64decode(
287 287 environ['HTTP_X_RC_REPO_CONFIG'])
288 288 config = msgpack.unpackb(packed_config)
289 289 app = scm_app.create_hg_wsgi_app(
290 290 repo_path, repo_name, config)
291 291
292 292 # Consitent path information for hgweb
293 293 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
294 294 environ['REPO_NAME'] = repo_name
295 295 return app(environ, ResponseFilter(start_response))
296 296 return _hg_stream
297 297
298 298 def git_stream(self):
299 299 if self._use_echo_app:
300 300 @wsgiapp
301 301 def _git_stream(environ, start_response):
302 302 app = EchoApp('fake_path', 'fake_name', None)
303 303 return app(environ, start_response)
304 304 return _git_stream
305 305 else:
306 306 @wsgiapp
307 307 def _git_stream(environ, start_response):
308 308 repo_path = environ['HTTP_X_RC_REPO_PATH']
309 309 repo_name = environ['HTTP_X_RC_REPO_NAME']
310 310 packed_config = base64.b64decode(
311 311 environ['HTTP_X_RC_REPO_CONFIG'])
312 312 config = msgpack.unpackb(packed_config)
313 313
314 314 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
315 315 app = scm_app.create_git_wsgi_app(
316 316 repo_path, repo_name, config)
317 317 return app(environ, start_response)
318 318 return _git_stream
319 319
320 320
321 321 class ResponseFilter(object):
322 322
323 323 def __init__(self, start_response):
324 324 self._start_response = start_response
325 325
326 326 def __call__(self, status, response_headers, exc_info=None):
327 327 headers = tuple(
328 328 (h, v) for h, v in response_headers
329 329 if not wsgiref.util.is_hop_by_hop(h))
330 330 return self._start_response(status, headers, exc_info)
331 331
332 332
333 333 def main(global_config, **settings):
334 if MercurialFactory:
335 hgpatches.patch_largefiles_capabilities()
334 336 app = HTTPApplication(settings=settings)
335 337 return app.wsgi_app()
General Comments 0
You need to be logged in to leave comments. Login now