##// END OF EJS Templates
license: update license metadata and generation code
marcink -
r3073:73a2f607 default
parent child Browse files
Show More
@@ -1,52 +1,60 b''
1
1
2 ==============================
2 ==============================
3 Generate the Nix expressions
3 Generate the Nix expressions
4 ==============================
4 ==============================
5
5
6 Details can be found in the repository of `RhodeCode Enterprise CE`_ inside of
6 Details can be found in the repository of `RhodeCode Enterprise CE`_ inside of
7 the file `docs/contributing/dependencies.rst`.
7 the file `docs/contributing/dependencies.rst`.
8
8
9 Start the environment as follows:
9 Start the environment as follows:
10
10
11 .. code:: shell
11 .. code:: shell
12
12
13 nix-shell pkgs/shell-generate.nix
13 nix-shell pkgs/shell-generate.nix
14
14
15
15
16
16
17 Python dependencies
17 Python dependencies
18 ===================
18 ===================
19
19
20 .. code:: shell
20 .. code:: shell
21
21
22 pip2nix generate --licenses
22 pip2nix generate --licenses
23 # or
23 # or
24 nix-shell pkgs/shell-generate.nix --command "pip2nix generate --licenses"
24 nix-shell pkgs/shell-generate.nix --command "pip2nix generate --licenses"
25
25
26
26
27 NodeJS dependencies
27 NodeJS dependencies
28 ===================
28 ===================
29
29
30 .. code:: shell
30 .. code:: shell
31
31
32 rm -rf node_modules
32 rm -rf node_modules
33 nix-shell pkgs/shell-generate.nix --command "
33 nix-shell pkgs/shell-generate.nix --command "
34 node2nix --input package.json \
34 node2nix --input package.json \
35 -o pkgs/node-packages.nix \
35 -o pkgs/node-packages.nix \
36 -e pkgs/node-env.nix \
36 -e pkgs/node-env.nix \
37 -c pkgs/node-default.nix \
37 -c pkgs/node-default.nix \
38 -d --flatten --nodejs-6 "
38 -d --flatten --nodejs-6 "
39
39
40
40
41
41
42 Bower dependencies
42 Bower dependencies
43 ==================
43 ==================
44
44
45 .. code:: shell
45 .. code:: shell
46
46
47 nix-shell pkgs/shell-generate.nix --command "bower2nix bower.json pkgs/bower-packages.nix"
47 nix-shell pkgs/shell-generate.nix --command "bower2nix bower.json pkgs/bower-packages.nix"
48
48
49
49
50 Generate license data
51 =====================
52
53 .. code:: shell
54
55 nix-build pkgs/license-generate.nix -o result-license && cat result-license/licenses.json | python -m json.tool > rhodecode/config/licenses.json
56
57
50 .. Links
58 .. Links
51
59
52 .. _RhodeCode Enterprise CE: https://code.rhodecode.com/rhodecode-enterprise-ce
60 .. _RhodeCode Enterprise CE: https://code.rhodecode.com/rhodecode-enterprise-ce
@@ -1,160 +1,159 b''
1 # Utility to generate the license information
1 # Utility to generate the license information
2 #
2 #
3 # Usage:
3 # Usage:
4 #
4 #
5 # nix-build -I ~/dev license.nix -A result
5 # nix-build license.nix -o result-license
6 #
6 #
7 # Afterwards ./result will contain the license information as JSON files.
7 # Afterwards ./result-license will contain the license information as JSON files.
8 #
8 #
9 #
9 #
10 # Overview
10 # Overview
11 #
11 #
12 # Uses two steps to get the relevant license information:
12 # Uses two steps to get the relevant license information:
13 #
13 #
14 # 1. Walk down the derivations based on "buildInputs" and
14 # 1. Walk down the derivations based on "buildInputs" and
15 # "propagatedBuildInputs". This results in all dependencies based on the nix
15 # "propagatedBuildInputs". This results in all dependencies based on the nix
16 # declartions.
16 # declartions.
17 #
17 #
18 # 2. Build Enterprise and query nix-store to get a list of runtime
18 # 2. Build Enterprise and query nix-store to get a list of runtime
19 # dependencies. The results from step 1 are then limited to the ones which
19 # dependencies. The results from step 1 are then limited to the ones which
20 # are in this list.
20 # are in this list.
21 #
21 #
22 # The result is then available in ./result/license.json.
22 # The result is then available in ./result-license/license.json.
23 #
23 #
24
24
25
25
26 let
26 let
27
27
28 nixpkgs = import <nixpkgs> {};
28 nixpkgs = import <nixpkgs> {};
29
29
30 stdenv = nixpkgs.stdenv;
30 stdenv = nixpkgs.stdenv;
31
31
32 # Enterprise as simple as possible, goal here is just to identify the runtime
32 # Enterprise as simple as possible, goal here is just to identify the runtime
33 # dependencies. Ideally we could avoid building Enterprise at all and somehow
33 # dependencies. Ideally we could avoid building Enterprise at all and somehow
34 # figure it out without calling into nix-store.
34 # figure it out without calling into nix-store.
35 enterprise = import ./default.nix {
35 enterprise = import ../default.nix {
36 doCheck = false;
36 doCheck = false;
37 };
37 };
38
38
39 # For a given derivation, return the list of all dependencies
39 # For a given derivation, return the list of all dependencies
40 drvToDependencies = drv: nixpkgs.lib.flatten [
40 drvToDependencies = drv: nixpkgs.lib.flatten [
41 drv.nativeBuildInputs or []
41 drv.buildInputs or []
42 drv.propagatedNativeBuildInputs or []
42 drv.propagatedBuildInputs or []
43 ];
43 ];
44
44
45 # Transform the given derivation into the meta information which we need in
45 # Transform the given derivation into the meta information which we need in
46 # the resulting JSON files.
46 # the resulting JSON files.
47 drvToMeta = drv: {
47 drvToMeta = drv: {
48 name = drv.name or "UNNAMED";
48 name = drv.name or drv;
49 license = if drv ? meta.license then drv.meta.license else "UNKNOWN";
49 license = if drv ? meta.license then drv.meta.license else "UNKNOWN";
50 };
50 };
51
51
52 # Walk the tree of buildInputs and propagatedBuildInputs and return it as a
52 # Walk the tree of buildInputs and propagatedBuildInputs and return it as a
53 # flat list. Duplicates are avoided.
53 # flat list. Duplicates are avoided.
54 listDrvDependencies = drv: let
54 listDrvDependencies = drv: let
55 addElement = element: seen:
55 addElement = element: seen:
56 if (builtins.elem element seen)
56 if (builtins.elem element seen)
57 then seen
57 then seen
58 else let
58 else let
59 newSeen = seen ++ [ element ];
59 newSeen = seen ++ [ element ];
60 newDeps = drvToDependencies element;
60 newDeps = drvToDependencies element;
61 in nixpkgs.lib.fold addElement newSeen newDeps;
61 in nixpkgs.lib.fold addElement newSeen newDeps;
62 initialElements = drvToDependencies drv;
62 initialElements = drvToDependencies drv;
63 in nixpkgs.lib.fold addElement [] initialElements;
63 in nixpkgs.lib.fold addElement [] initialElements;
64
64
65 # Reads in a file with store paths and returns a list of derivation names.
65 # Reads in a file with store paths and returns a list of derivation names.
66 #
66 #
67 # Reads the file, splits the lines, then removes the prefix, so that we
67 # Reads the file, splits the lines, then removes the prefix, so that we
68 # end up with a list of derivation names in the end.
68 # end up with a list of derivation names in the end.
69 storePathsToDrvNames = srcPath: let
69 storePathsToDrvNames = srcPath: let
70 rawStorePaths = nixpkgs.lib.removeSuffix "\n" (
70 rawStorePaths = nixpkgs.lib.removeSuffix "\n" (
71 builtins.readFile srcPath);
71 builtins.readFile srcPath);
72 storePaths = nixpkgs.lib.splitString "\n" rawStorePaths;
72 storePaths = nixpkgs.lib.splitString "\n" rawStorePaths;
73 # TODO: johbo: Would be nice to use some sort of utility here to convert
74 # the path to a derivation name.
75 storePathPrefix = (
73 storePathPrefix = (
76 builtins.stringLength "/nix/store/zwy7aavnif9ayw30rya1k6xiacafzzl6-");
74 builtins.stringLength "/nix/store/afafafafafafafafafafafafafafafaf-");
77 storePathToName = path:
75 storePathToName = path:
78 builtins.substring storePathPrefix (builtins.stringLength path) path;
76 builtins.substring storePathPrefix (builtins.stringLength path) path;
79 in (map storePathToName storePaths);
77 in (map storePathToName storePaths);
80
78
81 in rec {
79 in rec {
82
80
83 # Build Enterprise and call nix-store to retrieve the runtime
81 # Build Enterprise and call nix-store to retrieve the runtime
84 # dependencies. The result is available in the nix store.
82 # dependencies. The result is available in the nix store.
85 runtimeDependencies = stdenv.mkDerivation {
83 runtimeDependencies = stdenv.mkDerivation {
86 name = "runtime-dependencies";
84 name = "runtime-dependencies";
87 buildInputs = [
85 buildInputs = [
88 # Needed to query the store
86 # Needed to query the store
89 nixpkgs.nix
87 nixpkgs.nix
90 ];
88 ];
91 unpackPhase = ''
89 unpackPhase = ''
92 echo "Nothing to unpack"
90 echo "Nothing to unpack"
93 '';
91 '';
94 buildPhase = ''
92 buildPhase = ''
95 # Get a list of runtime dependencies
93 # Get a list of runtime dependencies
96 nix-store -q --references ${enterprise} > nix-store-references
94 nix-store -q --references ${enterprise} > nix-store-references
97 '';
95 '';
98 installPhase = ''
96 installPhase = ''
99 mkdir -p $out
97 mkdir -p $out
100 cp -v nix-store-references $out/
98 cp -v nix-store-references $out/
101 '';
99 '';
102 };
100 };
103
101
104 # Produce the license overview files.
102 # Produce the license overview files.
105 result = let
103 result = let
106
104
107 # Dependencies according to the nix-store
105 # Dependencies according to the nix-store
108 runtimeDependencyNames = (
106 runtimeDependencyNames = (
109 storePathsToDrvNames "${runtimeDependencies}/nix-store-references");
107 storePathsToDrvNames "${runtimeDependencies}/nix-store-references");
110
108
111 # Dependencies based on buildInputs and propagatedBuildInputs
109 # Dependencies based on buildInputs and propagatedBuildInputs
112 enterpriseAllDependencies = listDrvDependencies enterprise;
110 enterpriseAllDependencies = listDrvDependencies enterprise;
113 enterpriseRuntimeDependencies = let
111 enterpriseRuntimeDependencies = let
114 elemName = element: element.name or "UNNAMED";
112 elemName = element: element.name or "UNNAMED";
115 isRuntime = element: builtins.elem (elemName element) runtimeDependencyNames;
113 isRuntime = element: builtins.elem (elemName element) runtimeDependencyNames;
116 in builtins.filter isRuntime enterpriseAllDependencies;
114 in builtins.filter isRuntime enterpriseAllDependencies;
117
115
118 # Extract relevant meta information
116 # Extract relevant meta information
119 enterpriseAllLicenses = map drvToMeta enterpriseAllDependencies;
117 enterpriseAllLicenses = map drvToMeta enterpriseAllDependencies;
120 enterpriseRuntimeLicenses = map drvToMeta enterpriseRuntimeDependencies;
118 enterpriseRuntimeLicenses = map drvToMeta enterpriseRuntimeDependencies;
121
119
122 in stdenv.mkDerivation {
120 in stdenv.mkDerivation {
123
121
124 name = "licenses";
122 name = "licenses";
125
123
126 buildInputs = [];
124 buildInputs = [];
127
125
128 unpackPhase = ''
126 unpackPhase = ''
129 echo "Nothing to unpack"
127 echo "Nothing to unpack"
130 '';
128 '';
131
129
132 buildPhase = ''
130 buildPhase = ''
133 mkdir build
131 mkdir build
134
132
135 # Copy list of runtime dependencies for the Python processor
133 # Copy list of runtime dependencies for the Python processor
136 cp "${runtimeDependencies}/nix-store-references" ./build/nix-store-references
134 cp "${runtimeDependencies}/nix-store-references" ./build/nix-store-references
137
135
138 # All licenses which we found by walking buildInputs and
136 # All licenses which we found by walking buildInputs and
139 # propagatedBuildInputs
137 # propagatedBuildInputs
140 cat > build/all-licenses.json <<EOF
138 cat > build/all-licenses.json <<EOF
141 ${builtins.toJSON enterpriseAllLicenses}
139 ${builtins.toJSON enterpriseAllLicenses}
142 EOF
140 EOF
143
141
144 # License information for our runtime dependencies only. Basically all
142 # License information for our runtime dependencies only. Basically all
145 # licenses limited to the items which where also reported by nix-store as
143 # licenses limited to the items which where also reported by nix-store as
146 # a dependency.
144 # a dependency.
147 cat > build/licenses.json <<EOF
145 cat > build/licenses.json <<EOF
148 ${builtins.toJSON enterpriseRuntimeLicenses}
146 ${builtins.toJSON enterpriseRuntimeLicenses}
149 EOF
147 EOF
148
150 '';
149 '';
151
150
152 installPhase = ''
151 installPhase = ''
153 mkdir -p $out
152 mkdir -p $out
154
153
155 # Store it all, that helps when things go wrong
154 # Store it all, that helps when things go wrong
156 cp -rv ./build/* $out
155 cp -rv ./build/* $out
157 '';
156 '';
158 };
157 };
159
158
160 }
159 }
@@ -1,729 +1,744 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.lib.utils2 import md5
26 from rhodecode.lib.utils2 import md5
27 from rhodecode.model.db import RhodeCodeUi
27 from rhodecode.model.db import RhodeCodeUi
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
30 from rhodecode.tests import assert_session_flash
30 from rhodecode.tests import assert_session_flash
31 from rhodecode.tests.utils import AssertResponse
31 from rhodecode.tests.utils import AssertResponse
32
32
33
33
34 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
34 UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data'
35
35
36
36
37 def route_path(name, params=None, **kwargs):
37 def route_path(name, params=None, **kwargs):
38 import urllib
38 import urllib
39 from rhodecode.apps._base import ADMIN_PREFIX
39 from rhodecode.apps._base import ADMIN_PREFIX
40
40
41 base_url = {
41 base_url = {
42
42
43 'admin_settings':
43 'admin_settings':
44 ADMIN_PREFIX +'/settings',
44 ADMIN_PREFIX +'/settings',
45 'admin_settings_update':
45 'admin_settings_update':
46 ADMIN_PREFIX + '/settings/update',
46 ADMIN_PREFIX + '/settings/update',
47 'admin_settings_global':
47 'admin_settings_global':
48 ADMIN_PREFIX + '/settings/global',
48 ADMIN_PREFIX + '/settings/global',
49 'admin_settings_global_update':
49 'admin_settings_global_update':
50 ADMIN_PREFIX + '/settings/global/update',
50 ADMIN_PREFIX + '/settings/global/update',
51 'admin_settings_vcs':
51 'admin_settings_vcs':
52 ADMIN_PREFIX + '/settings/vcs',
52 ADMIN_PREFIX + '/settings/vcs',
53 'admin_settings_vcs_update':
53 'admin_settings_vcs_update':
54 ADMIN_PREFIX + '/settings/vcs/update',
54 ADMIN_PREFIX + '/settings/vcs/update',
55 'admin_settings_vcs_svn_pattern_delete':
55 'admin_settings_vcs_svn_pattern_delete':
56 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
56 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
57 'admin_settings_mapping':
57 'admin_settings_mapping':
58 ADMIN_PREFIX + '/settings/mapping',
58 ADMIN_PREFIX + '/settings/mapping',
59 'admin_settings_mapping_update':
59 'admin_settings_mapping_update':
60 ADMIN_PREFIX + '/settings/mapping/update',
60 ADMIN_PREFIX + '/settings/mapping/update',
61 'admin_settings_visual':
61 'admin_settings_visual':
62 ADMIN_PREFIX + '/settings/visual',
62 ADMIN_PREFIX + '/settings/visual',
63 'admin_settings_visual_update':
63 'admin_settings_visual_update':
64 ADMIN_PREFIX + '/settings/visual/update',
64 ADMIN_PREFIX + '/settings/visual/update',
65 'admin_settings_issuetracker':
65 'admin_settings_issuetracker':
66 ADMIN_PREFIX + '/settings/issue-tracker',
66 ADMIN_PREFIX + '/settings/issue-tracker',
67 'admin_settings_issuetracker_update':
67 'admin_settings_issuetracker_update':
68 ADMIN_PREFIX + '/settings/issue-tracker/update',
68 ADMIN_PREFIX + '/settings/issue-tracker/update',
69 'admin_settings_issuetracker_test':
69 'admin_settings_issuetracker_test':
70 ADMIN_PREFIX + '/settings/issue-tracker/test',
70 ADMIN_PREFIX + '/settings/issue-tracker/test',
71 'admin_settings_issuetracker_delete':
71 'admin_settings_issuetracker_delete':
72 ADMIN_PREFIX + '/settings/issue-tracker/delete',
72 ADMIN_PREFIX + '/settings/issue-tracker/delete',
73 'admin_settings_email':
73 'admin_settings_email':
74 ADMIN_PREFIX + '/settings/email',
74 ADMIN_PREFIX + '/settings/email',
75 'admin_settings_email_update':
75 'admin_settings_email_update':
76 ADMIN_PREFIX + '/settings/email/update',
76 ADMIN_PREFIX + '/settings/email/update',
77 'admin_settings_hooks':
77 'admin_settings_hooks':
78 ADMIN_PREFIX + '/settings/hooks',
78 ADMIN_PREFIX + '/settings/hooks',
79 'admin_settings_hooks_update':
79 'admin_settings_hooks_update':
80 ADMIN_PREFIX + '/settings/hooks/update',
80 ADMIN_PREFIX + '/settings/hooks/update',
81 'admin_settings_hooks_delete':
81 'admin_settings_hooks_delete':
82 ADMIN_PREFIX + '/settings/hooks/delete',
82 ADMIN_PREFIX + '/settings/hooks/delete',
83 'admin_settings_search':
83 'admin_settings_search':
84 ADMIN_PREFIX + '/settings/search',
84 ADMIN_PREFIX + '/settings/search',
85 'admin_settings_labs':
85 'admin_settings_labs':
86 ADMIN_PREFIX + '/settings/labs',
86 ADMIN_PREFIX + '/settings/labs',
87 'admin_settings_labs_update':
87 'admin_settings_labs_update':
88 ADMIN_PREFIX + '/settings/labs/update',
88 ADMIN_PREFIX + '/settings/labs/update',
89
89
90 'admin_settings_sessions':
90 'admin_settings_sessions':
91 ADMIN_PREFIX + '/settings/sessions',
91 ADMIN_PREFIX + '/settings/sessions',
92 'admin_settings_sessions_cleanup':
92 'admin_settings_sessions_cleanup':
93 ADMIN_PREFIX + '/settings/sessions/cleanup',
93 ADMIN_PREFIX + '/settings/sessions/cleanup',
94 'admin_settings_system':
94 'admin_settings_system':
95 ADMIN_PREFIX + '/settings/system',
95 ADMIN_PREFIX + '/settings/system',
96 'admin_settings_system_update':
96 'admin_settings_system_update':
97 ADMIN_PREFIX + '/settings/system/updates',
97 ADMIN_PREFIX + '/settings/system/updates',
98 'admin_settings_open_source':
98 'admin_settings_open_source':
99 ADMIN_PREFIX + '/settings/open_source',
99 ADMIN_PREFIX + '/settings/open_source',
100
100
101
101
102 }[name].format(**kwargs)
102 }[name].format(**kwargs)
103
103
104 if params:
104 if params:
105 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
105 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
106 return base_url
106 return base_url
107
107
108
108
109 @pytest.mark.usefixtures('autologin_user', 'app')
109 @pytest.mark.usefixtures('autologin_user', 'app')
110 class TestAdminSettingsController(object):
110 class TestAdminSettingsController(object):
111
111
112 @pytest.mark.parametrize('urlname', [
112 @pytest.mark.parametrize('urlname', [
113 'admin_settings_vcs',
113 'admin_settings_vcs',
114 'admin_settings_mapping',
114 'admin_settings_mapping',
115 'admin_settings_global',
115 'admin_settings_global',
116 'admin_settings_visual',
116 'admin_settings_visual',
117 'admin_settings_email',
117 'admin_settings_email',
118 'admin_settings_hooks',
118 'admin_settings_hooks',
119 'admin_settings_search',
119 'admin_settings_search',
120 ])
120 ])
121 def test_simple_get(self, urlname):
121 def test_simple_get(self, urlname):
122 self.app.get(route_path(urlname))
122 self.app.get(route_path(urlname))
123
123
124 def test_create_custom_hook(self, csrf_token):
124 def test_create_custom_hook(self, csrf_token):
125 response = self.app.post(
125 response = self.app.post(
126 route_path('admin_settings_hooks_update'),
126 route_path('admin_settings_hooks_update'),
127 params={
127 params={
128 'new_hook_ui_key': 'test_hooks_1',
128 'new_hook_ui_key': 'test_hooks_1',
129 'new_hook_ui_value': 'cd /tmp',
129 'new_hook_ui_value': 'cd /tmp',
130 'csrf_token': csrf_token})
130 'csrf_token': csrf_token})
131
131
132 response = response.follow()
132 response = response.follow()
133 response.mustcontain('test_hooks_1')
133 response.mustcontain('test_hooks_1')
134 response.mustcontain('cd /tmp')
134 response.mustcontain('cd /tmp')
135
135
136 def test_create_custom_hook_delete(self, csrf_token):
136 def test_create_custom_hook_delete(self, csrf_token):
137 response = self.app.post(
137 response = self.app.post(
138 route_path('admin_settings_hooks_update'),
138 route_path('admin_settings_hooks_update'),
139 params={
139 params={
140 'new_hook_ui_key': 'test_hooks_2',
140 'new_hook_ui_key': 'test_hooks_2',
141 'new_hook_ui_value': 'cd /tmp2',
141 'new_hook_ui_value': 'cd /tmp2',
142 'csrf_token': csrf_token})
142 'csrf_token': csrf_token})
143
143
144 response = response.follow()
144 response = response.follow()
145 response.mustcontain('test_hooks_2')
145 response.mustcontain('test_hooks_2')
146 response.mustcontain('cd /tmp2')
146 response.mustcontain('cd /tmp2')
147
147
148 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
148 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
149
149
150 # delete
150 # delete
151 self.app.post(
151 self.app.post(
152 route_path('admin_settings_hooks_delete'),
152 route_path('admin_settings_hooks_delete'),
153 params={'hook_id': hook_id, 'csrf_token': csrf_token})
153 params={'hook_id': hook_id, 'csrf_token': csrf_token})
154 response = self.app.get(route_path('admin_settings_hooks'))
154 response = self.app.get(route_path('admin_settings_hooks'))
155 response.mustcontain(no=['test_hooks_2'])
155 response.mustcontain(no=['test_hooks_2'])
156 response.mustcontain(no=['cd /tmp2'])
156 response.mustcontain(no=['cd /tmp2'])
157
157
158
158
159 @pytest.mark.usefixtures('autologin_user', 'app')
159 @pytest.mark.usefixtures('autologin_user', 'app')
160 class TestAdminSettingsGlobal(object):
160 class TestAdminSettingsGlobal(object):
161
161
162 def test_pre_post_code_code_active(self, csrf_token):
162 def test_pre_post_code_code_active(self, csrf_token):
163 pre_code = 'rc-pre-code-187652122'
163 pre_code = 'rc-pre-code-187652122'
164 post_code = 'rc-postcode-98165231'
164 post_code = 'rc-postcode-98165231'
165
165
166 response = self.post_and_verify_settings({
166 response = self.post_and_verify_settings({
167 'rhodecode_pre_code': pre_code,
167 'rhodecode_pre_code': pre_code,
168 'rhodecode_post_code': post_code,
168 'rhodecode_post_code': post_code,
169 'csrf_token': csrf_token,
169 'csrf_token': csrf_token,
170 })
170 })
171
171
172 response = response.follow()
172 response = response.follow()
173 response.mustcontain(pre_code, post_code)
173 response.mustcontain(pre_code, post_code)
174
174
175 def test_pre_post_code_code_inactive(self, csrf_token):
175 def test_pre_post_code_code_inactive(self, csrf_token):
176 pre_code = 'rc-pre-code-187652122'
176 pre_code = 'rc-pre-code-187652122'
177 post_code = 'rc-postcode-98165231'
177 post_code = 'rc-postcode-98165231'
178 response = self.post_and_verify_settings({
178 response = self.post_and_verify_settings({
179 'rhodecode_pre_code': '',
179 'rhodecode_pre_code': '',
180 'rhodecode_post_code': '',
180 'rhodecode_post_code': '',
181 'csrf_token': csrf_token,
181 'csrf_token': csrf_token,
182 })
182 })
183
183
184 response = response.follow()
184 response = response.follow()
185 response.mustcontain(no=[pre_code, post_code])
185 response.mustcontain(no=[pre_code, post_code])
186
186
187 def test_captcha_activate(self, csrf_token):
187 def test_captcha_activate(self, csrf_token):
188 self.post_and_verify_settings({
188 self.post_and_verify_settings({
189 'rhodecode_captcha_private_key': '1234567890',
189 'rhodecode_captcha_private_key': '1234567890',
190 'rhodecode_captcha_public_key': '1234567890',
190 'rhodecode_captcha_public_key': '1234567890',
191 'csrf_token': csrf_token,
191 'csrf_token': csrf_token,
192 })
192 })
193
193
194 response = self.app.get(ADMIN_PREFIX + '/register')
194 response = self.app.get(ADMIN_PREFIX + '/register')
195 response.mustcontain('captcha')
195 response.mustcontain('captcha')
196
196
197 def test_captcha_deactivate(self, csrf_token):
197 def test_captcha_deactivate(self, csrf_token):
198 self.post_and_verify_settings({
198 self.post_and_verify_settings({
199 'rhodecode_captcha_private_key': '',
199 'rhodecode_captcha_private_key': '',
200 'rhodecode_captcha_public_key': '1234567890',
200 'rhodecode_captcha_public_key': '1234567890',
201 'csrf_token': csrf_token,
201 'csrf_token': csrf_token,
202 })
202 })
203
203
204 response = self.app.get(ADMIN_PREFIX + '/register')
204 response = self.app.get(ADMIN_PREFIX + '/register')
205 response.mustcontain(no=['captcha'])
205 response.mustcontain(no=['captcha'])
206
206
207 def test_title_change(self, csrf_token):
207 def test_title_change(self, csrf_token):
208 old_title = 'RhodeCode'
208 old_title = 'RhodeCode'
209
209
210 for new_title in ['Changed', 'Żółwik', old_title]:
210 for new_title in ['Changed', 'Żółwik', old_title]:
211 response = self.post_and_verify_settings({
211 response = self.post_and_verify_settings({
212 'rhodecode_title': new_title,
212 'rhodecode_title': new_title,
213 'csrf_token': csrf_token,
213 'csrf_token': csrf_token,
214 })
214 })
215
215
216 response = response.follow()
216 response = response.follow()
217 response.mustcontain(
217 response.mustcontain(
218 """<div class="branding">- %s</div>""" % new_title)
218 """<div class="branding">- %s</div>""" % new_title)
219
219
220 def post_and_verify_settings(self, settings):
220 def post_and_verify_settings(self, settings):
221 old_title = 'RhodeCode'
221 old_title = 'RhodeCode'
222 old_realm = 'RhodeCode authentication'
222 old_realm = 'RhodeCode authentication'
223 params = {
223 params = {
224 'rhodecode_title': old_title,
224 'rhodecode_title': old_title,
225 'rhodecode_realm': old_realm,
225 'rhodecode_realm': old_realm,
226 'rhodecode_pre_code': '',
226 'rhodecode_pre_code': '',
227 'rhodecode_post_code': '',
227 'rhodecode_post_code': '',
228 'rhodecode_captcha_private_key': '',
228 'rhodecode_captcha_private_key': '',
229 'rhodecode_captcha_public_key': '',
229 'rhodecode_captcha_public_key': '',
230 'rhodecode_create_personal_repo_group': False,
230 'rhodecode_create_personal_repo_group': False,
231 'rhodecode_personal_repo_group_pattern': '${username}',
231 'rhodecode_personal_repo_group_pattern': '${username}',
232 }
232 }
233 params.update(settings)
233 params.update(settings)
234 response = self.app.post(
234 response = self.app.post(
235 route_path('admin_settings_global_update'), params=params)
235 route_path('admin_settings_global_update'), params=params)
236
236
237 assert_session_flash(response, 'Updated application settings')
237 assert_session_flash(response, 'Updated application settings')
238 app_settings = SettingsModel().get_all_settings()
238 app_settings = SettingsModel().get_all_settings()
239 del settings['csrf_token']
239 del settings['csrf_token']
240 for key, value in settings.iteritems():
240 for key, value in settings.iteritems():
241 assert app_settings[key] == value.decode('utf-8')
241 assert app_settings[key] == value.decode('utf-8')
242
242
243 return response
243 return response
244
244
245
245
246 @pytest.mark.usefixtures('autologin_user', 'app')
246 @pytest.mark.usefixtures('autologin_user', 'app')
247 class TestAdminSettingsVcs(object):
247 class TestAdminSettingsVcs(object):
248
248
249 def test_contains_svn_default_patterns(self):
249 def test_contains_svn_default_patterns(self):
250 response = self.app.get(route_path('admin_settings_vcs'))
250 response = self.app.get(route_path('admin_settings_vcs'))
251 expected_patterns = [
251 expected_patterns = [
252 '/trunk',
252 '/trunk',
253 '/branches/*',
253 '/branches/*',
254 '/tags/*',
254 '/tags/*',
255 ]
255 ]
256 for pattern in expected_patterns:
256 for pattern in expected_patterns:
257 response.mustcontain(pattern)
257 response.mustcontain(pattern)
258
258
259 def test_add_new_svn_branch_and_tag_pattern(
259 def test_add_new_svn_branch_and_tag_pattern(
260 self, backend_svn, form_defaults, disable_sql_cache,
260 self, backend_svn, form_defaults, disable_sql_cache,
261 csrf_token):
261 csrf_token):
262 form_defaults.update({
262 form_defaults.update({
263 'new_svn_branch': '/exp/branches/*',
263 'new_svn_branch': '/exp/branches/*',
264 'new_svn_tag': '/important_tags/*',
264 'new_svn_tag': '/important_tags/*',
265 'csrf_token': csrf_token,
265 'csrf_token': csrf_token,
266 })
266 })
267
267
268 response = self.app.post(
268 response = self.app.post(
269 route_path('admin_settings_vcs_update'),
269 route_path('admin_settings_vcs_update'),
270 params=form_defaults, status=302)
270 params=form_defaults, status=302)
271 response = response.follow()
271 response = response.follow()
272
272
273 # Expect to find the new values on the page
273 # Expect to find the new values on the page
274 response.mustcontain('/exp/branches/*')
274 response.mustcontain('/exp/branches/*')
275 response.mustcontain('/important_tags/*')
275 response.mustcontain('/important_tags/*')
276
276
277 # Expect that those patterns are used to match branches and tags now
277 # Expect that those patterns are used to match branches and tags now
278 repo = backend_svn['svn-simple-layout'].scm_instance()
278 repo = backend_svn['svn-simple-layout'].scm_instance()
279 assert 'exp/branches/exp-sphinx-docs' in repo.branches
279 assert 'exp/branches/exp-sphinx-docs' in repo.branches
280 assert 'important_tags/v0.5' in repo.tags
280 assert 'important_tags/v0.5' in repo.tags
281
281
282 def test_add_same_svn_value_twice_shows_an_error_message(
282 def test_add_same_svn_value_twice_shows_an_error_message(
283 self, form_defaults, csrf_token, settings_util):
283 self, form_defaults, csrf_token, settings_util):
284 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
284 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
285 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
285 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
286
286
287 response = self.app.post(
287 response = self.app.post(
288 route_path('admin_settings_vcs_update'),
288 route_path('admin_settings_vcs_update'),
289 params={
289 params={
290 'paths_root_path': form_defaults['paths_root_path'],
290 'paths_root_path': form_defaults['paths_root_path'],
291 'new_svn_branch': '/test',
291 'new_svn_branch': '/test',
292 'new_svn_tag': '/test',
292 'new_svn_tag': '/test',
293 'csrf_token': csrf_token,
293 'csrf_token': csrf_token,
294 },
294 },
295 status=200)
295 status=200)
296
296
297 response.mustcontain("Pattern already exists")
297 response.mustcontain("Pattern already exists")
298 response.mustcontain("Some form inputs contain invalid data.")
298 response.mustcontain("Some form inputs contain invalid data.")
299
299
300 @pytest.mark.parametrize('section', [
300 @pytest.mark.parametrize('section', [
301 'vcs_svn_branch',
301 'vcs_svn_branch',
302 'vcs_svn_tag',
302 'vcs_svn_tag',
303 ])
303 ])
304 def test_delete_svn_patterns(
304 def test_delete_svn_patterns(
305 self, section, csrf_token, settings_util):
305 self, section, csrf_token, settings_util):
306 setting = settings_util.create_rhodecode_ui(
306 setting = settings_util.create_rhodecode_ui(
307 section, '/test_delete', cleanup=False)
307 section, '/test_delete', cleanup=False)
308
308
309 self.app.post(
309 self.app.post(
310 route_path('admin_settings_vcs_svn_pattern_delete'),
310 route_path('admin_settings_vcs_svn_pattern_delete'),
311 params={
311 params={
312 'delete_svn_pattern': setting.ui_id,
312 'delete_svn_pattern': setting.ui_id,
313 'csrf_token': csrf_token},
313 'csrf_token': csrf_token},
314 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
314 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
315
315
316 @pytest.mark.parametrize('section', [
316 @pytest.mark.parametrize('section', [
317 'vcs_svn_branch',
317 'vcs_svn_branch',
318 'vcs_svn_tag',
318 'vcs_svn_tag',
319 ])
319 ])
320 def test_delete_svn_patterns_raises_404_when_no_xhr(
320 def test_delete_svn_patterns_raises_404_when_no_xhr(
321 self, section, csrf_token, settings_util):
321 self, section, csrf_token, settings_util):
322 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
322 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
323
323
324 self.app.post(
324 self.app.post(
325 route_path('admin_settings_vcs_svn_pattern_delete'),
325 route_path('admin_settings_vcs_svn_pattern_delete'),
326 params={
326 params={
327 'delete_svn_pattern': setting.ui_id,
327 'delete_svn_pattern': setting.ui_id,
328 'csrf_token': csrf_token},
328 'csrf_token': csrf_token},
329 status=404)
329 status=404)
330
330
331 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
331 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
332 form_defaults.update({
332 form_defaults.update({
333 'csrf_token': csrf_token,
333 'csrf_token': csrf_token,
334 'extensions_hgsubversion': 'True',
334 'extensions_hgsubversion': 'True',
335 })
335 })
336 response = self.app.post(
336 response = self.app.post(
337 route_path('admin_settings_vcs_update'),
337 route_path('admin_settings_vcs_update'),
338 params=form_defaults,
338 params=form_defaults,
339 status=302)
339 status=302)
340
340
341 response = response.follow()
341 response = response.follow()
342 extensions_input = (
342 extensions_input = (
343 '<input id="extensions_hgsubversion" '
343 '<input id="extensions_hgsubversion" '
344 'name="extensions_hgsubversion" type="checkbox" '
344 'name="extensions_hgsubversion" type="checkbox" '
345 'value="True" checked="checked" />')
345 'value="True" checked="checked" />')
346 response.mustcontain(extensions_input)
346 response.mustcontain(extensions_input)
347
347
348 def test_extensions_hgevolve(self, form_defaults, csrf_token):
348 def test_extensions_hgevolve(self, form_defaults, csrf_token):
349 form_defaults.update({
349 form_defaults.update({
350 'csrf_token': csrf_token,
350 'csrf_token': csrf_token,
351 'extensions_evolve': 'True',
351 'extensions_evolve': 'True',
352 })
352 })
353 response = self.app.post(
353 response = self.app.post(
354 route_path('admin_settings_vcs_update'),
354 route_path('admin_settings_vcs_update'),
355 params=form_defaults,
355 params=form_defaults,
356 status=302)
356 status=302)
357
357
358 response = response.follow()
358 response = response.follow()
359 extensions_input = (
359 extensions_input = (
360 '<input id="extensions_evolve" '
360 '<input id="extensions_evolve" '
361 'name="extensions_evolve" type="checkbox" '
361 'name="extensions_evolve" type="checkbox" '
362 'value="True" checked="checked" />')
362 'value="True" checked="checked" />')
363 response.mustcontain(extensions_input)
363 response.mustcontain(extensions_input)
364
364
365 def test_has_a_section_for_pull_request_settings(self):
365 def test_has_a_section_for_pull_request_settings(self):
366 response = self.app.get(route_path('admin_settings_vcs'))
366 response = self.app.get(route_path('admin_settings_vcs'))
367 response.mustcontain('Pull Request Settings')
367 response.mustcontain('Pull Request Settings')
368
368
369 def test_has_an_input_for_invalidation_of_inline_comments(self):
369 def test_has_an_input_for_invalidation_of_inline_comments(self):
370 response = self.app.get(route_path('admin_settings_vcs'))
370 response = self.app.get(route_path('admin_settings_vcs'))
371 assert_response = AssertResponse(response)
371 assert_response = AssertResponse(response)
372 assert_response.one_element_exists(
372 assert_response.one_element_exists(
373 '[name=rhodecode_use_outdated_comments]')
373 '[name=rhodecode_use_outdated_comments]')
374
374
375 @pytest.mark.parametrize('new_value', [True, False])
375 @pytest.mark.parametrize('new_value', [True, False])
376 def test_allows_to_change_invalidation_of_inline_comments(
376 def test_allows_to_change_invalidation_of_inline_comments(
377 self, form_defaults, csrf_token, new_value):
377 self, form_defaults, csrf_token, new_value):
378 setting_key = 'use_outdated_comments'
378 setting_key = 'use_outdated_comments'
379 setting = SettingsModel().create_or_update_setting(
379 setting = SettingsModel().create_or_update_setting(
380 setting_key, not new_value, 'bool')
380 setting_key, not new_value, 'bool')
381 Session().add(setting)
381 Session().add(setting)
382 Session().commit()
382 Session().commit()
383
383
384 form_defaults.update({
384 form_defaults.update({
385 'csrf_token': csrf_token,
385 'csrf_token': csrf_token,
386 'rhodecode_use_outdated_comments': str(new_value),
386 'rhodecode_use_outdated_comments': str(new_value),
387 })
387 })
388 response = self.app.post(
388 response = self.app.post(
389 route_path('admin_settings_vcs_update'),
389 route_path('admin_settings_vcs_update'),
390 params=form_defaults,
390 params=form_defaults,
391 status=302)
391 status=302)
392 response = response.follow()
392 response = response.follow()
393 setting = SettingsModel().get_setting_by_name(setting_key)
393 setting = SettingsModel().get_setting_by_name(setting_key)
394 assert setting.app_settings_value is new_value
394 assert setting.app_settings_value is new_value
395
395
396 @pytest.mark.parametrize('new_value', [True, False])
396 @pytest.mark.parametrize('new_value', [True, False])
397 def test_allows_to_change_hg_rebase_merge_strategy(
397 def test_allows_to_change_hg_rebase_merge_strategy(
398 self, form_defaults, csrf_token, new_value):
398 self, form_defaults, csrf_token, new_value):
399 setting_key = 'hg_use_rebase_for_merging'
399 setting_key = 'hg_use_rebase_for_merging'
400
400
401 form_defaults.update({
401 form_defaults.update({
402 'csrf_token': csrf_token,
402 'csrf_token': csrf_token,
403 'rhodecode_' + setting_key: str(new_value),
403 'rhodecode_' + setting_key: str(new_value),
404 })
404 })
405
405
406 with mock.patch.dict(
406 with mock.patch.dict(
407 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
407 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
408 self.app.post(
408 self.app.post(
409 route_path('admin_settings_vcs_update'),
409 route_path('admin_settings_vcs_update'),
410 params=form_defaults,
410 params=form_defaults,
411 status=302)
411 status=302)
412
412
413 setting = SettingsModel().get_setting_by_name(setting_key)
413 setting = SettingsModel().get_setting_by_name(setting_key)
414 assert setting.app_settings_value is new_value
414 assert setting.app_settings_value is new_value
415
415
416 @pytest.fixture
416 @pytest.fixture
417 def disable_sql_cache(self, request):
417 def disable_sql_cache(self, request):
418 patcher = mock.patch(
418 patcher = mock.patch(
419 'rhodecode.lib.caching_query.FromCache.process_query')
419 'rhodecode.lib.caching_query.FromCache.process_query')
420 request.addfinalizer(patcher.stop)
420 request.addfinalizer(patcher.stop)
421 patcher.start()
421 patcher.start()
422
422
423 @pytest.fixture
423 @pytest.fixture
424 def form_defaults(self):
424 def form_defaults(self):
425 from rhodecode.apps.admin.views.settings import AdminSettingsView
425 from rhodecode.apps.admin.views.settings import AdminSettingsView
426 return AdminSettingsView._form_defaults()
426 return AdminSettingsView._form_defaults()
427
427
428 # TODO: johbo: What we really want is to checkpoint before a test run and
428 # TODO: johbo: What we really want is to checkpoint before a test run and
429 # reset the session afterwards.
429 # reset the session afterwards.
430 @pytest.fixture(scope='class', autouse=True)
430 @pytest.fixture(scope='class', autouse=True)
431 def cleanup_settings(self, request, baseapp):
431 def cleanup_settings(self, request, baseapp):
432 ui_id = RhodeCodeUi.ui_id
432 ui_id = RhodeCodeUi.ui_id
433 original_ids = list(
433 original_ids = list(
434 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
434 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
435
435
436 @request.addfinalizer
436 @request.addfinalizer
437 def cleanup():
437 def cleanup():
438 RhodeCodeUi.query().filter(
438 RhodeCodeUi.query().filter(
439 ui_id.notin_(original_ids)).delete(False)
439 ui_id.notin_(original_ids)).delete(False)
440
440
441
441
442 @pytest.mark.usefixtures('autologin_user', 'app')
442 @pytest.mark.usefixtures('autologin_user', 'app')
443 class TestLabsSettings(object):
443 class TestLabsSettings(object):
444 def test_get_settings_page_disabled(self):
444 def test_get_settings_page_disabled(self):
445 with mock.patch.dict(
445 with mock.patch.dict(
446 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
446 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
447
447
448 response = self.app.get(
448 response = self.app.get(
449 route_path('admin_settings_labs'), status=302)
449 route_path('admin_settings_labs'), status=302)
450
450
451 assert response.location.endswith(route_path('admin_settings'))
451 assert response.location.endswith(route_path('admin_settings'))
452
452
453 def test_get_settings_page_enabled(self):
453 def test_get_settings_page_enabled(self):
454 from rhodecode.apps.admin.views import settings
454 from rhodecode.apps.admin.views import settings
455 lab_settings = [
455 lab_settings = [
456 settings.LabSetting(
456 settings.LabSetting(
457 key='rhodecode_bool',
457 key='rhodecode_bool',
458 type='bool',
458 type='bool',
459 group='bool group',
459 group='bool group',
460 label='bool label',
460 label='bool label',
461 help='bool help'
461 help='bool help'
462 ),
462 ),
463 settings.LabSetting(
463 settings.LabSetting(
464 key='rhodecode_text',
464 key='rhodecode_text',
465 type='unicode',
465 type='unicode',
466 group='text group',
466 group='text group',
467 label='text label',
467 label='text label',
468 help='text help'
468 help='text help'
469 ),
469 ),
470 ]
470 ]
471 with mock.patch.dict(rhodecode.CONFIG,
471 with mock.patch.dict(rhodecode.CONFIG,
472 {'labs_settings_active': 'true'}):
472 {'labs_settings_active': 'true'}):
473 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
473 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
474 response = self.app.get(route_path('admin_settings_labs'))
474 response = self.app.get(route_path('admin_settings_labs'))
475
475
476 assert '<label>bool group:</label>' in response
476 assert '<label>bool group:</label>' in response
477 assert '<label for="rhodecode_bool">bool label</label>' in response
477 assert '<label for="rhodecode_bool">bool label</label>' in response
478 assert '<p class="help-block">bool help</p>' in response
478 assert '<p class="help-block">bool help</p>' in response
479 assert 'name="rhodecode_bool" type="checkbox"' in response
479 assert 'name="rhodecode_bool" type="checkbox"' in response
480
480
481 assert '<label>text group:</label>' in response
481 assert '<label>text group:</label>' in response
482 assert '<label for="rhodecode_text">text label</label>' in response
482 assert '<label for="rhodecode_text">text label</label>' in response
483 assert '<p class="help-block">text help</p>' in response
483 assert '<p class="help-block">text help</p>' in response
484 assert 'name="rhodecode_text" size="60" type="text"' in response
484 assert 'name="rhodecode_text" size="60" type="text"' in response
485
485
486
486
487 @pytest.mark.usefixtures('app')
487 @pytest.mark.usefixtures('app')
488 class TestOpenSourceLicenses(object):
488 class TestOpenSourceLicenses(object):
489
489
490 def test_records_are_displayed(self, autologin_user):
490 def test_records_are_displayed(self, autologin_user):
491 sample_licenses = {
491 sample_licenses = [
492 "python2.7-pytest-2.7.1": {
492 {
493 "UNKNOWN": None
493 "license": [
494 {
495 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
496 "shortName": "bsdOriginal",
497 "spdxId": "BSD-4-Clause",
498 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
499 }
500 ],
501 "name": "python2.7-coverage-3.7.1"
494 },
502 },
495 "python2.7-Markdown-2.6.2": {
503 {
496 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
504 "license": [
497 }
505 {
498 }
506 "fullName": "MIT License",
507 "shortName": "mit",
508 "spdxId": "MIT",
509 "url": "http://spdx.org/licenses/MIT.html"
510 }
511 ],
512 "name": "python2.7-bootstrapped-pip-9.0.1"
513 },
514 ]
499 read_licenses_patch = mock.patch(
515 read_licenses_patch = mock.patch(
500 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
516 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
501 return_value=sample_licenses)
517 return_value=sample_licenses)
502 with read_licenses_patch:
518 with read_licenses_patch:
503 response = self.app.get(
519 response = self.app.get(
504 route_path('admin_settings_open_source'), status=200)
520 route_path('admin_settings_open_source'), status=200)
505
521
506 assert_response = AssertResponse(response)
522 assert_response = AssertResponse(response)
507 assert_response.element_contains(
523 assert_response.element_contains(
508 '.panel-heading', 'Licenses of Third Party Packages')
524 '.panel-heading', 'Licenses of Third Party Packages')
509 for name in sample_licenses:
525 for license_data in sample_licenses:
510 response.mustcontain(name)
526 response.mustcontain(license_data["license"][0]["spdxId"])
511 for license in sample_licenses[name]:
527 assert_response.element_contains('.panel-body', license_data["name"])
512 assert_response.element_contains('.panel-body', license)
513
528
514 def test_records_can_be_read(self, autologin_user):
529 def test_records_can_be_read(self, autologin_user):
515 response = self.app.get(
530 response = self.app.get(
516 route_path('admin_settings_open_source'), status=200)
531 route_path('admin_settings_open_source'), status=200)
517 assert_response = AssertResponse(response)
532 assert_response = AssertResponse(response)
518 assert_response.element_contains(
533 assert_response.element_contains(
519 '.panel-heading', 'Licenses of Third Party Packages')
534 '.panel-heading', 'Licenses of Third Party Packages')
520
535
521 def test_forbidden_when_normal_user(self, autologin_regular_user):
536 def test_forbidden_when_normal_user(self, autologin_regular_user):
522 self.app.get(
537 self.app.get(
523 route_path('admin_settings_open_source'), status=404)
538 route_path('admin_settings_open_source'), status=404)
524
539
525
540
526 @pytest.mark.usefixtures('app')
541 @pytest.mark.usefixtures('app')
527 class TestUserSessions(object):
542 class TestUserSessions(object):
528
543
529 def test_forbidden_when_normal_user(self, autologin_regular_user):
544 def test_forbidden_when_normal_user(self, autologin_regular_user):
530 self.app.get(route_path('admin_settings_sessions'), status=404)
545 self.app.get(route_path('admin_settings_sessions'), status=404)
531
546
532 def test_show_sessions_page(self, autologin_user):
547 def test_show_sessions_page(self, autologin_user):
533 response = self.app.get(route_path('admin_settings_sessions'), status=200)
548 response = self.app.get(route_path('admin_settings_sessions'), status=200)
534 response.mustcontain('file')
549 response.mustcontain('file')
535
550
536 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
551 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
537
552
538 post_data = {
553 post_data = {
539 'csrf_token': csrf_token,
554 'csrf_token': csrf_token,
540 'expire_days': '60'
555 'expire_days': '60'
541 }
556 }
542 response = self.app.post(
557 response = self.app.post(
543 route_path('admin_settings_sessions_cleanup'), params=post_data,
558 route_path('admin_settings_sessions_cleanup'), params=post_data,
544 status=302)
559 status=302)
545 assert_session_flash(response, 'Cleaned up old sessions')
560 assert_session_flash(response, 'Cleaned up old sessions')
546
561
547
562
548 @pytest.mark.usefixtures('app')
563 @pytest.mark.usefixtures('app')
549 class TestAdminSystemInfo(object):
564 class TestAdminSystemInfo(object):
550
565
551 def test_forbidden_when_normal_user(self, autologin_regular_user):
566 def test_forbidden_when_normal_user(self, autologin_regular_user):
552 self.app.get(route_path('admin_settings_system'), status=404)
567 self.app.get(route_path('admin_settings_system'), status=404)
553
568
554 def test_system_info_page(self, autologin_user):
569 def test_system_info_page(self, autologin_user):
555 response = self.app.get(route_path('admin_settings_system'))
570 response = self.app.get(route_path('admin_settings_system'))
556 response.mustcontain('RhodeCode Community Edition, version {}'.format(
571 response.mustcontain('RhodeCode Community Edition, version {}'.format(
557 rhodecode.__version__))
572 rhodecode.__version__))
558
573
559 def test_system_update_new_version(self, autologin_user):
574 def test_system_update_new_version(self, autologin_user):
560 update_data = {
575 update_data = {
561 'versions': [
576 'versions': [
562 {
577 {
563 'version': '100.3.1415926535',
578 'version': '100.3.1415926535',
564 'general': 'The latest version we are ever going to ship'
579 'general': 'The latest version we are ever going to ship'
565 },
580 },
566 {
581 {
567 'version': '0.0.0',
582 'version': '0.0.0',
568 'general': 'The first version we ever shipped'
583 'general': 'The first version we ever shipped'
569 }
584 }
570 ]
585 ]
571 }
586 }
572 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
587 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
573 response = self.app.get(route_path('admin_settings_system_update'))
588 response = self.app.get(route_path('admin_settings_system_update'))
574 response.mustcontain('A <b>new version</b> is available')
589 response.mustcontain('A <b>new version</b> is available')
575
590
576 def test_system_update_nothing_new(self, autologin_user):
591 def test_system_update_nothing_new(self, autologin_user):
577 update_data = {
592 update_data = {
578 'versions': [
593 'versions': [
579 {
594 {
580 'version': '0.0.0',
595 'version': '0.0.0',
581 'general': 'The first version we ever shipped'
596 'general': 'The first version we ever shipped'
582 }
597 }
583 ]
598 ]
584 }
599 }
585 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
600 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
586 response = self.app.get(route_path('admin_settings_system_update'))
601 response = self.app.get(route_path('admin_settings_system_update'))
587 response.mustcontain(
602 response.mustcontain(
588 'This instance is already running the <b>latest</b> stable version')
603 'This instance is already running the <b>latest</b> stable version')
589
604
590 def test_system_update_bad_response(self, autologin_user):
605 def test_system_update_bad_response(self, autologin_user):
591 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
606 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
592 response = self.app.get(route_path('admin_settings_system_update'))
607 response = self.app.get(route_path('admin_settings_system_update'))
593 response.mustcontain(
608 response.mustcontain(
594 'Bad data sent from update server')
609 'Bad data sent from update server')
595
610
596
611
597 @pytest.mark.usefixtures("app")
612 @pytest.mark.usefixtures("app")
598 class TestAdminSettingsIssueTracker(object):
613 class TestAdminSettingsIssueTracker(object):
599 RC_PREFIX = 'rhodecode_'
614 RC_PREFIX = 'rhodecode_'
600 SHORT_PATTERN_KEY = 'issuetracker_pat_'
615 SHORT_PATTERN_KEY = 'issuetracker_pat_'
601 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
616 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
602
617
603 def test_issuetracker_index(self, autologin_user):
618 def test_issuetracker_index(self, autologin_user):
604 response = self.app.get(route_path('admin_settings_issuetracker'))
619 response = self.app.get(route_path('admin_settings_issuetracker'))
605 assert response.status_code == 200
620 assert response.status_code == 200
606
621
607 def test_add_empty_issuetracker_pattern(
622 def test_add_empty_issuetracker_pattern(
608 self, request, autologin_user, csrf_token):
623 self, request, autologin_user, csrf_token):
609 post_url = route_path('admin_settings_issuetracker_update')
624 post_url = route_path('admin_settings_issuetracker_update')
610 post_data = {
625 post_data = {
611 'csrf_token': csrf_token
626 'csrf_token': csrf_token
612 }
627 }
613 self.app.post(post_url, post_data, status=302)
628 self.app.post(post_url, post_data, status=302)
614
629
615 def test_add_issuetracker_pattern(
630 def test_add_issuetracker_pattern(
616 self, request, autologin_user, csrf_token):
631 self, request, autologin_user, csrf_token):
617 pattern = 'issuetracker_pat'
632 pattern = 'issuetracker_pat'
618 another_pattern = pattern+'1'
633 another_pattern = pattern+'1'
619 post_url = route_path('admin_settings_issuetracker_update')
634 post_url = route_path('admin_settings_issuetracker_update')
620 post_data = {
635 post_data = {
621 'new_pattern_pattern_0': pattern,
636 'new_pattern_pattern_0': pattern,
622 'new_pattern_url_0': 'http://url',
637 'new_pattern_url_0': 'http://url',
623 'new_pattern_prefix_0': 'prefix',
638 'new_pattern_prefix_0': 'prefix',
624 'new_pattern_description_0': 'description',
639 'new_pattern_description_0': 'description',
625 'new_pattern_pattern_1': another_pattern,
640 'new_pattern_pattern_1': another_pattern,
626 'new_pattern_url_1': 'https://url1',
641 'new_pattern_url_1': 'https://url1',
627 'new_pattern_prefix_1': 'prefix1',
642 'new_pattern_prefix_1': 'prefix1',
628 'new_pattern_description_1': 'description1',
643 'new_pattern_description_1': 'description1',
629 'csrf_token': csrf_token
644 'csrf_token': csrf_token
630 }
645 }
631 self.app.post(post_url, post_data, status=302)
646 self.app.post(post_url, post_data, status=302)
632 settings = SettingsModel().get_all_settings()
647 settings = SettingsModel().get_all_settings()
633 self.uid = md5(pattern)
648 self.uid = md5(pattern)
634 assert settings[self.PATTERN_KEY+self.uid] == pattern
649 assert settings[self.PATTERN_KEY+self.uid] == pattern
635 self.another_uid = md5(another_pattern)
650 self.another_uid = md5(another_pattern)
636 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
651 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
637
652
638 @request.addfinalizer
653 @request.addfinalizer
639 def cleanup():
654 def cleanup():
640 defaults = SettingsModel().get_all_settings()
655 defaults = SettingsModel().get_all_settings()
641
656
642 entries = [name for name in defaults if (
657 entries = [name for name in defaults if (
643 (self.uid in name) or (self.another_uid) in name)]
658 (self.uid in name) or (self.another_uid) in name)]
644 start = len(self.RC_PREFIX)
659 start = len(self.RC_PREFIX)
645 for del_key in entries:
660 for del_key in entries:
646 # TODO: anderson: get_by_name needs name without prefix
661 # TODO: anderson: get_by_name needs name without prefix
647 entry = SettingsModel().get_setting_by_name(del_key[start:])
662 entry = SettingsModel().get_setting_by_name(del_key[start:])
648 Session().delete(entry)
663 Session().delete(entry)
649
664
650 Session().commit()
665 Session().commit()
651
666
652 def test_edit_issuetracker_pattern(
667 def test_edit_issuetracker_pattern(
653 self, autologin_user, backend, csrf_token, request):
668 self, autologin_user, backend, csrf_token, request):
654 old_pattern = 'issuetracker_pat'
669 old_pattern = 'issuetracker_pat'
655 old_uid = md5(old_pattern)
670 old_uid = md5(old_pattern)
656 pattern = 'issuetracker_pat_new'
671 pattern = 'issuetracker_pat_new'
657 self.new_uid = md5(pattern)
672 self.new_uid = md5(pattern)
658
673
659 SettingsModel().create_or_update_setting(
674 SettingsModel().create_or_update_setting(
660 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
675 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
661
676
662 post_url = route_path('admin_settings_issuetracker_update')
677 post_url = route_path('admin_settings_issuetracker_update')
663 post_data = {
678 post_data = {
664 'new_pattern_pattern_0': pattern,
679 'new_pattern_pattern_0': pattern,
665 'new_pattern_url_0': 'https://url',
680 'new_pattern_url_0': 'https://url',
666 'new_pattern_prefix_0': 'prefix',
681 'new_pattern_prefix_0': 'prefix',
667 'new_pattern_description_0': 'description',
682 'new_pattern_description_0': 'description',
668 'uid': old_uid,
683 'uid': old_uid,
669 'csrf_token': csrf_token
684 'csrf_token': csrf_token
670 }
685 }
671 self.app.post(post_url, post_data, status=302)
686 self.app.post(post_url, post_data, status=302)
672 settings = SettingsModel().get_all_settings()
687 settings = SettingsModel().get_all_settings()
673 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
688 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
674 assert self.PATTERN_KEY+old_uid not in settings
689 assert self.PATTERN_KEY+old_uid not in settings
675
690
676 @request.addfinalizer
691 @request.addfinalizer
677 def cleanup():
692 def cleanup():
678 IssueTrackerSettingsModel().delete_entries(self.new_uid)
693 IssueTrackerSettingsModel().delete_entries(self.new_uid)
679
694
680 def test_replace_issuetracker_pattern_description(
695 def test_replace_issuetracker_pattern_description(
681 self, autologin_user, csrf_token, request, settings_util):
696 self, autologin_user, csrf_token, request, settings_util):
682 prefix = 'issuetracker'
697 prefix = 'issuetracker'
683 pattern = 'issuetracker_pat'
698 pattern = 'issuetracker_pat'
684 self.uid = md5(pattern)
699 self.uid = md5(pattern)
685 pattern_key = '_'.join([prefix, 'pat', self.uid])
700 pattern_key = '_'.join([prefix, 'pat', self.uid])
686 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
701 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
687 desc_key = '_'.join([prefix, 'desc', self.uid])
702 desc_key = '_'.join([prefix, 'desc', self.uid])
688 rc_desc_key = '_'.join(['rhodecode', desc_key])
703 rc_desc_key = '_'.join(['rhodecode', desc_key])
689 new_description = 'new_description'
704 new_description = 'new_description'
690
705
691 settings_util.create_rhodecode_setting(
706 settings_util.create_rhodecode_setting(
692 pattern_key, pattern, 'unicode', cleanup=False)
707 pattern_key, pattern, 'unicode', cleanup=False)
693 settings_util.create_rhodecode_setting(
708 settings_util.create_rhodecode_setting(
694 desc_key, 'old description', 'unicode', cleanup=False)
709 desc_key, 'old description', 'unicode', cleanup=False)
695
710
696 post_url = route_path('admin_settings_issuetracker_update')
711 post_url = route_path('admin_settings_issuetracker_update')
697 post_data = {
712 post_data = {
698 'new_pattern_pattern_0': pattern,
713 'new_pattern_pattern_0': pattern,
699 'new_pattern_url_0': 'https://url',
714 'new_pattern_url_0': 'https://url',
700 'new_pattern_prefix_0': 'prefix',
715 'new_pattern_prefix_0': 'prefix',
701 'new_pattern_description_0': new_description,
716 'new_pattern_description_0': new_description,
702 'uid': self.uid,
717 'uid': self.uid,
703 'csrf_token': csrf_token
718 'csrf_token': csrf_token
704 }
719 }
705 self.app.post(post_url, post_data, status=302)
720 self.app.post(post_url, post_data, status=302)
706 settings = SettingsModel().get_all_settings()
721 settings = SettingsModel().get_all_settings()
707 assert settings[rc_pattern_key] == pattern
722 assert settings[rc_pattern_key] == pattern
708 assert settings[rc_desc_key] == new_description
723 assert settings[rc_desc_key] == new_description
709
724
710 @request.addfinalizer
725 @request.addfinalizer
711 def cleanup():
726 def cleanup():
712 IssueTrackerSettingsModel().delete_entries(self.uid)
727 IssueTrackerSettingsModel().delete_entries(self.uid)
713
728
714 def test_delete_issuetracker_pattern(
729 def test_delete_issuetracker_pattern(
715 self, autologin_user, backend, csrf_token, settings_util):
730 self, autologin_user, backend, csrf_token, settings_util):
716 pattern = 'issuetracker_pat'
731 pattern = 'issuetracker_pat'
717 uid = md5(pattern)
732 uid = md5(pattern)
718 settings_util.create_rhodecode_setting(
733 settings_util.create_rhodecode_setting(
719 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
734 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
720
735
721 post_url = route_path('admin_settings_issuetracker_delete')
736 post_url = route_path('admin_settings_issuetracker_delete')
722 post_data = {
737 post_data = {
723 '_method': 'delete',
738 '_method': 'delete',
724 'uid': uid,
739 'uid': uid,
725 'csrf_token': csrf_token
740 'csrf_token': csrf_token
726 }
741 }
727 self.app.post(post_url, post_data, status=302)
742 self.app.post(post_url, post_data, status=302)
728 settings = SettingsModel().get_all_settings()
743 settings = SettingsModel().get_all_settings()
729 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
744 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
@@ -1,53 +1,51 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import collections
21 import collections
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.admin.navigation import navigation_list
27 from rhodecode.apps.admin.navigation import navigation_list
28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
29 from rhodecode.lib.utils import read_opensource_licenses
29 from rhodecode.lib.utils import read_opensource_licenses
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 class OpenSourceLicensesAdminSettingsView(BaseAppView):
34 class OpenSourceLicensesAdminSettingsView(BaseAppView):
35
35
36 def load_default_context(self):
36 def load_default_context(self):
37 c = self._get_local_tmpl_context()
37 c = self._get_local_tmpl_context()
38
39 return c
38 return c
40
39
41 @LoginRequired()
40 @LoginRequired()
42 @HasPermissionAllDecorator('hg.admin')
41 @HasPermissionAllDecorator('hg.admin')
43 @view_config(
42 @view_config(
44 route_name='admin_settings_open_source', request_method='GET',
43 route_name='admin_settings_open_source', request_method='GET',
45 renderer='rhodecode:templates/admin/settings/settings.mako')
44 renderer='rhodecode:templates/admin/settings/settings.mako')
46 def open_source_licenses(self):
45 def open_source_licenses(self):
47 c = self.load_default_context()
46 c = self.load_default_context()
48 c.active = 'open_source'
47 c.active = 'open_source'
49 c.navlist = navigation_list(self.request)
48 c.navlist = navigation_list(self.request)
50 items = sorted(read_opensource_licenses().items(), key=lambda t: t[0])
49 c.opensource_licenses = sorted(
51 c.opensource_licenses = collections.OrderedDict(items)
50 read_opensource_licenses(), key=lambda d: d["name"])
52
53 return self._get_template_context(c)
51 return self._get_template_context(c)
This diff has been collapsed as it changes many lines, (2281 lines changed) Show them Hide them
@@ -1,369 +1,1912 b''
1 {
1 [
2 "libnghttp2-1.7.1": {
2 {
3 "MIT License": "http://spdx.org/licenses/MIT"
3 "license": [
4 },
4 {
5 "nodejs-4.3.1": {
5 "fullName": "Python Software Foundation License version 2",
6 "MIT License": "http://spdx.org/licenses/MIT"
6 "shortName": "psfl",
7 },
7 "spdxId": "Python-2.0",
8 "python-2.7.12": {
8 "url": "http://spdx.org/licenses/Python-2.0.html"
9 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
9 },
10 },
10 {
11 "python2.7-Babel-1.3": {
11 "fullName": "Zope Public License 2.0",
12 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
12 "shortName": "zpl20",
13 },
13 "spdxId": "ZPL-2.0",
14 "python2.7-Beaker-1.7.0": {
14 "url": "http://spdx.org/licenses/ZPL-2.0.html"
15 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
15 }
16 },
16 ],
17 "python2.7-Chameleon-2.24": {
17 "name": "python2.7-setuptools-38.4.0"
18 "BSD-like": "http://repoze.org/license.html"
18 },
19 },
19 {
20 "python2.7-FormEncode-1.2.4": {
20 "license": {
21 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
21 "fullName": "Python Software Foundation License version 2",
22 },
22 "shortName": "psfl",
23 "python2.7-Jinja2-2.7.3": {
23 "spdxId": "Python-2.0",
24 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
24 "url": "http://spdx.org/licenses/Python-2.0.html"
25 },
25 },
26 "python2.7-Mako-1.0.6": {
26 "name": "python-2.7.15"
27 "MIT License": "http://spdx.org/licenses/MIT"
27 },
28 },
28 {
29 "python2.7-Markdown-2.6.7": {
29 "license": [
30 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
30 {
31 },
31 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
32 "python2.7-MarkupSafe-0.23": {
32 "shortName": "bsdOriginal",
33 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
33 "spdxId": "BSD-4-Clause",
34 },
34 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
35 "python2.7-Paste-2.0.3": {
35 }
36 "MIT License": "http://spdx.org/licenses/MIT"
36 ],
37 },
37 "name": "python2.7-coverage-3.7.1"
38 "python2.7-PasteDeploy-1.5.2": {
38 },
39 "MIT License": "http://spdx.org/licenses/MIT"
39 {
40 },
40 "license": [
41 "python2.7-PasteScript-1.7.5": {
41 {
42 "MIT License": "http://spdx.org/licenses/MIT"
42 "fullName": "MIT License",
43 },
43 "shortName": "mit",
44 "python2.7-Pygments-2.2.0": {
44 "spdxId": "MIT",
45 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
45 "url": "http://spdx.org/licenses/MIT.html"
46 },
46 }
47 "python2.7-Routes-1.13": {
47 ],
48 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
48 "name": "python2.7-bootstrapped-pip-9.0.1"
49 },
49 },
50 "python2.7-SQLAlchemy-0.9.9": {
50 {
51 "MIT License": "http://spdx.org/licenses/MIT"
51 "license": [
52 },
52 {
53 "python2.7-Tempita-0.5.2": {
53 "fullName": "MIT License",
54 "MIT License": "http://spdx.org/licenses/MIT"
54 "shortName": "mit",
55 },
55 "spdxId": "MIT",
56 "python2.7-URLObject-2.4.0": {
56 "url": "http://spdx.org/licenses/MIT.html"
57 "The Unlicense": "http://unlicense.org/"
57 }
58 },
58 ],
59 "python2.7-WebError-0.10.3": {
59 "name": "python2.7-cov-core-1.15.0"
60 "MIT License": "http://spdx.org/licenses/MIT"
60 },
61 },
61 {
62 "python2.7-WebHelpers-1.3": {
62 "license": [
63 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
63 {
64 },
64 "fullName": "MIT License",
65 "python2.7-WebHelpers2-2.0": {
65 "shortName": "mit",
66 "MIT License": "http://spdx.org/licenses/MIT"
66 "spdxId": "MIT",
67 },
67 "url": "http://spdx.org/licenses/MIT.html"
68 "python2.7-WebOb-1.3.1": {
68 }
69 "MIT License": "http://spdx.org/licenses/MIT"
69 ],
70 },
70 "name": "python2.7-webtest-2.0.29"
71 "python2.7-Whoosh-2.7.4": {
71 },
72 "BSD 2-clause \"Simplified\" License": "http://spdx.org/licenses/BSD-2-Clause",
72 {
73 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
73 "license": [
74 },
74 {
75 "python2.7-alembic-0.8.4": {
75 "fullName": "MIT License",
76 "MIT License": "http://spdx.org/licenses/MIT"
76 "shortName": "mit",
77 },
77 "spdxId": "MIT",
78 "python2.7-appenlight-client-0.6.14": {
78 "url": "http://spdx.org/licenses/MIT.html"
79 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
79 }
80 },
80 ],
81 "python2.7-authomatic-0.1.0.post1": {
81 "name": "python2.7-beautifulsoup4-4.6.3"
82 "MIT License": "http://spdx.org/licenses/MIT"
82 },
83 },
83 {
84 "python2.7-backports.shutil-get-terminal-size-1.0.0": {
84 "license": [
85 "MIT License": "http://spdx.org/licenses/MIT"
85 {
86 },
86 "fullName": "Zope Public License 2.1",
87 "python2.7-bleach-1.5.0": {
87 "shortName": "zpl21",
88 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
88 "spdxId": "ZPL-2.1",
89 },
89 "url": "http://spdx.org/licenses/ZPL-2.1.html"
90 "python2.7-celery-2.2.10": {
90 }
91 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
91 ],
92 },
92 "name": "python2.7-waitress-1.1.0"
93 "python2.7-channelstream-0.5.2": {
93 },
94 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
94 {
95 },
95 "license": [
96 "python2.7-click-5.1": {
96 {
97 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
97 "fullName": "MIT License",
98 },
98 "shortName": "mit",
99 "python2.7-colander-1.2": {
99 "spdxId": "MIT",
100 "Repoze License": "http://www.repoze.org/LICENSE.txt"
100 "url": "http://spdx.org/licenses/MIT.html"
101 },
101 }
102 "python2.7-configobj-5.0.6": {
102 ],
103 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
103 "name": "python2.7-webob-1.7.4"
104 },
104 },
105 "python2.7-configparser-3.5.0": {
105 {
106 "MIT License": "http://spdx.org/licenses/MIT"
106 "license": [
107 },
107 {
108 "python2.7-cssselect-1.0.1": {
108 "fullName": "MIT License",
109 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
109 "shortName": "mit",
110 },
110 "spdxId": "MIT",
111 "python2.7-decorator-4.0.11": {
111 "url": "http://spdx.org/licenses/MIT.html"
112 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
112 }
113 },
113 ],
114 "python2.7-deform-2.0a2": {
114 "name": "python2.7-six-1.11.0"
115 "BSD-derived": "http://www.repoze.org/LICENSE.txt"
115 },
116 },
116 {
117 "python2.7-docutils-0.12": {
117 "license": [
118 "BSD 2-clause \"Simplified\" License": "http://spdx.org/licenses/BSD-2-Clause"
118 {
119 },
119 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
120 "python2.7-dogpile.cache-0.6.1": {
120 "shortName": "bsdOriginal",
121 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
121 "spdxId": "BSD-4-Clause",
122 },
122 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
123 "python2.7-dogpile.core-0.4.1": {
123 }
124 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
124 ],
125 },
125 "name": "python2.7-mock-1.0.1"
126 "python2.7-elasticsearch-2.3.0": {
126 },
127 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
127 {
128 },
128 "license": [
129 "python2.7-elasticsearch-dsl-2.2.0": {
129 {
130 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
130 "fullName": "MIT License",
131 },
131 "shortName": "mit",
132 "python2.7-entrypoints-0.2.2": {
132 "spdxId": "MIT",
133 "MIT License": "http://spdx.org/licenses/MIT"
133 "url": "http://spdx.org/licenses/MIT.html"
134 },
134 },
135 "python2.7-enum34-1.1.6": {
135 {
136 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
136 "fullName": "DFSG approved"
137 },
137 }
138 "python2.7-functools32-3.2.3.post2": {
138 ],
139 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
139 "name": "python2.7-pytest-timeout-1.2.1"
140 },
140 },
141 "python2.7-future-0.14.3": {
141 {
142 "MIT License": "http://spdx.org/licenses/MIT"
142 "license": [
143 },
143 {
144 "python2.7-futures-3.0.2": {
144 "fullName": "MIT License",
145 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
145 "shortName": "mit",
146 },
146 "spdxId": "MIT",
147 "python2.7-gevent-1.1.2": {
147 "url": "http://spdx.org/licenses/MIT.html"
148 "MIT License": "http://spdx.org/licenses/MIT"
148 }
149 },
149 ],
150 "python2.7-gnureadline-6.3.3": {
150 "name": "python2.7-pytest-3.6.0"
151 "GNU General Public License v1.0 only": "http://spdx.org/licenses/GPL-1.0"
151 },
152 },
152 {
153 "python2.7-gprof2dot-2016.10.13": {
153 "license": [
154 "GNU Lesser General Public License v3.0 or later": "http://spdx.org/licenses/LGPL-3.0+"
154 {
155 },
155 "fullName": "ASL"
156 "python2.7-greenlet-0.4.10": {
156 },
157 "MIT License": "http://spdx.org/licenses/MIT"
157 {
158 },
158 "fullName": "Apache License 2.0",
159 "python2.7-gunicorn-19.6.0": {
159 "shortName": "asl20",
160 "MIT License": "http://spdx.org/licenses/MIT"
160 "spdxId": "Apache-2.0",
161 },
161 "url": "http://spdx.org/licenses/Apache-2.0.html"
162 "python2.7-html5lib-0.9999999": {
162 }
163 "MIT License": "http://spdx.org/licenses/MIT"
163 ],
164 },
164 "name": "python2.7-funcsigs-1.0.2"
165 "python2.7-infrae.cache-1.0.1": {
165 },
166 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
166 {
167 },
167 "license": [
168 "python2.7-ipython-5.1.0": {
168 {
169 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
169 "fullName": "MIT License",
170 },
170 "shortName": "mit",
171 "python2.7-ipython-genutils-0.2.0": {
171 "spdxId": "MIT",
172 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
172 "url": "http://spdx.org/licenses/MIT.html"
173 },
173 }
174 "python2.7-iso8601-0.1.11": {
174 ],
175 "MIT License": "http://spdx.org/licenses/MIT"
175 "name": "python2.7-pluggy-0.6.0"
176 },
176 },
177 "python2.7-itsdangerous-0.24": {
177 {
178 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
178 "license": [
179 },
179 {
180 "python2.7-jsonschema-2.6.0": {
180 "fullName": "MIT License",
181 "MIT License": "http://spdx.org/licenses/MIT"
181 "shortName": "mit",
182 },
182 "spdxId": "MIT",
183 "python2.7-jupyter-client-5.0.0": {
183 "url": "http://spdx.org/licenses/MIT.html"
184 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
184 }
185 },
185 ],
186 "python2.7-jupyter-core-4.3.0": {
186 "name": "python2.7-atomicwrites-1.1.5"
187 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
187 },
188 },
188 {
189 "python2.7-kombu-4.1.0": {
189 "license": [
190 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
190 {
191 },
191 "fullName": "MIT License",
192 "python2.7-mistune-0.7.4": {
192 "shortName": "mit",
193 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
193 "spdxId": "MIT",
194 },
194 "url": "http://spdx.org/licenses/MIT.html"
195 "python2.7-msgpack-python-0.4.8": {
195 }
196 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
196 ],
197 },
197 "name": "python2.7-more-itertools-4.3.0"
198 "python2.7-nbconvert-5.1.1": {
198 },
199 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
199 {
200 },
200 "license": [
201 "python2.7-nbformat-4.3.0": {
201 {
202 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
202 "fullName": "MIT License",
203 },
203 "shortName": "mit",
204 "python2.7-packaging-15.2": {
204 "spdxId": "MIT",
205 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
205 "url": "http://spdx.org/licenses/MIT.html"
206 },
206 }
207 "python2.7-pandocfilters-1.4.1": {
207 ],
208 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
208 "name": "python2.7-attrs-18.1.0"
209 },
209 },
210 "python2.7-pathlib2-2.1.0": {
210 {
211 "MIT License": "http://spdx.org/licenses/MIT"
211 "license": [
212 },
212 {
213 "python2.7-peppercorn-0.5": {
213 "fullName": "MIT License",
214 "BSD-derived": "http://www.repoze.org/LICENSE.txt"
214 "shortName": "mit",
215 },
215 "spdxId": "MIT",
216 "python2.7-pexpect-4.2.1": {
216 "url": "http://spdx.org/licenses/MIT.html"
217 "ISC License": "http://spdx.org/licenses/ISC"
217 }
218 },
218 ],
219 "python2.7-pickleshare-0.7.4": {
219 "name": "python2.7-py-1.5.3"
220 "MIT License": "http://spdx.org/licenses/MIT"
220 },
221 },
221 {
222 "python2.7-prompt-toolkit-1.0.14": {
222 "license": [
223 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
223 {
224 },
224 "fullName": "GNU Lesser General Public License v3 or later (LGPLv3+)"
225 "python2.7-psutil-4.3.1": {
225 },
226 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
226 {
227 },
227 "fullName": "LGPL"
228 "python2.7-psycopg2-2.6.1": {
228 }
229 "GNU Lesser General Public License v3.0 or later": "http://spdx.org/licenses/LGPL-3.0+"
229 ],
230 },
230 "name": "python2.7-gprof2dot-2017.9.19"
231 "python2.7-ptyprocess-0.5.1": {
231 },
232 "ISC License": "http://opensource.org/licenses/ISC"
232 {
233 },
233 "license": [
234 "python2.7-py-1.4.31": {
234 {
235 "MIT License": "http://spdx.org/licenses/MIT"
235 "fullName": "MIT License",
236 },
236 "shortName": "mit",
237 "python2.7-py-bcrypt-0.4": {
237 "spdxId": "MIT",
238 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
238 "url": "http://spdx.org/licenses/MIT.html"
239 },
239 }
240 "python2.7-py-gfm-0.1.3.rhodecode-upstream1": {
240 ],
241 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
241 "name": "python2.7-pytest-profiling-1.3.0"
242 },
242 },
243 "python2.7-pycrypto-2.6.1": {
243 {
244 "Public Domain": null
244 "license": [
245 },
245 {
246 "python2.7-pycurl-7.19.5": {
246 "fullName": "MIT License",
247 "MIT License": "http://spdx.org/licenses/MIT"
247 "shortName": "mit",
248 },
248 "spdxId": "MIT",
249 "python2.7-pygments-markdown-lexer-0.1.0.dev39": {
249 "url": "http://spdx.org/licenses/MIT.html"
250 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
250 }
251 },
251 ],
252 "python2.7-pyparsing-1.5.7": {
252 "name": "python2.7-pytest-runner-4.2"
253 "MIT License": "http://spdx.org/licenses/MIT"
253 },
254 },
254 {
255 "python2.7-pyramid-1.7.4": {
255 "license": [
256 "Repoze License": "http://www.repoze.org/LICENSE.txt"
256 {
257 },
257 "fullName": "MIT License",
258 "python2.7-pyramid-beaker-0.8": {
258 "shortName": "mit",
259 "Repoze License": "http://www.repoze.org/LICENSE.txt"
259 "spdxId": "MIT",
260 },
260 "url": "http://spdx.org/licenses/MIT.html"
261 "python2.7-pyramid-debugtoolbar-3.0.5": {
261 }
262 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause",
262 ],
263 "Repoze License": "http://www.repoze.org/LICENSE.txt"
263 "name": "python2.7-setuptools-scm-2.1.0"
264 },
264 },
265 "python2.7-pyramid-jinja2-2.5": {
265 {
266 "BSD-derived": "http://www.repoze.org/LICENSE.txt"
266 "license": [
267 },
267 {
268 "python2.7-pyramid-mako-1.0.2": {
268 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
269 "Repoze License": "http://www.repoze.org/LICENSE.txt"
269 "shortName": "bsdOriginal",
270 },
270 "spdxId": "BSD-4-Clause",
271 "python2.7-pysqlite-2.6.3": {
271 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
272 "libpng License": "http://spdx.org/licenses/Libpng",
272 }
273 "zlib License": "http://spdx.org/licenses/Zlib"
273 ],
274 },
274 "name": "python2.7-pytest-sugar-0.9.1"
275 "python2.7-pytest-3.0.5": {
275 },
276 "MIT License": "http://spdx.org/licenses/MIT"
276 {
277 },
277 "license": [
278 "python2.7-pytest-profiling-1.2.2": {
278 {
279 "MIT License": "http://spdx.org/licenses/MIT"
279 "fullName": "MIT License",
280 },
280 "shortName": "mit",
281 "python2.7-pytest-runner-2.9": {
281 "spdxId": "MIT",
282 "MIT License": "http://spdx.org/licenses/MIT"
282 "url": "http://spdx.org/licenses/MIT.html"
283 },
283 }
284 "python2.7-pytest-sugar-0.7.1": {
284 ],
285 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
285 "name": "python2.7-termcolor-1.1.0"
286 },
286 },
287 "python2.7-pytest-timeout-1.2.0": {
287 {
288 "MIT License": "http://spdx.org/licenses/MIT"
288 "license": [
289 },
289 {
290 "python2.7-python-dateutil-2.1": {
290 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
291 "Simplified BSD": null
291 "shortName": "bsdOriginal",
292 },
292 "spdxId": "BSD-4-Clause",
293 "python2.7-python-editor-1.0.3": {
293 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
294 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
294 },
295 },
295 {
296 "python2.7-python-ldap-2.4.19": {
296 "fullName": "MIT License",
297 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
297 "shortName": "mit",
298 },
298 "spdxId": "MIT",
299 "python2.7-python-memcached-1.57": {
299 "url": "http://spdx.org/licenses/MIT.html"
300 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
300 }
301 },
301 ],
302 "python2.7-pytz-2015.4": {
302 "name": "python2.7-pytest-cov-2.5.1"
303 "MIT License": "http://spdx.org/licenses/MIT"
303 },
304 },
304 {
305 "python2.7-pyzmq-14.6.0": {
305 "license": [
306 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
306 {
307 },
307 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
308 "python2.7-repoze.lru-0.6": {
308 "shortName": "bsdOriginal",
309 "Repoze License": "http://www.repoze.org/LICENSE.txt"
309 "spdxId": "BSD-4-Clause",
310 },
310 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
311 "python2.7-requests-2.9.1": {
311 }
312 "Apache License 2.0": "http://spdx.org/licenses/Apache-2.0"
312 ],
313 },
313 "name": "python2.7-appenlight-client-0.6.25"
314 "python2.7-setuptools-scm-1.15.6": {
314 },
315 "MIT License": "http://spdx.org/licenses/MIT"
315 {
316 },
316 "license": [
317 "python2.7-simplegeneric-0.8.1": {
317 {
318 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
318 "fullName": "Apache License 2.0",
319 },
319 "shortName": "asl20",
320 "python2.7-simplejson-3.7.2": {
320 "spdxId": "Apache-2.0",
321 "MIT License": "http://spdx.org/licenses/MIT"
321 "url": "http://spdx.org/licenses/Apache-2.0.html"
322 },
322 }
323 "python2.7-six-1.9.0": {
323 ],
324 "MIT License": "http://spdx.org/licenses/MIT"
324 "name": "python2.7-requests-2.9.1"
325 },
325 },
326 "python2.7-subprocess32-3.2.6": {
326 {
327 "Python Software Foundation License version 2": "http://spdx.org/licenses/Python-2.0"
327 "license": [
328 },
328 {
329 "python2.7-termcolor-1.1.0": {
329 "fullName": "AGPLv3 and Proprietary"
330 "MIT License": "http://spdx.org/licenses/MIT"
330 }
331 },
331 ],
332 "python2.7-testpath-0.1": {
332 "name": "python2.7-rhodecode-tools-0.16.0"
333 "MIT License": "http://spdx.org/licenses/MIT"
333 },
334 },
334 {
335 "python2.7-traitlets-4.3.2": {
335 "license": [
336 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
336 {
337 },
337 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
338 "python2.7-translationstring-1.3": {
338 "shortName": "bsdOriginal",
339 "Repoze License": "http://www.repoze.org/LICENSE.txt"
339 "spdxId": "BSD-4-Clause",
340 },
340 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
341 "python2.7-urllib3-1.16": {
341 },
342 "MIT License": "http://spdx.org/licenses/MIT"
342 {
343 },
343 "fullName": "BSD 2-clause \"Simplified\" License",
344 "python2.7-venusian-1.0": {
344 "shortName": "bsd2",
345 "Repoze License": "http://www.repoze.org/LICENSE.txt"
345 "spdxId": "BSD-2-Clause",
346 },
346 "url": "http://spdx.org/licenses/BSD-2-Clause.html"
347 "python2.7-waitress-1.0.1": {
347 }
348 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
348 ],
349 },
349 "name": "python2.7-whoosh-2.7.4"
350 "python2.7-wcwidth-0.1.7": {
350 },
351 "MIT License": "http://spdx.org/licenses/MIT"
351 {
352 },
352 "license": [
353 "python2.7-ws4py-0.3.5": {
353 {
354 "BSD 4-clause \"Original\" or \"Old\" License": "http://spdx.org/licenses/BSD-4-Clause"
354 "fullName": "MIT License",
355 },
355 "shortName": "mit",
356 "python2.7-zope.cachedescriptors-4.0.0": {
356 "spdxId": "MIT",
357 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
357 "url": "http://spdx.org/licenses/MIT.html"
358 },
358 }
359 "python2.7-zope.deprecation-4.1.2": {
359 ],
360 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
360 "name": "python2.7-urllib3-1.21"
361 },
361 },
362 "python2.7-zope.interface-4.1.3": {
362 {
363 "Zope Public License 2.1": "http://spdx.org/licenses/ZPL-2.1"
363 "license": [
364 },
364 {
365 "xz-5.2.2": {
365 "fullName": "Apache License 2.0",
366 "GNU General Public License v2.0 or later": "http://spdx.org/licenses/GPL-2.0+",
366 "shortName": "asl20",
367 "GNU Library General Public License v2.1 or later": "http://spdx.org/licenses/LGPL-2.1+"
367 "spdxId": "Apache-2.0",
368 }
368 "url": "http://spdx.org/licenses/Apache-2.0.html"
369 } No newline at end of file
369 }
370 ],
371 "name": "python2.7-elasticsearch-dsl-2.2.0"
372 },
373 {
374 "license": [
375 {
376 "fullName": "Apache License 2.0",
377 "shortName": "asl20",
378 "spdxId": "Apache-2.0",
379 "url": "http://spdx.org/licenses/Apache-2.0.html"
380 }
381 ],
382 "name": "python2.7-elasticsearch-2.3.0"
383 },
384 {
385 "license": [
386 {
387 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
388 "shortName": "bsdOriginal",
389 "spdxId": "BSD-4-Clause",
390 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
391 },
392 {
393 "fullName": "Apache License 2.0",
394 "shortName": "asl20",
395 "spdxId": "Apache-2.0",
396 "url": "http://spdx.org/licenses/Apache-2.0.html"
397 },
398 {
399 "fullName": "Dual License"
400 }
401 ],
402 "name": "python2.7-python-dateutil-2.7.3"
403 },
404 {
405 "license": [
406 {
407 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
408 "shortName": "bsdOriginal",
409 "spdxId": "BSD-4-Clause",
410 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
411 }
412 ],
413 "name": "python2.7-markupsafe-1.0"
414 },
415 {
416 "license": [
417 {
418 "fullName": "MIT License",
419 "shortName": "mit",
420 "spdxId": "MIT",
421 "url": "http://spdx.org/licenses/MIT.html"
422 }
423 ],
424 "name": "python2.7-mako-1.0.7"
425 },
426 {
427 "license": [
428 {
429 "fullName": "MIT License",
430 "shortName": "mit",
431 "spdxId": "MIT",
432 "url": "http://spdx.org/licenses/MIT.html"
433 }
434 ],
435 "name": "python2.7-future-0.14.3"
436 },
437 {
438 "license": [
439 {
440 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
441 "shortName": "bsdOriginal",
442 "spdxId": "BSD-4-Clause",
443 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
444 }
445 ],
446 "name": "python2.7-click-6.6"
447 },
448 {
449 "license": [
450 {
451 "fullName": "MIT License",
452 "shortName": "mit",
453 "spdxId": "MIT",
454 "url": "http://spdx.org/licenses/MIT.html"
455 }
456 ],
457 "name": "python2.7-bottle-0.12.13"
458 },
459 {
460 "license": [
461 {
462 "fullName": "MIT License",
463 "shortName": "mit",
464 "spdxId": "MIT",
465 "url": "http://spdx.org/licenses/MIT.html"
466 }
467 ],
468 "name": "python2.7-cprofilev-1.0.7"
469 },
470 {
471 "license": [
472 {
473 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
474 "shortName": "bsdOriginal",
475 "spdxId": "BSD-4-Clause",
476 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
477 }
478 ],
479 "name": "python2.7-ipython-5.1.0"
480 },
481 {
482 "license": [
483 {
484 "fullName": "GNU General Public License v3 (GPLv3)"
485 },
486 {
487 "fullName": "GNU General Public License v1.0 only",
488 "shortName": "gpl1",
489 "spdxId": "GPL-1.0",
490 "url": "http://spdx.org/licenses/GPL-1.0.html"
491 }
492 ],
493 "name": "python2.7-gnureadline-6.3.8"
494 },
495 {
496 "license": [
497 {
498 "fullName": "ISC License",
499 "shortName": "isc",
500 "spdxId": "ISC",
501 "url": "http://spdx.org/licenses/ISC.html"
502 },
503 {
504 "fullName": "ISC License (ISCL)"
505 }
506 ],
507 "name": "python2.7-pexpect-4.6.0"
508 },
509 {
510 "license": [],
511 "name": "python2.7-ptyprocess-0.6.0"
512 },
513 {
514 "license": [
515 {
516 "fullName": "MIT License",
517 "shortName": "mit",
518 "spdxId": "MIT",
519 "url": "http://spdx.org/licenses/MIT.html"
520 }
521 ],
522 "name": "python2.7-pathlib2-2.3.0"
523 },
524 {
525 "license": [
526 {
527 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
528 "shortName": "bsdOriginal",
529 "spdxId": "BSD-4-Clause",
530 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
531 },
532 {
533 "fullName": "New BSD License"
534 }
535 ],
536 "name": "python2.7-scandir-1.9.0"
537 },
538 {
539 "license": [
540 {
541 "fullName": "MIT License",
542 "shortName": "mit",
543 "spdxId": "MIT",
544 "url": "http://spdx.org/licenses/MIT.html"
545 }
546 ],
547 "name": "python2.7-backports.shutil-get-terminal-size-1.0.0"
548 },
549 {
550 "license": [
551 {
552 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
553 "shortName": "bsdOriginal",
554 "spdxId": "BSD-4-Clause",
555 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
556 }
557 ],
558 "name": "python2.7-pygments-2.2.0"
559 },
560 {
561 "license": [
562 {
563 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
564 "shortName": "bsdOriginal",
565 "spdxId": "BSD-4-Clause",
566 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
567 }
568 ],
569 "name": "python2.7-prompt-toolkit-1.0.15"
570 },
571 {
572 "license": [
573 {
574 "fullName": "MIT License",
575 "shortName": "mit",
576 "spdxId": "MIT",
577 "url": "http://spdx.org/licenses/MIT.html"
578 }
579 ],
580 "name": "python2.7-wcwidth-0.1.7"
581 },
582 {
583 "license": [
584 {
585 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
586 "shortName": "bsdOriginal",
587 "spdxId": "BSD-4-Clause",
588 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
589 }
590 ],
591 "name": "python2.7-traitlets-4.3.2"
592 },
593 {
594 "license": [
595 {
596 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
597 "shortName": "bsdOriginal",
598 "spdxId": "BSD-4-Clause",
599 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
600 }
601 ],
602 "name": "python2.7-enum34-1.1.6"
603 },
604 {
605 "license": [
606 {
607 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
608 "shortName": "bsdOriginal",
609 "spdxId": "BSD-4-Clause",
610 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
611 },
612 {
613 "fullName": "new BSD License"
614 }
615 ],
616 "name": "python2.7-decorator-4.1.2"
617 },
618 {
619 "license": [
620 {
621 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
622 "shortName": "bsdOriginal",
623 "spdxId": "BSD-4-Clause",
624 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
625 }
626 ],
627 "name": "python2.7-ipython-genutils-0.2.0"
628 },
629 {
630 "license": [
631 {
632 "fullName": "Zope Public License 2.1",
633 "shortName": "zpl21",
634 "spdxId": "ZPL-2.1",
635 "url": "http://spdx.org/licenses/ZPL-2.1.html"
636 }
637 ],
638 "name": "python2.7-simplegeneric-0.8.1"
639 },
640 {
641 "license": [
642 {
643 "fullName": "MIT License",
644 "shortName": "mit",
645 "spdxId": "MIT",
646 "url": "http://spdx.org/licenses/MIT.html"
647 }
648 ],
649 "name": "python2.7-pickleshare-0.7.4"
650 },
651 {
652 "license": [
653 {
654 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
655 "shortName": "bsdOriginal",
656 "spdxId": "BSD-4-Clause",
657 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
658 }
659 ],
660 "name": "python2.7-ipdb-0.11"
661 },
662 {
663 "license": [
664 {
665 "fullName": "MIT License",
666 "shortName": "mit",
667 "spdxId": "MIT",
668 "url": "http://spdx.org/licenses/MIT.html"
669 }
670 ],
671 "name": "python2.7-gunicorn-19.9.0"
672 },
673 {
674 "license": [
675 {
676 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
677 "shortName": "bsdOriginal",
678 "spdxId": "BSD-4-Clause",
679 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
680 }
681 ],
682 "name": "python2.7-futures-3.0.2"
683 },
684 {
685 "license": [
686 {
687 "fullName": "MIT License",
688 "shortName": "mit",
689 "spdxId": "MIT",
690 "url": "http://spdx.org/licenses/MIT.html"
691 }
692 ],
693 "name": "python2.7-greenlet-0.4.13"
694 },
695 {
696 "license": [
697 {
698 "fullName": "MIT License",
699 "shortName": "mit",
700 "spdxId": "MIT",
701 "url": "http://spdx.org/licenses/MIT.html"
702 }
703 ],
704 "name": "python2.7-gevent-1.3.5"
705 },
706 {
707 "license": [
708 {
709 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
710 "shortName": "bsdOriginal",
711 "spdxId": "BSD-4-Clause",
712 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
713 }
714 ],
715 "name": "python2.7-psutil-5.4.6"
716 },
717 {
718 "license": [
719 {
720 "fullName": "MIT License",
721 "shortName": "mit",
722 "spdxId": "MIT",
723 "url": "http://spdx.org/licenses/MIT.html"
724 }
725 ],
726 "name": "python2.7-bumpversion-0.5.3"
727 },
728 {
729 "license": [
730 {
731 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
732 "shortName": "bsdOriginal",
733 "spdxId": "BSD-4-Clause",
734 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
735 }
736 ],
737 "name": "python2.7-invoke-0.13.0"
738 },
739 {
740 "license": [
741 {
742 "fullName": "MIT License",
743 "shortName": "mit",
744 "spdxId": "MIT",
745 "url": "http://spdx.org/licenses/MIT.html"
746 }
747 ],
748 "name": "python2.7-alembic-0.9.9"
749 },
750 {
751 "license": {
752 "fullName": "Apache License 2.0",
753 "shortName": "asl20",
754 "spdxId": "Apache-2.0",
755 "url": "http://spdx.org/licenses/Apache-2.0.html"
756 },
757 "name": "python2.7-python-editor-1.0.3"
758 },
759 {
760 "license": [
761 {
762 "fullName": "MIT License",
763 "shortName": "mit",
764 "spdxId": "MIT",
765 "url": "http://spdx.org/licenses/MIT.html"
766 }
767 ],
768 "name": "python2.7-sqlalchemy-1.1.18"
769 },
770 {
771 "license": [
772 {
773 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
774 "shortName": "bsdOriginal",
775 "spdxId": "BSD-4-Clause",
776 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
777 }
778 ],
779 "name": "python2.7-jupyter-client-5.0.0"
780 },
781 {
782 "license": [
783 {
784 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
785 "shortName": "bsdOriginal",
786 "spdxId": "BSD-4-Clause",
787 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
788 },
789 {
790 "fullName": "LGPL+BSD"
791 },
792 {
793 "fullName": "GNU Library or Lesser General Public License (LGPL)"
794 }
795 ],
796 "name": "python2.7-pyzmq-14.6.0"
797 },
798 {
799 "license": [
800 {
801 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
802 "shortName": "bsdOriginal",
803 "spdxId": "BSD-4-Clause",
804 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
805 }
806 ],
807 "name": "python2.7-jupyter-core-4.4.0"
808 },
809 {
810 "license": [
811 {
812 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
813 "shortName": "bsdOriginal",
814 "spdxId": "BSD-4-Clause",
815 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
816 }
817 ],
818 "name": "python2.7-nbformat-4.4.0"
819 },
820 {
821 "license": [
822 {
823 "fullName": "MIT License",
824 "shortName": "mit",
825 "spdxId": "MIT",
826 "url": "http://spdx.org/licenses/MIT.html"
827 }
828 ],
829 "name": "python2.7-jsonschema-2.6.0"
830 },
831 {
832 "license": [
833 {
834 "fullName": "Python Software Foundation License version 2",
835 "shortName": "psfl",
836 "spdxId": "Python-2.0",
837 "url": "http://spdx.org/licenses/Python-2.0.html"
838 }
839 ],
840 "name": "python2.7-functools32-3.2.3.post2"
841 },
842 {
843 "license": [
844 {
845 "fullName": "Apache License 2.0",
846 "shortName": "asl20",
847 "spdxId": "Apache-2.0",
848 "url": "http://spdx.org/licenses/Apache-2.0.html"
849 }
850 ],
851 "name": "python2.7-bleach-2.1.4"
852 },
853 {
854 "license": [
855 {
856 "fullName": "MIT License",
857 "shortName": "mit",
858 "spdxId": "MIT",
859 "url": "http://spdx.org/licenses/MIT.html"
860 }
861 ],
862 "name": "python2.7-html5lib-1.0.1"
863 },
864 {
865 "license": [
866 {
867 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
868 "shortName": "bsdOriginal",
869 "spdxId": "BSD-4-Clause",
870 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
871 }
872 ],
873 "name": "python2.7-webencodings-0.5.1"
874 },
875 {
876 "license": [
877 {
878 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
879 "shortName": "bsdOriginal",
880 "spdxId": "BSD-4-Clause",
881 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
882 }
883 ],
884 "name": "python2.7-nbconvert-5.3.1"
885 },
886 {
887 "license": [
888 {
889 "fullName": "MIT License",
890 "shortName": "mit",
891 "spdxId": "MIT",
892 "url": "http://spdx.org/licenses/MIT.html"
893 }
894 ],
895 "name": "python2.7-testpath-0.3.1"
896 },
897 {
898 "license": [
899 {
900 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
901 "shortName": "bsdOriginal",
902 "spdxId": "BSD-4-Clause",
903 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
904 }
905 ],
906 "name": "python2.7-pandocfilters-1.4.2"
907 },
908 {
909 "license": [
910 {
911 "fullName": "MIT License",
912 "shortName": "mit",
913 "spdxId": "MIT",
914 "url": "http://spdx.org/licenses/MIT.html"
915 }
916 ],
917 "name": "python2.7-entrypoints-0.2.2"
918 },
919 {
920 "license": [
921 {
922 "fullName": "MIT License",
923 "shortName": "mit",
924 "spdxId": "MIT",
925 "url": "http://spdx.org/licenses/MIT.html"
926 }
927 ],
928 "name": "python2.7-configparser-3.5.0"
929 },
930 {
931 "license": [
932 {
933 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
934 "shortName": "bsdOriginal",
935 "spdxId": "BSD-4-Clause",
936 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
937 }
938 ],
939 "name": "python2.7-jinja2-2.9.6"
940 },
941 {
942 "license": [
943 {
944 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
945 "shortName": "bsdOriginal",
946 "spdxId": "BSD-4-Clause",
947 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
948 }
949 ],
950 "name": "python2.7-mistune-0.8.3"
951 },
952 {
953 "license": [
954 {
955 "fullName": "Zope Public License 2.1",
956 "shortName": "zpl21",
957 "spdxId": "ZPL-2.1",
958 "url": "http://spdx.org/licenses/ZPL-2.1.html"
959 }
960 ],
961 "name": "python2.7-zope.interface-4.5.0"
962 },
963 {
964 "license": [
965 {
966 "fullName": "Zope Public License 2.1",
967 "shortName": "zpl21",
968 "spdxId": "ZPL-2.1",
969 "url": "http://spdx.org/licenses/ZPL-2.1.html"
970 }
971 ],
972 "name": "python2.7-zope.event-4.3.0"
973 },
974 {
975 "license": [
976 {
977 "fullName": "Zope Public License 2.1",
978 "shortName": "zpl21",
979 "spdxId": "ZPL-2.1",
980 "url": "http://spdx.org/licenses/ZPL-2.1.html"
981 }
982 ],
983 "name": "python2.7-zope.deprecation-4.3.0"
984 },
985 {
986 "license": [
987 {
988 "fullName": "Zope Public License 2.1",
989 "shortName": "zpl21",
990 "spdxId": "ZPL-2.1",
991 "url": "http://spdx.org/licenses/ZPL-2.1.html"
992 }
993 ],
994 "name": "python2.7-zope.cachedescriptors-4.3.1"
995 },
996 {
997 "license": [
998 {
999 "fullName": "PSF or ZPL"
1000 }
1001 ],
1002 "name": "python2.7-wsgiref-0.1.2"
1003 },
1004 {
1005 "license": [
1006 {
1007 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1008 "shortName": "bsdOriginal",
1009 "spdxId": "BSD-4-Clause",
1010 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1011 }
1012 ],
1013 "name": "python2.7-webhelpers-1.3"
1014 },
1015 {
1016 "license": [
1017 {
1018 "fullName": "MIT License",
1019 "shortName": "mit",
1020 "spdxId": "MIT",
1021 "url": "http://spdx.org/licenses/MIT.html"
1022 }
1023 ],
1024 "name": "python2.7-webhelpers2-2.0"
1025 },
1026 {
1027 "license": [
1028 {
1029 "fullName": "MIT License",
1030 "shortName": "mit",
1031 "spdxId": "MIT",
1032 "url": "http://spdx.org/licenses/MIT.html"
1033 }
1034 ],
1035 "name": "python2.7-weberror-0.10.3"
1036 },
1037 {
1038 "license": [
1039 {
1040 "fullName": "MIT License",
1041 "shortName": "mit",
1042 "spdxId": "MIT",
1043 "url": "http://spdx.org/licenses/MIT.html"
1044 }
1045 ],
1046 "name": "python2.7-paste-2.0.3"
1047 },
1048 {
1049 "license": [
1050 {
1051 "fullName": "MIT License",
1052 "shortName": "mit",
1053 "spdxId": "MIT",
1054 "url": "http://spdx.org/licenses/MIT.html"
1055 }
1056 ],
1057 "name": "python2.7-tempita-0.5.2"
1058 },
1059 {
1060 "license": {
1061 "fullName": "Repoze License",
1062 "url": "http://www.repoze.org/LICENSE.txt"
1063 },
1064 "name": "python2.7-venusian-1.1.0"
1065 },
1066 {
1067 "license": {
1068 "fullName": "The Unlicense",
1069 "spdxId": "Unlicense",
1070 "url": "http://unlicense.org/"
1071 },
1072 "name": "python2.7-urlobject-2.4.3"
1073 },
1074 {
1075 "license": [
1076 {
1077 "fullName": "Apache License 2.0",
1078 "shortName": "asl20",
1079 "spdxId": "Apache-2.0",
1080 "url": "http://spdx.org/licenses/Apache-2.0.html"
1081 }
1082 ],
1083 "name": "python2.7-trollius-1.0.4"
1084 },
1085 {
1086 "license": {
1087 "fullName": "Repoze License",
1088 "url": "http://www.repoze.org/LICENSE.txt"
1089 },
1090 "name": "python2.7-translationstring-1.3"
1091 },
1092 {
1093 "license": [
1094 {
1095 "fullName": "BSD-derived (http://www.repoze.org/LICENSE.txt)"
1096 }
1097 ],
1098 "name": "python2.7-supervisor-3.3.4"
1099 },
1100 {
1101 "license": [
1102 {
1103 "fullName": "BSD-derived (http://www.repoze.org/LICENSE.txt)"
1104 }
1105 ],
1106 "name": "python2.7-meld3-1.0.2"
1107 },
1108 {
1109 "license": [
1110 {
1111 "fullName": "Python Software Foundation License version 2",
1112 "shortName": "psfl",
1113 "spdxId": "Python-2.0",
1114 "url": "http://spdx.org/licenses/Python-2.0.html"
1115 }
1116 ],
1117 "name": "python2.7-subprocess32-3.5.1"
1118 },
1119 {
1120 "license": [
1121 {
1122 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1123 "shortName": "bsdOriginal",
1124 "spdxId": "BSD-4-Clause",
1125 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1126 }
1127 ],
1128 "name": "python2.7-sshpubkeys-2.2.0"
1129 },
1130 {
1131 "license": [
1132 {
1133 "fullName": "MIT License",
1134 "shortName": "mit",
1135 "spdxId": "MIT",
1136 "url": "http://spdx.org/licenses/MIT.html"
1137 }
1138 ],
1139 "name": "python2.7-ecdsa-0.13"
1140 },
1141 {
1142 "license": [
1143 {
1144 "fullName": "Public Domain",
1145 "shortName": "publicDomain"
1146 }
1147 ],
1148 "name": "python2.7-pycrypto-2.6.1"
1149 },
1150 {
1151 "license": [
1152 {
1153 "fullName": "Academic Free License (AFL)"
1154 },
1155 {
1156 "fullName": "MIT License",
1157 "shortName": "mit",
1158 "spdxId": "MIT",
1159 "url": "http://spdx.org/licenses/MIT.html"
1160 }
1161 ],
1162 "name": "python2.7-simplejson-3.11.1"
1163 },
1164 {
1165 "license": [
1166 {
1167 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1168 "shortName": "bsdOriginal",
1169 "spdxId": "BSD-4-Clause",
1170 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1171 }
1172 ],
1173 "name": "python2.7-setproctitle-1.1.10"
1174 },
1175 {
1176 "license": [
1177 {
1178 "fullName": "MIT License",
1179 "shortName": "mit",
1180 "spdxId": "MIT",
1181 "url": "http://spdx.org/licenses/MIT.html"
1182 }
1183 ],
1184 "name": "python2.7-routes-2.4.1"
1185 },
1186 {
1187 "license": {
1188 "fullName": "Repoze License",
1189 "url": "http://www.repoze.org/LICENSE.txt"
1190 },
1191 "name": "python2.7-repoze.lru-0.7"
1192 },
1193 {
1194 "license": [
1195 {
1196 "fullName": "MIT License",
1197 "shortName": "mit",
1198 "spdxId": "MIT",
1199 "url": "http://spdx.org/licenses/MIT.html"
1200 }
1201 ],
1202 "name": "python2.7-redis-2.10.6"
1203 },
1204 {
1205 "license": [
1206 {
1207 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1208 "shortName": "bsdOriginal",
1209 "spdxId": "BSD-4-Clause",
1210 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1211 }
1212 ],
1213 "name": "python2.7-py-gfm-0.1.3"
1214 },
1215 {
1216 "license": [
1217 {
1218 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1219 "shortName": "bsdOriginal",
1220 "spdxId": "BSD-4-Clause",
1221 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1222 }
1223 ],
1224 "name": "python2.7-markdown-2.6.11"
1225 },
1226 {
1227 "license": [
1228 {
1229 "fullName": "MIT License",
1230 "shortName": "mit",
1231 "spdxId": "MIT",
1232 "url": "http://spdx.org/licenses/MIT.html"
1233 }
1234 ],
1235 "name": "python2.7-tzlocal-1.5.1"
1236 },
1237 {
1238 "license": [
1239 {
1240 "fullName": "MIT License",
1241 "shortName": "mit",
1242 "spdxId": "MIT",
1243 "url": "http://spdx.org/licenses/MIT.html"
1244 }
1245 ],
1246 "name": "python2.7-pytz-2018.4"
1247 },
1248 {
1249 "license": [
1250 {
1251 "fullName": "License :: OSI Approved :: MIT License"
1252 },
1253 {
1254 "fullName": "MIT License",
1255 "shortName": "mit",
1256 "spdxId": "MIT",
1257 "url": "http://spdx.org/licenses/MIT.html"
1258 }
1259 ],
1260 "name": "python2.7-python-pam-1.8.4"
1261 },
1262 {
1263 "license": [
1264 {
1265 "fullName": "GNU General Public License v1.0 only",
1266 "shortName": "gpl1",
1267 "spdxId": "GPL-1.0",
1268 "url": "http://spdx.org/licenses/GPL-1.0.html"
1269 }
1270 ],
1271 "name": "linux-pam-1.3.0"
1272 },
1273 {
1274 "license": [
1275 {
1276 "fullName": "Python Software Foundation License version 2",
1277 "shortName": "psfl",
1278 "spdxId": "Python-2.0",
1279 "url": "http://spdx.org/licenses/Python-2.0.html"
1280 }
1281 ],
1282 "name": "python2.7-python-memcached-1.59"
1283 },
1284 {
1285 "license": [
1286 {
1287 "fullName": "Python Software Foundation License version 2",
1288 "shortName": "psfl",
1289 "spdxId": "Python-2.0",
1290 "url": "http://spdx.org/licenses/Python-2.0.html"
1291 }
1292 ],
1293 "name": "python2.7-python-ldap-3.1.0"
1294 },
1295 {
1296 "license": {
1297 "fullName": "MIT License",
1298 "shortName": "mit",
1299 "spdxId": "MIT",
1300 "url": "http://spdx.org/licenses/MIT.html"
1301 },
1302 "name": "libkrb5-1.15.2"
1303 },
1304 {
1305 "license":{
1306 "fullName": "BSD-derived (https://www.openldap.org/software/release/license.html)"
1307 },
1308 "name": "openldap-2.4.45"
1309 },
1310 {
1311 "license": [
1312 {
1313 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1314 "shortName": "bsdOriginal",
1315 "spdxId": "BSD-4-Clause",
1316 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1317 }
1318 ],
1319 "name": "python2.7-pyasn1-modules-0.2.2"
1320 },
1321 {
1322 "license": [
1323 {
1324 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1325 "shortName": "bsdOriginal",
1326 "spdxId": "BSD-4-Clause",
1327 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1328 }
1329 ],
1330 "name": "python2.7-pyasn1-0.4.4"
1331 },
1332 {
1333 "license": [
1334 {
1335 "fullName": "zlib License",
1336 "shortName": "zlib",
1337 "spdxId": "Zlib",
1338 "url": "http://spdx.org/licenses/Zlib.html"
1339 },
1340 {
1341 "fullName": "libpng License",
1342 "shortName": "libpng",
1343 "spdxId": "Libpng",
1344 "url": "http://spdx.org/licenses/Libpng.html"
1345 }
1346 ],
1347 "name": "python2.7-pysqlite-2.8.3"
1348 },
1349 {
1350 "license": {
1351 "fullName": "Repoze License",
1352 "url": "http://www.repoze.org/LICENSE.txt"
1353 },
1354 "name": "python2.7-pyramid-1.9.2"
1355 },
1356 {
1357 "license": [
1358 {
1359 "fullName": "MIT License",
1360 "shortName": "mit",
1361 "spdxId": "MIT",
1362 "url": "http://spdx.org/licenses/MIT.html"
1363 }
1364 ],
1365 "name": "python2.7-hupper-1.3"
1366 },
1367 {
1368 "license": [
1369 {
1370 "fullName": "MIT License",
1371 "shortName": "mit",
1372 "spdxId": "MIT",
1373 "url": "http://spdx.org/licenses/MIT.html"
1374 }
1375 ],
1376 "name": "python2.7-plaster-pastedeploy-0.6"
1377 },
1378 {
1379 "license": [
1380 {
1381 "fullName": "MIT License",
1382 "shortName": "mit",
1383 "spdxId": "MIT",
1384 "url": "http://spdx.org/licenses/MIT.html"
1385 }
1386 ],
1387 "name": "python2.7-plaster-1.0"
1388 },
1389 {
1390 "license": [
1391 {
1392 "fullName": "MIT License",
1393 "shortName": "mit",
1394 "spdxId": "MIT",
1395 "url": "http://spdx.org/licenses/MIT.html"
1396 }
1397 ],
1398 "name": "python2.7-pastedeploy-1.5.2"
1399 },
1400 {
1401 "license": {
1402 "fullName": "Repoze License",
1403 "url": "http://www.repoze.org/LICENSE.txt"
1404 },
1405 "name": "python2.7-pyramid-mako-1.0.2"
1406 },
1407 {
1408 "license": [
1409 {
1410 "fullName": "Repoze Public License"
1411 },
1412 {
1413 "fullName": "BSD-derived (http://www.repoze.org/LICENSE.txt)"
1414 }
1415 ],
1416 "name": "python2.7-pyramid-jinja2-2.7"
1417 },
1418 {
1419 "license": [
1420 {
1421 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1422 "shortName": "bsdOriginal",
1423 "spdxId": "BSD-4-Clause",
1424 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1425 },
1426 {
1427 "fullName": "Repoze License",
1428 "url": "http://www.repoze.org/LICENSE.txt"
1429 }
1430 ],
1431 "name": "python2.7-pyramid-debugtoolbar-4.4"
1432 },
1433 {
1434 "license": [
1435 {
1436 "fullName": "Python Software Foundation License version 2",
1437 "shortName": "psfl",
1438 "spdxId": "Python-2.0",
1439 "url": "http://spdx.org/licenses/Python-2.0.html"
1440 }
1441 ],
1442 "name": "python2.7-ipaddress-1.0.22"
1443 },
1444 {
1445 "license": {
1446 "fullName": "Repoze License",
1447 "url": "http://www.repoze.org/LICENSE.txt"
1448 },
1449 "name": "python2.7-pyramid-beaker-0.8"
1450 },
1451 {
1452 "license": [
1453 {
1454 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1455 "shortName": "bsdOriginal",
1456 "spdxId": "BSD-4-Clause",
1457 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1458 }
1459 ],
1460 "name": "python2.7-beaker-1.9.1"
1461 },
1462 {
1463 "license": [
1464 {
1465 "fullName": "MIT License",
1466 "shortName": "mit",
1467 "spdxId": "MIT",
1468 "url": "http://spdx.org/licenses/MIT.html"
1469 }
1470 ],
1471 "name": "python2.7-pyparsing-1.5.7"
1472 },
1473 {
1474 "license": [
1475 {
1476 "fullName": "Apache License 2.0",
1477 "shortName": "asl20",
1478 "spdxId": "Apache-2.0",
1479 "url": "http://spdx.org/licenses/Apache-2.0.html"
1480 }
1481 ],
1482 "name": "python2.7-pygments-markdown-lexer-0.1.0.dev39"
1483 },
1484 {
1485 "license": [
1486 {
1487 "fullName": "MIT License",
1488 "shortName": "mit",
1489 "spdxId": "MIT",
1490 "url": "http://spdx.org/licenses/MIT.html"
1491 }
1492 ],
1493 "name": "python2.7-pyflakes-0.8.1"
1494 },
1495 {
1496 "license": {
1497 "fullName": "MIT License",
1498 "shortName": "mit",
1499 "spdxId": "MIT",
1500 "url": "http://spdx.org/licenses/MIT.html"
1501 },
1502 "name": "python2.7-pycurl-7.43.0.2"
1503 },
1504 {
1505 "license": [
1506 {
1507 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1508 "shortName": "bsdOriginal",
1509 "spdxId": "BSD-4-Clause",
1510 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1511 }
1512 ],
1513 "name": "python2.7-py-bcrypt-0.4"
1514 },
1515 {
1516 "license": {
1517 "fullName": "GNU Lesser General Public License v3.0 or later",
1518 "shortName": "lgpl3Plus",
1519 "spdxId": "LGPL-3.0+",
1520 "url": "http://spdx.org/licenses/LGPL-3.0+.html"
1521 },
1522 "name": "python2.7-psycopg2-2.7.4"
1523 },
1524 {
1525 "license": {
1526 "fullName": "PostgreSQL License",
1527 "shortName": "postgresql",
1528 "spdxId": "PostgreSQL",
1529 "url": "http://spdx.org/licenses/PostgreSQL.html"
1530 },
1531 "name": "postgresql-9.6.10"
1532 },
1533 {
1534 "license": [
1535 {
1536 "fullName": "BSD-derived (http://www.repoze.org/LICENSE.txt)"
1537 }
1538 ],
1539 "name": "python2.7-peppercorn-0.5"
1540 },
1541 {
1542 "license": [
1543 {
1544 "fullName": "MIT License",
1545 "shortName": "mit",
1546 "spdxId": "MIT",
1547 "url": "http://spdx.org/licenses/MIT.html"
1548 }
1549 ],
1550 "name": "python2.7-pastescript-2.0.2"
1551 },
1552 {
1553 "license": [
1554 {
1555 "fullName": "Apache License 2.0",
1556 "shortName": "asl20",
1557 "spdxId": "Apache-2.0",
1558 "url": "http://spdx.org/licenses/Apache-2.0.html"
1559 }
1560 ],
1561 "name": "python2.7-packaging-15.2"
1562 },
1563 {
1564 "license": [
1565 {
1566 "fullName": "MIT License",
1567 "shortName": "mit",
1568 "spdxId": "MIT",
1569 "url": "http://spdx.org/licenses/MIT.html"
1570 }
1571 ],
1572 "name": "python2.7-objgraph-3.1.1"
1573 },
1574 {
1575 "license": [
1576 {
1577 "fullName": "MIT License",
1578 "shortName": "mit",
1579 "spdxId": "MIT",
1580 "url": "http://spdx.org/licenses/MIT.html"
1581 }
1582 ],
1583 "name": "python2.7-graphviz-0.9"
1584 },
1585 {
1586 "license": [
1587 {
1588 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1589 "shortName": "bsdOriginal",
1590 "spdxId": "BSD-4-Clause",
1591 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1592 }
1593 ],
1594 "name": "python2.7-pyotp-2.2.6"
1595 },
1596 {
1597 "license": [
1598 {
1599 "fullName": "MIT License",
1600 "shortName": "mit",
1601 "spdxId": "MIT",
1602 "url": "http://spdx.org/licenses/MIT.html"
1603 }
1604 ],
1605 "name": "python2.7-pymysql-0.8.1"
1606 },
1607 {
1608 "license": [
1609 {
1610 "fullName": "GNU General Public License v1.0 only",
1611 "shortName": "gpl1",
1612 "spdxId": "GPL-1.0",
1613 "url": "http://spdx.org/licenses/GPL-1.0.html"
1614 }
1615 ],
1616 "name": "python2.7-mysql-python-1.2.5"
1617 },
1618 {
1619 "license": {
1620 "fullName": "GNU Library General Public License v2.1 only",
1621 "shortName": "lgpl21",
1622 "spdxId": "LGPL-2.1",
1623 "url": "http://spdx.org/licenses/LGPL-2.1.html"
1624 },
1625 "name": "mariadb-connector-c-2.3.4"
1626 },
1627 {
1628 "license": [
1629 {
1630 "fullName": "Apache License 2.0",
1631 "shortName": "asl20",
1632 "spdxId": "Apache-2.0",
1633 "url": "http://spdx.org/licenses/Apache-2.0.html"
1634 }
1635 ],
1636 "name": "python2.7-msgpack-python-0.5.6"
1637 },
1638 {
1639 "license": [
1640 {
1641 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1642 "shortName": "bsdOriginal",
1643 "spdxId": "BSD-4-Clause",
1644 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1645 }
1646 ],
1647 "name": "python2.7-lxml-3.7.3"
1648 },
1649 {
1650 "license": [
1651 {
1652 "fullName": "MIT License",
1653 "shortName": "mit",
1654 "spdxId": "MIT",
1655 "url": "http://spdx.org/licenses/MIT.html"
1656 }
1657 ],
1658 "name": "python2.7-wheel-0.30.0"
1659 },
1660 {
1661 "license": [
1662 {
1663 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1664 "shortName": "bsdOriginal",
1665 "spdxId": "BSD-4-Clause",
1666 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1667 }
1668 ],
1669 "name": "python2.7-kombu-4.2.0"
1670 },
1671 {
1672 "license": [
1673 {
1674 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1675 "shortName": "bsdOriginal",
1676 "spdxId": "BSD-4-Clause",
1677 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1678 }
1679 ],
1680 "name": "python2.7-amqp-2.3.1"
1681 },
1682 {
1683 "license": [
1684 {
1685 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1686 "shortName": "bsdOriginal",
1687 "spdxId": "BSD-4-Clause",
1688 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1689 }
1690 ],
1691 "name": "python2.7-vine-1.1.4"
1692 },
1693 {
1694 "license": [
1695 {
1696 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1697 "shortName": "bsdOriginal",
1698 "spdxId": "BSD-4-Clause",
1699 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1700 }
1701 ],
1702 "name": "python2.7-billiard-3.5.0.3"
1703 },
1704 {
1705 "license": [
1706 {
1707 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1708 "shortName": "bsdOriginal",
1709 "spdxId": "BSD-4-Clause",
1710 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1711 }
1712 ],
1713 "name": "python2.7-itsdangerous-0.24"
1714 },
1715 {
1716 "license": [
1717 {
1718 "fullName": "MIT License",
1719 "shortName": "mit",
1720 "spdxId": "MIT",
1721 "url": "http://spdx.org/licenses/MIT.html"
1722 }
1723 ],
1724 "name": "python2.7-iso8601-0.1.11"
1725 },
1726 {
1727 "license": [
1728 {
1729 "fullName": "Zope Public License 2.1",
1730 "shortName": "zpl21",
1731 "spdxId": "ZPL-2.1",
1732 "url": "http://spdx.org/licenses/ZPL-2.1.html"
1733 }
1734 ],
1735 "name": "python2.7-infrae.cache-1.0.1"
1736 },
1737 {
1738 "license": [
1739 {
1740 "fullName": "Python Software Foundation License version 2",
1741 "shortName": "psfl",
1742 "spdxId": "Python-2.0",
1743 "url": "http://spdx.org/licenses/Python-2.0.html"
1744 }
1745 ],
1746 "name": "python2.7-formencode-1.2.4"
1747 },
1748 {
1749 "license": [
1750 {
1751 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1752 "shortName": "bsdOriginal",
1753 "spdxId": "BSD-4-Clause",
1754 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1755 }
1756 ],
1757 "name": "python2.7-dogpile.core-0.4.1"
1758 },
1759 {
1760 "license": [
1761 {
1762 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1763 "shortName": "bsdOriginal",
1764 "spdxId": "BSD-4-Clause",
1765 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1766 }
1767 ],
1768 "name": "python2.7-dogpile.cache-0.6.6"
1769 },
1770 {
1771 "license": {
1772 "fullName": "BSD 2-clause \"Simplified\" License",
1773 "shortName": "bsd2",
1774 "spdxId": "BSD-2-Clause",
1775 "url": "http://spdx.org/licenses/BSD-2-Clause.html"
1776 },
1777 "name": "python2.7-docutils-0.14"
1778 },
1779 {
1780 "license": [
1781 {
1782 "fullName": "BSD-derived (http://www.repoze.org/LICENSE.txt)"
1783 }
1784 ],
1785 "name": "python2.7-deform-2.0.5"
1786 },
1787 {
1788 "license": {
1789 "fullName": "Repoze License",
1790 "url": "http://www.repoze.org/LICENSE.txt"
1791 },
1792 "name": "python2.7-colander-1.4"
1793 },
1794 {
1795 "license": [
1796 {
1797 "fullName": "BSD-like (http://repoze.org/license.html)"
1798 }
1799 ],
1800 "name": "python2.7-chameleon-2.24"
1801 },
1802 {
1803 "license": [
1804 {
1805 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1806 "shortName": "bsdOriginal",
1807 "spdxId": "BSD-4-Clause",
1808 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1809 }
1810 ],
1811 "name": "python2.7-cssselect-1.0.3"
1812 },
1813 {
1814 "license": [
1815 {
1816 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1817 "shortName": "bsdOriginal",
1818 "spdxId": "BSD-4-Clause",
1819 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1820 }
1821 ],
1822 "name": "python2.7-configobj-5.0.6"
1823 },
1824 {
1825 "license": [
1826 {
1827 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1828 "shortName": "bsdOriginal",
1829 "spdxId": "BSD-4-Clause",
1830 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1831 }
1832 ],
1833 "name": "python2.7-channelstream-0.5.2"
1834 },
1835 {
1836 "license": [
1837 {
1838 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1839 "shortName": "bsdOriginal",
1840 "spdxId": "BSD-4-Clause",
1841 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1842 }
1843 ],
1844 "name": "python2.7-ws4py-0.5.1"
1845 },
1846 {
1847 "license": [
1848 {
1849 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1850 "shortName": "bsdOriginal",
1851 "spdxId": "BSD-4-Clause",
1852 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1853 }
1854 ],
1855 "name": "python2.7-celery-4.1.1"
1856 },
1857 {
1858 "license": [
1859 {
1860 "fullName": "BSD 4-clause \"Original\" or \"Old\" License",
1861 "shortName": "bsdOriginal",
1862 "spdxId": "BSD-4-Clause",
1863 "url": "http://spdx.org/licenses/BSD-4-Clause.html"
1864 }
1865 ],
1866 "name": "python2.7-babel-1.3"
1867 },
1868 {
1869 "license": [
1870 {
1871 "fullName": "MIT License",
1872 "shortName": "mit",
1873 "spdxId": "MIT",
1874 "url": "http://spdx.org/licenses/MIT.html"
1875 }
1876 ],
1877 "name": "python2.7-authomatic-0.1.0.post1"
1878 },
1879 {
1880 "license": [
1881 {
1882 "fullName": "MIT License",
1883 "shortName": "mit",
1884 "spdxId": "MIT",
1885 "url": "http://spdx.org/licenses/MIT.html"
1886 }
1887 ],
1888 "name": "node-grunt-cli-1.2.0"
1889 },
1890 {
1891 "license": [
1892 {
1893 "fullName": "MIT License",
1894 "shortName": "mit",
1895 "spdxId": "MIT",
1896 "url": "http://spdx.org/licenses/MIT.html"
1897 }
1898 ],
1899 "name": "node-bower-1.8.4"
1900 },
1901 {
1902 "license": [
1903 {
1904 "fullName": "MIT License",
1905 "shortName": "mit",
1906 "spdxId": "MIT",
1907 "url": "http://spdx.org/licenses/MIT.html"
1908 }
1909 ],
1910 "name": "python2.7-rhodecode-testdata-0.10.0"
1911 }
1912 ]
@@ -1,503 +1,504 b''
1
1
2 // tables.less
2 // tables.less
3 // For use in RhodeCode application tables;
3 // For use in RhodeCode application tables;
4 // see style guide documentation for guidelines.
4 // see style guide documentation for guidelines.
5
5
6 // TABLES
6 // TABLES
7
7
8 .rctable,
8 .rctable,
9 table.rctable,
9 table.rctable,
10 table.dataTable {
10 table.dataTable {
11 clear:both;
11 clear:both;
12 width: 100%;
12 width: 100%;
13 margin: 0 auto @padding;
13 margin: 0 auto @padding;
14 padding: 0;
14 padding: 0;
15 vertical-align: baseline;
15 vertical-align: baseline;
16 line-height:1.5em;
16 line-height:1.5em;
17 border: none;
17 border: none;
18 outline: none;
18 outline: none;
19 border-collapse: collapse;
19 border-collapse: collapse;
20 border-spacing: 0;
20 border-spacing: 0;
21 color: @grey2;
21 color: @grey2;
22
22
23 b {
23 b {
24 font-weight: normal;
24 font-weight: normal;
25 }
25 }
26
26
27 em {
27 em {
28 font-weight: bold;
28 font-weight: bold;
29 font-style: normal;
29 font-style: normal;
30 }
30 }
31
31
32 th,
32 th,
33 td {
33 td {
34 height: auto;
34 height: auto;
35 max-width: 20%;
35 max-width: 20%;
36 padding: .65em 1em .65em 0;
36 padding: .65em 1em .65em 0;
37 vertical-align: middle;
37 vertical-align: middle;
38 border-bottom: @border-thickness solid @grey5;
38 border-bottom: @border-thickness solid @grey5;
39 white-space: normal;
39 white-space: normal;
40
40
41 &.td-radio,
41 &.td-radio,
42 &.td-checkbox {
42 &.td-checkbox {
43 padding-right: 0;
43 padding-right: 0;
44 text-align: center;
44 text-align: center;
45
45
46 input {
46 input {
47 margin: 0 1em;
47 margin: 0 1em;
48 }
48 }
49 }
49 }
50
50
51 &.truncate-wrap {
51 &.truncate-wrap {
52 white-space: nowrap !important;
52 white-space: nowrap !important;
53 }
53 }
54
54
55 pre {
55 pre {
56 margin: 0;
56 margin: 0;
57 }
57 }
58
58
59 .show_more {
59 .show_more {
60 height: inherit;
60 height: inherit;
61 }
61 }
62 }
62 }
63
63
64 .expired td {
64 .expired td {
65 background-color: @grey7;
65 background-color: @grey7;
66 }
66 }
67
67
68 .td-radio + .td-owner {
68 .td-radio + .td-owner {
69 padding-left: 1em;
69 padding-left: 1em;
70 }
70 }
71
71
72
72
73 th {
73 th {
74 text-align: left;
74 text-align: left;
75 font-family: @text-semibold;
75 font-family: @text-semibold;
76 }
76 }
77
77
78 .hl {
78 .hl {
79 td {
79 td {
80 background-color: lighten(@alert4,25%);
80 background-color: lighten(@alert4,25%);
81 }
81 }
82 }
82 }
83
83
84 // Special Data Cell Types
84 // Special Data Cell Types
85 // See style guide for desciptions and examples.
85 // See style guide for desciptions and examples.
86
86
87 td {
87 td {
88
88
89 &.user {
89 &.user {
90 padding-left: 1em;
90 padding-left: 1em;
91 }
91 }
92
92
93 &.td-rss {
93 &.td-rss {
94 width: 20px;
94 width: 20px;
95 min-width: 0;
95 min-width: 0;
96 margin: 0;
96 margin: 0;
97 }
97 }
98
98
99 &.quick_repo_menu {
99 &.quick_repo_menu {
100 width: 15px;
100 width: 15px;
101 text-align: center;
101 text-align: center;
102
102
103 &:hover {
103 &:hover {
104 background-color: @grey5;
104 background-color: @grey5;
105 }
105 }
106 }
106 }
107
107
108 &.td-hash {
108 &.td-hash {
109 min-width: 80px;
109 min-width: 80px;
110 width: 200px;
110 width: 200px;
111
111
112 .obsolete {
112 .obsolete {
113 text-decoration: line-through;
113 text-decoration: line-through;
114 color: lighten(@grey2,25%);
114 color: lighten(@grey2,25%);
115 }
115 }
116 }
116 }
117
117
118 &.td-time {
118 &.td-time {
119 width: 160px;
119 width: 160px;
120 white-space: nowrap;
120 white-space: nowrap;
121 }
121 }
122
122
123 &.annotate{
123 &.annotate{
124 padding-right: 0;
124 padding-right: 0;
125
125
126 div.annotatediv{
126 div.annotatediv{
127 margin: 0 0.7em;
127 margin: 0 0.7em;
128 }
128 }
129 }
129 }
130
130
131 &.tags-col {
131 &.tags-col {
132 padding-right: 0;
132 padding-right: 0;
133 }
133 }
134
134
135 &.td-description {
135 &.td-description {
136 min-width: 350px;
136 min-width: 350px;
137
137
138 &.truncate, .truncate-wrap {
138 &.truncate, .truncate-wrap {
139 white-space: nowrap;
139 white-space: nowrap;
140 overflow: hidden;
140 overflow: hidden;
141 text-overflow: ellipsis;
141 text-overflow: ellipsis;
142 max-width: 450px;
142 max-width: 450px;
143 }
143 }
144 }
144 }
145
145
146 &.td-componentname {
146 &.td-componentname {
147 white-space: nowrap;
147 white-space: nowrap;
148 }
148 }
149
149
150 &.td-name {
150 &.td-name {
151
151
152 }
152 }
153
153
154 &.td-journalaction {
154 &.td-journalaction {
155 min-width: 300px;
155 min-width: 300px;
156
156
157 .journal_action_params {
157 .journal_action_params {
158 // waiting for feedback
158 // waiting for feedback
159 }
159 }
160 }
160 }
161
161
162 &.td-active {
162 &.td-active {
163 padding-left: .65em;
163 padding-left: .65em;
164 }
164 }
165
165
166 &.td-url {
166 &.td-url {
167 white-space: nowrap;
167 white-space: nowrap;
168 }
168 }
169
169
170 &.td-comments {
170 &.td-comments {
171 min-width: 3em;
171 min-width: 3em;
172 }
172 }
173
173
174 &.td-buttons {
174 &.td-buttons {
175 padding: .3em 0;
175 padding: .3em 0;
176 }
176 }
177
177
178 &.td-action {
178 &.td-action {
179 // this is for the remove/delete/edit buttons
179 // this is for the remove/delete/edit buttons
180 padding-right: 0;
180 padding-right: 0;
181 min-width: 95px;
181 min-width: 95px;
182 text-transform: capitalize;
182 text-transform: capitalize;
183
183
184 i {
184 i {
185 display: none;
185 display: none;
186 }
186 }
187 }
187 }
188
188
189 // TODO: lisa: this needs to be cleaned up with the buttons
189 // TODO: lisa: this needs to be cleaned up with the buttons
190 .grid_edit,
190 .grid_edit,
191 .grid_delete {
191 .grid_delete {
192 display: inline-block;
192 display: inline-block;
193 margin: 0 @padding/3 0 0;
193 margin: 0 @padding/3 0 0;
194 font-family: @text-light;
194 font-family: @text-light;
195
195
196 i {
196 i {
197 display: none;
197 display: none;
198 }
198 }
199 }
199 }
200
200
201 .grid_edit + .grid_delete {
201 .grid_edit + .grid_delete {
202 border-left: @border-thickness solid @grey5;
202 border-left: @border-thickness solid @grey5;
203 padding-left: @padding/2;
203 padding-left: @padding/2;
204 }
204 }
205
205
206 &.td-compare {
206 &.td-compare {
207
207
208 input {
208 input {
209 margin-right: 1em;
209 margin-right: 1em;
210 }
210 }
211
211
212 .compare-radio-button {
212 .compare-radio-button {
213 margin: 0 1em 0 0;
213 margin: 0 1em 0 0;
214 }
214 }
215
215
216
216
217 }
217 }
218
218
219 &.td-tags {
219 &.td-tags {
220 padding: .5em 1em .5em 0;
220 padding: .5em 1em .5em 0;
221 width: 140px;
221 width: 140px;
222
222
223 .tag {
223 .tag {
224 margin: 1px;
224 margin: 1px;
225 float: left;
225 float: left;
226 }
226 }
227 }
227 }
228
228
229 .icon-svn, .icon-hg, .icon-git {
229 .icon-svn, .icon-hg, .icon-git {
230 font-size: 1.4em;
230 font-size: 1.4em;
231 }
231 }
232
232
233 &.collapse_commit,
233 &.collapse_commit,
234 &.expand_commit {
234 &.expand_commit {
235 padding-right: 0;
235 padding-right: 0;
236 padding-left: 1em;
236 padding-left: 1em;
237 }
237 }
238 }
238 }
239
239
240 .perm_admin_row {
240 .perm_admin_row {
241 color: @grey4;
241 color: @grey4;
242 background-color: @grey6;
242 background-color: @grey6;
243 }
243 }
244
244
245 .noborder {
245 .noborder {
246 border: none;
246 border: none;
247
247
248 td {
248 td {
249 border: none;
249 border: none;
250 }
250 }
251 }
251 }
252 }
252 }
253 .rctable.audit-log {
253 .rctable.audit-log {
254 td {
254 td {
255 vertical-align: top;
255 vertical-align: top;
256 }
256 }
257 }
257 }
258
258
259 // TRUNCATING
259 // TRUNCATING
260 // TODO: lisaq: should this possibly be moved out of tables.less?
260 // TODO: lisaq: should this possibly be moved out of tables.less?
261 // for truncated text
261 // for truncated text
262 // used inside of table cells and in code block headers
262 // used inside of table cells and in code block headers
263 .truncate-wrap {
263 .truncate-wrap {
264 white-space: nowrap !important;
264 white-space: nowrap !important;
265
265
266 //truncated text
266 //truncated text
267 .truncate {
267 .truncate {
268 max-width: 450px;
268 max-width: 450px;
269 width: 300px;
269 width: 300px;
270 overflow: hidden;
270 overflow: hidden;
271 text-overflow: ellipsis;
271 text-overflow: ellipsis;
272 -o-text-overflow: ellipsis;
272 -o-text-overflow: ellipsis;
273 -ms-text-overflow: ellipsis;
273 -ms-text-overflow: ellipsis;
274
274
275 &.autoexpand {
275 &.autoexpand {
276 width: 120px;
276 width: 120px;
277 margin-right: 200px;
277 margin-right: 200px;
278 }
278 }
279 }
279 }
280 &:hover .truncate.autoexpand {
280 &:hover .truncate.autoexpand {
281 overflow: visible;
281 overflow: visible;
282 }
282 }
283
283
284 .tags-truncate {
284 .tags-truncate {
285 width: 150px;
285 width: 150px;
286 height: 22px;
286 height: 22px;
287 overflow: hidden;
287 overflow: hidden;
288
288
289 .tag {
289 .tag {
290 display: inline-block;
290 display: inline-block;
291 }
291 }
292
292
293 &.truncate {
293 &.truncate {
294 height: 22px;
294 height: 22px;
295 max-height:2em;
295 max-height:2em;
296 width: 140px;
296 width: 140px;
297 }
297 }
298 }
298 }
299 }
299 }
300
300
301 .apikeys_wrap {
301 .apikeys_wrap {
302 margin-bottom: @padding;
302 margin-bottom: @padding;
303
303
304 table.rctable td:first-child {
304 table.rctable td:first-child {
305 width: 340px;
305 width: 340px;
306 }
306 }
307 }
307 }
308
308
309
309
310
310
311 // SPECIAL CASES
311 // SPECIAL CASES
312
312
313 // Repository Followers
313 // Repository Followers
314 table.rctable.followers_data {
314 table.rctable.followers_data {
315 width: 75%;
315 width: 75%;
316 margin: 0;
316 margin: 0;
317 }
317 }
318
318
319 // Repository List
319 // Repository List
320 // Group Members List
320 // Group Members List
321 table.rctable.group_members,
321 table.rctable.group_members,
322 table#repo_list_table {
322 table#repo_list_table {
323 min-width: 600px;
323 min-width: 600px;
324 }
324 }
325
325
326 // Keyboard mappings
326 // Keyboard mappings
327 table.keyboard-mappings {
327 table.keyboard-mappings {
328 th {
328 th {
329 text-align: left;
329 text-align: left;
330 font-family: @text-semibold;
330 font-family: @text-semibold;
331 }
331 }
332 }
332 }
333
333
334 // Branches, Tags, and Bookmarks
334 // Branches, Tags, and Bookmarks
335 #obj_list_table.dataTable {
335 #obj_list_table.dataTable {
336 td.td-time {
336 td.td-time {
337 padding-right: 1em;
337 padding-right: 1em;
338 }
338 }
339 }
339 }
340
340
341 // User Admin
341 // User Admin
342 .rctable.useremails,
342 .rctable.useremails,
343 .rctable.account_emails {
343 .rctable.account_emails {
344 .tag,
344 .tag,
345 .btn {
345 .btn {
346 float: right;
346 float: right;
347 }
347 }
348 .btn { //to line up with tags
348 .btn { //to line up with tags
349 margin-right: 1.65em;
349 margin-right: 1.65em;
350 }
350 }
351 }
351 }
352
352
353 // User List
353 // User List
354 #user_list_table {
354 #user_list_table {
355
355
356 td.td-user {
356 td.td-user {
357 min-width: 100px;
357 min-width: 100px;
358 }
358 }
359 }
359 }
360
360
361 // Pull Request List Table
361 // Pull Request List Table
362 #pull_request_list_table.dataTable {
362 #pull_request_list_table.dataTable {
363
363
364 //TODO: lisa: This needs to be removed once the description is adjusted
364 //TODO: lisa: This needs to be removed once the description is adjusted
365 // for using an expand_commit button (see issue 765)
365 // for using an expand_commit button (see issue 765)
366 td {
366 td {
367 vertical-align: middle;
367 vertical-align: middle;
368 }
368 }
369 }
369 }
370
370
371 // Settings (no border)
371 // Settings (no border)
372 table.rctable.dl-settings {
372 table.rctable.dl-settings {
373 td {
373 td {
374 border: none;
374 border: none;
375 vertical-align: baseline;
375 }
376 }
376 }
377 }
377
378
378
379
379 // Statistics
380 // Statistics
380 table.trending_language_tbl {
381 table.trending_language_tbl {
381 width: 100%;
382 width: 100%;
382 line-height: 1em;
383 line-height: 1em;
383
384
384 td div {
385 td div {
385 overflow: visible;
386 overflow: visible;
386 }
387 }
387 }
388 }
388
389
389 .trending_language_tbl, .trending_language_tbl td {
390 .trending_language_tbl, .trending_language_tbl td {
390 border: 0;
391 border: 0;
391 margin: 0;
392 margin: 0;
392 padding: 0;
393 padding: 0;
393 background: transparent;
394 background: transparent;
394 }
395 }
395
396
396 .trending_language_tbl, .trending_language_tbl tr {
397 .trending_language_tbl, .trending_language_tbl tr {
397 border-spacing: 0 3px;
398 border-spacing: 0 3px;
398 }
399 }
399
400
400 .trending_language {
401 .trending_language {
401 position: relative;
402 position: relative;
402 width: 100%;
403 width: 100%;
403 height: 19px;
404 height: 19px;
404 overflow: hidden;
405 overflow: hidden;
405 background-color: @grey6;
406 background-color: @grey6;
406
407
407 span, b{
408 span, b{
408 position: absolute;
409 position: absolute;
409 display: block;
410 display: block;
410 height: 12px;
411 height: 12px;
411 margin-bottom: 0px;
412 margin-bottom: 0px;
412 white-space: pre;
413 white-space: pre;
413 padding: floor(@basefontsize/4);
414 padding: floor(@basefontsize/4);
414 top: 0;
415 top: 0;
415 left: 0;
416 left: 0;
416 }
417 }
417
418
418 span{
419 span{
419 color: @text-color;
420 color: @text-color;
420 z-index: 0;
421 z-index: 0;
421 min-width: 20px;
422 min-width: 20px;
422 }
423 }
423
424
424 b {
425 b {
425 z-index: 1;
426 z-index: 1;
426 overflow: hidden;
427 overflow: hidden;
427 background-color: @rcblue;
428 background-color: @rcblue;
428 color: #FFF;
429 color: #FFF;
429 text-decoration: none;
430 text-decoration: none;
430 }
431 }
431
432
432 }
433 }
433
434
434 // Changesets
435 // Changesets
435 #changesets.rctable {
436 #changesets.rctable {
436
437
437 // td must be fixed height for graph
438 // td must be fixed height for graph
438 td {
439 td {
439 height: 32px;
440 height: 32px;
440 padding: 0 1em 0 0;
441 padding: 0 1em 0 0;
441 vertical-align: middle;
442 vertical-align: middle;
442 white-space: nowrap;
443 white-space: nowrap;
443
444
444 &.td-description {
445 &.td-description {
445 white-space: normal;
446 white-space: normal;
446 }
447 }
447
448
448 &.expand_commit {
449 &.expand_commit {
449 padding-right: 0;
450 padding-right: 0;
450 }
451 }
451 }
452 }
452 }
453 }
453
454
454 // Compare
455 // Compare
455 table.compare_view_commits {
456 table.compare_view_commits {
456 margin-top: @space;
457 margin-top: @space;
457
458
458 td.td-time {
459 td.td-time {
459 padding-left: .5em;
460 padding-left: .5em;
460 }
461 }
461
462
462 // special case to not show hover actions on hidden indicator
463 // special case to not show hover actions on hidden indicator
463 tr.compare_select_hidden:hover {
464 tr.compare_select_hidden:hover {
464 cursor: inherit;
465 cursor: inherit;
465
466
466 td {
467 td {
467 background-color: inherit;
468 background-color: inherit;
468 }
469 }
469 }
470 }
470
471
471 tr:hover {
472 tr:hover {
472 cursor: pointer;
473 cursor: pointer;
473
474
474 td {
475 td {
475 background-color: lighten(@alert4,25%);
476 background-color: lighten(@alert4,25%);
476 }
477 }
477 }
478 }
478
479
479
480
480 }
481 }
481
482
482 .file_history {
483 .file_history {
483 td.td-actions {
484 td.td-actions {
484 text-align: right;
485 text-align: right;
485 }
486 }
486 }
487 }
487
488
488
489
489 // Gist List
490 // Gist List
490 #gist_list_table {
491 #gist_list_table {
491 td {
492 td {
492 vertical-align: middle;
493 vertical-align: middle;
493
494
494 div{
495 div{
495 display: inline-block;
496 display: inline-block;
496 vertical-align: middle;
497 vertical-align: middle;
497 }
498 }
498
499
499 img{
500 img{
500 vertical-align: middle;
501 vertical-align: middle;
501 }
502 }
502 }
503 }
503 }
504 }
@@ -1,33 +1,46 b''
1 <%def name="show_license(license_data)">
2 % if isinstance(license_data, dict):
3 <a href="${license_data.get('url', "#noUrl")}">${license_data.get('spdxId') or license_data.get('fullName')}</a>
4 % else:
5 ${license_data}
6 % endif
7 </%def>
8
1 <div class="panel panel-default">
9 <div class="panel panel-default">
2 <div class="panel-heading">
10 <div class="panel-heading">
3 <h3 class="panel-title">${_('Licenses of Third Party Packages')}</h3>
11 <h3 class="panel-title">${_('Licenses of Third Party Packages')}</h3>
4 </div>
12 </div>
5 <div class="panel-body">
13 <div class="panel-body">
6 <p>
14 <p>
7 RhodeCode Enterprise uses various third party packages, many of them
15 RhodeCode Enterprise uses various third party packages, many of them
8 provided by the open source community.
16 provided by the open source community.
9 </p>
17 </p>
10
11 % if c.opensource_licenses:
18 % if c.opensource_licenses:
12 <table class="rctable dl-settings">
19 <table class="rctable dl-settings">
13 <thead>
20 <thead>
14 <th>Product</th>
21 <th>Product</th>
15 <th>License</th>
22 <th>License</th>
16 </thead>
23 </thead>
17 %for product, licenses in c.opensource_licenses.items():
24 % for lib in c.opensource_licenses:
18 <tr>
25 <tr>
19 <td>${product}</td>
26 <td>${lib["name"]}</td>
20 <td>
27 <td>
21 ${h.literal(', '.join([
28 <ol>
22 '<a href="%(link)s" title="%(name)s">%(name)s</a>' % {'link':link, 'name':name}
29 % if isinstance(lib["license"], list):
23 if link else name
30 % for license_data in lib["license"]:
24 for name,link in licenses.items()]))}
31 <li>${show_license(license_data)}</li>
25 </td>
32 % endfor
26 </tr>
33 % else:
27 %endfor
34 <% license_data = lib["license"] %>
35 <li>${show_license(license_data)}</li>
36 % endif
37 </ol>
38 </td>
39 </tr>
40 % endfor
28 </table>
41 </table>
29 % endif
42 % endif
30 </div>
43 </div>
31 </div>
44 </div>
32
45
33
46
@@ -1,445 +1,449 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
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 Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import json
21 import json
22 import multiprocessing
22 import multiprocessing
23 import os
23 import os
24
24
25 import mock
25 import mock
26 import py
26 import py
27 import pytest
27 import pytest
28
28
29 from rhodecode.lib import caching_query
29 from rhodecode.lib import caching_query
30 from rhodecode.lib import utils
30 from rhodecode.lib import utils
31 from rhodecode.lib.utils2 import md5
31 from rhodecode.lib.utils2 import md5
32 from rhodecode.model import settings
32 from rhodecode.model import settings
33 from rhodecode.model import db
33 from rhodecode.model import db
34 from rhodecode.model import meta
34 from rhodecode.model import meta
35 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.repo_group import RepoGroupModel
36 from rhodecode.model.repo_group import RepoGroupModel
37 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.settings import UiSetting, SettingsModel
38 from rhodecode.model.settings import UiSetting, SettingsModel
39 from rhodecode.tests.fixture import Fixture
39 from rhodecode.tests.fixture import Fixture
40
40
41
41
42 fixture = Fixture()
42 fixture = Fixture()
43
43
44
44
45 def extract_hooks(config):
45 def extract_hooks(config):
46 """Return a dictionary with the hook entries of the given config."""
46 """Return a dictionary with the hook entries of the given config."""
47 hooks = {}
47 hooks = {}
48 config_items = config.serialize()
48 config_items = config.serialize()
49 for section, name, value in config_items:
49 for section, name, value in config_items:
50 if section != 'hooks':
50 if section != 'hooks':
51 continue
51 continue
52 hooks[name] = value
52 hooks[name] = value
53
53
54 return hooks
54 return hooks
55
55
56
56
57 def disable_hooks(request, hooks):
57 def disable_hooks(request, hooks):
58 """Disables the given hooks from the UI settings."""
58 """Disables the given hooks from the UI settings."""
59 session = meta.Session()
59 session = meta.Session()
60
60
61 model = SettingsModel()
61 model = SettingsModel()
62 for hook_key in hooks:
62 for hook_key in hooks:
63 sett = model.get_ui_by_key(hook_key)
63 sett = model.get_ui_by_key(hook_key)
64 sett.ui_active = False
64 sett.ui_active = False
65 session.add(sett)
65 session.add(sett)
66
66
67 # Invalidate cache
67 # Invalidate cache
68 ui_settings = session.query(db.RhodeCodeUi).options(
68 ui_settings = session.query(db.RhodeCodeUi).options(
69 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
69 caching_query.FromCache('sql_cache_short', 'get_hg_ui_settings'))
70 ui_settings.invalidate()
70 ui_settings.invalidate()
71
71
72 ui_settings = session.query(db.RhodeCodeUi).options(
72 ui_settings = session.query(db.RhodeCodeUi).options(
73 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
73 caching_query.FromCache('sql_cache_short', 'get_hook_settings'))
74 ui_settings.invalidate()
74 ui_settings.invalidate()
75
75
76 @request.addfinalizer
76 @request.addfinalizer
77 def rollback():
77 def rollback():
78 session.rollback()
78 session.rollback()
79
79
80
80
81 HOOK_PRE_PUSH = db.RhodeCodeUi.HOOK_PRE_PUSH
81 HOOK_PRE_PUSH = db.RhodeCodeUi.HOOK_PRE_PUSH
82 HOOK_PRETX_PUSH = db.RhodeCodeUi.HOOK_PRETX_PUSH
82 HOOK_PRETX_PUSH = db.RhodeCodeUi.HOOK_PRETX_PUSH
83 HOOK_PUSH = db.RhodeCodeUi.HOOK_PUSH
83 HOOK_PUSH = db.RhodeCodeUi.HOOK_PUSH
84 HOOK_PRE_PULL = db.RhodeCodeUi.HOOK_PRE_PULL
84 HOOK_PRE_PULL = db.RhodeCodeUi.HOOK_PRE_PULL
85 HOOK_PULL = db.RhodeCodeUi.HOOK_PULL
85 HOOK_PULL = db.RhodeCodeUi.HOOK_PULL
86 HOOK_REPO_SIZE = db.RhodeCodeUi.HOOK_REPO_SIZE
86 HOOK_REPO_SIZE = db.RhodeCodeUi.HOOK_REPO_SIZE
87 HOOK_PUSH_KEY = db.RhodeCodeUi.HOOK_PUSH_KEY
87 HOOK_PUSH_KEY = db.RhodeCodeUi.HOOK_PUSH_KEY
88
88
89 HG_HOOKS = frozenset(
89 HG_HOOKS = frozenset(
90 (HOOK_PRE_PULL, HOOK_PULL, HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH,
90 (HOOK_PRE_PULL, HOOK_PULL, HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH,
91 HOOK_REPO_SIZE, HOOK_PUSH_KEY))
91 HOOK_REPO_SIZE, HOOK_PUSH_KEY))
92
92
93
93
94 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
94 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
95 ([], HG_HOOKS),
95 ([], HG_HOOKS),
96 (HG_HOOKS, []),
96 (HG_HOOKS, []),
97
97
98 ([HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_REPO_SIZE, HOOK_PUSH_KEY], [HOOK_PRE_PULL, HOOK_PULL, HOOK_PUSH]),
98 ([HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_REPO_SIZE, HOOK_PUSH_KEY], [HOOK_PRE_PULL, HOOK_PULL, HOOK_PUSH]),
99
99
100 # When a pull/push hook is disabled, its pre-pull/push counterpart should
100 # When a pull/push hook is disabled, its pre-pull/push counterpart should
101 # be disabled too.
101 # be disabled too.
102 ([HOOK_PUSH], [HOOK_PRE_PULL, HOOK_PULL, HOOK_REPO_SIZE]),
102 ([HOOK_PUSH], [HOOK_PRE_PULL, HOOK_PULL, HOOK_REPO_SIZE]),
103 ([HOOK_PULL], [HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH, HOOK_REPO_SIZE,
103 ([HOOK_PULL], [HOOK_PRE_PUSH, HOOK_PRETX_PUSH, HOOK_PUSH, HOOK_REPO_SIZE,
104 HOOK_PUSH_KEY]),
104 HOOK_PUSH_KEY]),
105 ])
105 ])
106 def test_make_db_config_hg_hooks(baseapp, request, disabled_hooks,
106 def test_make_db_config_hg_hooks(baseapp, request, disabled_hooks,
107 expected_hooks):
107 expected_hooks):
108 disable_hooks(request, disabled_hooks)
108 disable_hooks(request, disabled_hooks)
109
109
110 config = utils.make_db_config()
110 config = utils.make_db_config()
111 hooks = extract_hooks(config)
111 hooks = extract_hooks(config)
112
112
113 assert set(hooks.iterkeys()).intersection(HG_HOOKS) == set(expected_hooks)
113 assert set(hooks.iterkeys()).intersection(HG_HOOKS) == set(expected_hooks)
114
114
115
115
116 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
116 @pytest.mark.parametrize('disabled_hooks,expected_hooks', [
117 ([], ['pull', 'push']),
117 ([], ['pull', 'push']),
118 ([HOOK_PUSH], ['pull']),
118 ([HOOK_PUSH], ['pull']),
119 ([HOOK_PULL], ['push']),
119 ([HOOK_PULL], ['push']),
120 ([HOOK_PULL, HOOK_PUSH], []),
120 ([HOOK_PULL, HOOK_PUSH], []),
121 ])
121 ])
122 def test_get_enabled_hook_classes(disabled_hooks, expected_hooks):
122 def test_get_enabled_hook_classes(disabled_hooks, expected_hooks):
123 hook_keys = (HOOK_PUSH, HOOK_PULL)
123 hook_keys = (HOOK_PUSH, HOOK_PULL)
124 ui_settings = [
124 ui_settings = [
125 ('hooks', key, 'some value', key not in disabled_hooks)
125 ('hooks', key, 'some value', key not in disabled_hooks)
126 for key in hook_keys]
126 for key in hook_keys]
127
127
128 result = utils.get_enabled_hook_classes(ui_settings)
128 result = utils.get_enabled_hook_classes(ui_settings)
129 assert sorted(result) == expected_hooks
129 assert sorted(result) == expected_hooks
130
130
131
131
132 def test_get_filesystem_repos_finds_repos(tmpdir, baseapp):
132 def test_get_filesystem_repos_finds_repos(tmpdir, baseapp):
133 _stub_git_repo(tmpdir.ensure('repo', dir=True))
133 _stub_git_repo(tmpdir.ensure('repo', dir=True))
134 repos = list(utils.get_filesystem_repos(str(tmpdir)))
134 repos = list(utils.get_filesystem_repos(str(tmpdir)))
135 assert repos == [('repo', ('git', tmpdir.join('repo')))]
135 assert repos == [('repo', ('git', tmpdir.join('repo')))]
136
136
137
137
138 def test_get_filesystem_repos_skips_directories(tmpdir, baseapp):
138 def test_get_filesystem_repos_skips_directories(tmpdir, baseapp):
139 tmpdir.ensure('not-a-repo', dir=True)
139 tmpdir.ensure('not-a-repo', dir=True)
140 repos = list(utils.get_filesystem_repos(str(tmpdir)))
140 repos = list(utils.get_filesystem_repos(str(tmpdir)))
141 assert repos == []
141 assert repos == []
142
142
143
143
144 def test_get_filesystem_repos_skips_directories_with_repos(tmpdir, baseapp):
144 def test_get_filesystem_repos_skips_directories_with_repos(tmpdir, baseapp):
145 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
145 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
146 repos = list(utils.get_filesystem_repos(str(tmpdir)))
146 repos = list(utils.get_filesystem_repos(str(tmpdir)))
147 assert repos == []
147 assert repos == []
148
148
149
149
150 def test_get_filesystem_repos_finds_repos_in_subdirectories(tmpdir, baseapp):
150 def test_get_filesystem_repos_finds_repos_in_subdirectories(tmpdir, baseapp):
151 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
151 _stub_git_repo(tmpdir.ensure('subdir/repo', dir=True))
152 repos = list(utils.get_filesystem_repos(str(tmpdir), recursive=True))
152 repos = list(utils.get_filesystem_repos(str(tmpdir), recursive=True))
153 assert repos == [('subdir/repo', ('git', tmpdir.join('subdir', 'repo')))]
153 assert repos == [('subdir/repo', ('git', tmpdir.join('subdir', 'repo')))]
154
154
155
155
156 def test_get_filesystem_repos_skips_names_starting_with_dot(tmpdir):
156 def test_get_filesystem_repos_skips_names_starting_with_dot(tmpdir):
157 _stub_git_repo(tmpdir.ensure('.repo', dir=True))
157 _stub_git_repo(tmpdir.ensure('.repo', dir=True))
158 repos = list(utils.get_filesystem_repos(str(tmpdir)))
158 repos = list(utils.get_filesystem_repos(str(tmpdir)))
159 assert repos == []
159 assert repos == []
160
160
161
161
162 def test_get_filesystem_repos_skips_files(tmpdir):
162 def test_get_filesystem_repos_skips_files(tmpdir):
163 tmpdir.ensure('test-file')
163 tmpdir.ensure('test-file')
164 repos = list(utils.get_filesystem_repos(str(tmpdir)))
164 repos = list(utils.get_filesystem_repos(str(tmpdir)))
165 assert repos == []
165 assert repos == []
166
166
167
167
168 def test_get_filesystem_repos_skips_removed_repositories(tmpdir):
168 def test_get_filesystem_repos_skips_removed_repositories(tmpdir):
169 removed_repo_name = 'rm__00000000_000000_000000__.stub'
169 removed_repo_name = 'rm__00000000_000000_000000__.stub'
170 assert utils.REMOVED_REPO_PAT.match(removed_repo_name)
170 assert utils.REMOVED_REPO_PAT.match(removed_repo_name)
171 _stub_git_repo(tmpdir.ensure(removed_repo_name, dir=True))
171 _stub_git_repo(tmpdir.ensure(removed_repo_name, dir=True))
172 repos = list(utils.get_filesystem_repos(str(tmpdir)))
172 repos = list(utils.get_filesystem_repos(str(tmpdir)))
173 assert repos == []
173 assert repos == []
174
174
175
175
176 def _stub_git_repo(repo_path):
176 def _stub_git_repo(repo_path):
177 """
177 """
178 Make `repo_path` look like a Git repository.
178 Make `repo_path` look like a Git repository.
179 """
179 """
180 repo_path.ensure('.git', dir=True)
180 repo_path.ensure('.git', dir=True)
181
181
182
182
183 @pytest.mark.parametrize('str_class', [str, unicode], ids=['str', 'unicode'])
183 @pytest.mark.parametrize('str_class', [str, unicode], ids=['str', 'unicode'])
184 def test_get_dirpaths_returns_all_paths(tmpdir, str_class):
184 def test_get_dirpaths_returns_all_paths(tmpdir, str_class):
185 tmpdir.ensure('test-file')
185 tmpdir.ensure('test-file')
186 dirpaths = utils._get_dirpaths(str_class(tmpdir))
186 dirpaths = utils._get_dirpaths(str_class(tmpdir))
187 assert dirpaths == ['test-file']
187 assert dirpaths == ['test-file']
188
188
189
189
190 def test_get_dirpaths_returns_all_paths_bytes(
190 def test_get_dirpaths_returns_all_paths_bytes(
191 tmpdir, platform_encodes_filenames):
191 tmpdir, platform_encodes_filenames):
192 if platform_encodes_filenames:
192 if platform_encodes_filenames:
193 pytest.skip("This platform seems to encode filenames.")
193 pytest.skip("This platform seems to encode filenames.")
194 tmpdir.ensure('repo-a-umlaut-\xe4')
194 tmpdir.ensure('repo-a-umlaut-\xe4')
195 dirpaths = utils._get_dirpaths(str(tmpdir))
195 dirpaths = utils._get_dirpaths(str(tmpdir))
196 assert dirpaths == ['repo-a-umlaut-\xe4']
196 assert dirpaths == ['repo-a-umlaut-\xe4']
197
197
198
198
199 def test_get_dirpaths_skips_paths_it_cannot_decode(
199 def test_get_dirpaths_skips_paths_it_cannot_decode(
200 tmpdir, platform_encodes_filenames):
200 tmpdir, platform_encodes_filenames):
201 if platform_encodes_filenames:
201 if platform_encodes_filenames:
202 pytest.skip("This platform seems to encode filenames.")
202 pytest.skip("This platform seems to encode filenames.")
203 path_with_latin1 = 'repo-a-umlaut-\xe4'
203 path_with_latin1 = 'repo-a-umlaut-\xe4'
204 tmpdir.ensure(path_with_latin1)
204 tmpdir.ensure(path_with_latin1)
205 dirpaths = utils._get_dirpaths(unicode(tmpdir))
205 dirpaths = utils._get_dirpaths(unicode(tmpdir))
206 assert dirpaths == []
206 assert dirpaths == []
207
207
208
208
209 @pytest.fixture(scope='session')
209 @pytest.fixture(scope='session')
210 def platform_encodes_filenames():
210 def platform_encodes_filenames():
211 """
211 """
212 Boolean indicator if the current platform changes filename encodings.
212 Boolean indicator if the current platform changes filename encodings.
213 """
213 """
214 path_with_latin1 = 'repo-a-umlaut-\xe4'
214 path_with_latin1 = 'repo-a-umlaut-\xe4'
215 tmpdir = py.path.local.mkdtemp()
215 tmpdir = py.path.local.mkdtemp()
216 tmpdir.ensure(path_with_latin1)
216 tmpdir.ensure(path_with_latin1)
217 read_path = tmpdir.listdir()[0].basename
217 read_path = tmpdir.listdir()[0].basename
218 tmpdir.remove()
218 tmpdir.remove()
219 return path_with_latin1 != read_path
219 return path_with_latin1 != read_path
220
220
221
221
222
222
223
223
224 def test_repo2db_mapper_groups(repo_groups):
224 def test_repo2db_mapper_groups(repo_groups):
225 session = meta.Session()
225 session = meta.Session()
226 zombie_group, parent_group, child_group = repo_groups
226 zombie_group, parent_group, child_group = repo_groups
227 zombie_path = os.path.join(
227 zombie_path = os.path.join(
228 RepoGroupModel().repos_path, zombie_group.full_path)
228 RepoGroupModel().repos_path, zombie_group.full_path)
229 os.rmdir(zombie_path)
229 os.rmdir(zombie_path)
230
230
231 # Avoid removing test repos when calling repo2db_mapper
231 # Avoid removing test repos when calling repo2db_mapper
232 repo_list = {
232 repo_list = {
233 repo.repo_name: 'test' for repo in session.query(db.Repository).all()
233 repo.repo_name: 'test' for repo in session.query(db.Repository).all()
234 }
234 }
235 utils.repo2db_mapper(repo_list, remove_obsolete=True)
235 utils.repo2db_mapper(repo_list, remove_obsolete=True)
236
236
237 groups_in_db = session.query(db.RepoGroup).all()
237 groups_in_db = session.query(db.RepoGroup).all()
238 assert child_group in groups_in_db
238 assert child_group in groups_in_db
239 assert parent_group in groups_in_db
239 assert parent_group in groups_in_db
240 assert zombie_path not in groups_in_db
240 assert zombie_path not in groups_in_db
241
241
242
242
243 def test_repo2db_mapper_enables_largefiles(backend):
243 def test_repo2db_mapper_enables_largefiles(backend):
244 repo = backend.create_repo()
244 repo = backend.create_repo()
245 repo_list = {repo.repo_name: 'test'}
245 repo_list = {repo.repo_name: 'test'}
246 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm_mock:
246 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm_mock:
247 utils.repo2db_mapper(repo_list, remove_obsolete=False)
247 utils.repo2db_mapper(repo_list, remove_obsolete=False)
248 _, kwargs = scm_mock.call_args
248 _, kwargs = scm_mock.call_args
249 assert kwargs['config'].get('extensions', 'largefiles') == ''
249 assert kwargs['config'].get('extensions', 'largefiles') == ''
250
250
251
251
252 @pytest.mark.backends("git", "svn")
252 @pytest.mark.backends("git", "svn")
253 def test_repo2db_mapper_installs_hooks_for_repos_in_db(backend):
253 def test_repo2db_mapper_installs_hooks_for_repos_in_db(backend):
254 repo = backend.create_repo()
254 repo = backend.create_repo()
255 repo_list = {repo.repo_name: 'test'}
255 repo_list = {repo.repo_name: 'test'}
256 utils.repo2db_mapper(repo_list, remove_obsolete=False)
256 utils.repo2db_mapper(repo_list, remove_obsolete=False)
257
257
258
258
259 @pytest.mark.backends("git", "svn")
259 @pytest.mark.backends("git", "svn")
260 def test_repo2db_mapper_installs_hooks_for_newly_added_repos(backend):
260 def test_repo2db_mapper_installs_hooks_for_newly_added_repos(backend):
261 repo = backend.create_repo()
261 repo = backend.create_repo()
262 RepoModel().delete(repo, fs_remove=False)
262 RepoModel().delete(repo, fs_remove=False)
263 meta.Session().commit()
263 meta.Session().commit()
264 repo_list = {repo.repo_name: repo.scm_instance()}
264 repo_list = {repo.repo_name: repo.scm_instance()}
265 utils.repo2db_mapper(repo_list, remove_obsolete=False)
265 utils.repo2db_mapper(repo_list, remove_obsolete=False)
266
266
267
267
268 class TestPasswordChanged(object):
268 class TestPasswordChanged(object):
269 def setup(self):
269 def setup(self):
270 self.session = {
270 self.session = {
271 'rhodecode_user': {
271 'rhodecode_user': {
272 'password': '0cc175b9c0f1b6a831c399e269772661'
272 'password': '0cc175b9c0f1b6a831c399e269772661'
273 }
273 }
274 }
274 }
275 self.auth_user = mock.Mock()
275 self.auth_user = mock.Mock()
276 self.auth_user.userame = 'test'
276 self.auth_user.userame = 'test'
277 self.auth_user.password = 'abc123'
277 self.auth_user.password = 'abc123'
278
278
279 def test_returns_false_for_default_user(self):
279 def test_returns_false_for_default_user(self):
280 self.auth_user.username = db.User.DEFAULT_USER
280 self.auth_user.username = db.User.DEFAULT_USER
281 result = utils.password_changed(self.auth_user, self.session)
281 result = utils.password_changed(self.auth_user, self.session)
282 assert result is False
282 assert result is False
283
283
284 def test_returns_false_if_password_was_not_changed(self):
284 def test_returns_false_if_password_was_not_changed(self):
285 self.session['rhodecode_user']['password'] = md5(
285 self.session['rhodecode_user']['password'] = md5(
286 self.auth_user.password)
286 self.auth_user.password)
287 result = utils.password_changed(self.auth_user, self.session)
287 result = utils.password_changed(self.auth_user, self.session)
288 assert result is False
288 assert result is False
289
289
290 def test_returns_true_if_password_was_changed(self):
290 def test_returns_true_if_password_was_changed(self):
291 result = utils.password_changed(self.auth_user, self.session)
291 result = utils.password_changed(self.auth_user, self.session)
292 assert result is True
292 assert result is True
293
293
294 def test_returns_true_if_auth_user_password_is_empty(self):
294 def test_returns_true_if_auth_user_password_is_empty(self):
295 self.auth_user.password = None
295 self.auth_user.password = None
296 result = utils.password_changed(self.auth_user, self.session)
296 result = utils.password_changed(self.auth_user, self.session)
297 assert result is True
297 assert result is True
298
298
299 def test_returns_true_if_session_password_is_empty(self):
299 def test_returns_true_if_session_password_is_empty(self):
300 self.session['rhodecode_user'].pop('password')
300 self.session['rhodecode_user'].pop('password')
301 result = utils.password_changed(self.auth_user, self.session)
301 result = utils.password_changed(self.auth_user, self.session)
302 assert result is True
302 assert result is True
303
303
304
304
305 class TestReadOpensourceLicenses(object):
305 class TestReadOpenSourceLicenses(object):
306 def test_success(self):
306 def test_success(self):
307 utils._license_cache = None
307 utils._license_cache = None
308 json_data = '''
308 json_data = '''
309 {
309 {
310 "python2.7-pytest-2.7.1": {"UNKNOWN": null},
310 "python2.7-pytest-2.7.1": {"UNKNOWN": null},
311 "python2.7-Markdown-2.6.2": {
311 "python2.7-Markdown-2.6.2": {
312 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
312 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
313 }
313 }
314 }
314 }
315 '''
315 '''
316 resource_string_patch = mock.patch.object(
316 resource_string_patch = mock.patch.object(
317 utils.pkg_resources, 'resource_string', return_value=json_data)
317 utils.pkg_resources, 'resource_string', return_value=json_data)
318 with resource_string_patch:
318 with resource_string_patch:
319 result = utils.read_opensource_licenses()
319 result = utils.read_opensource_licenses()
320 assert result == json.loads(json_data)
320 assert result == json.loads(json_data)
321
321
322 def test_caching(self):
322 def test_caching(self):
323 utils._license_cache = {
323 utils._license_cache = {
324 "python2.7-pytest-2.7.1": {
324 "python2.7-pytest-2.7.1": {
325 "UNKNOWN": None
325 "UNKNOWN": None
326 },
326 },
327 "python2.7-Markdown-2.6.2": {
327 "python2.7-Markdown-2.6.2": {
328 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
328 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
329 }
329 }
330 }
330 }
331 resource_patch = mock.patch.object(
331 resource_patch = mock.patch.object(
332 utils.pkg_resources, 'resource_string', side_effect=Exception)
332 utils.pkg_resources, 'resource_string', side_effect=Exception)
333 json_patch = mock.patch.object(
333 json_patch = mock.patch.object(
334 utils.json, 'loads', side_effect=Exception)
334 utils.json, 'loads', side_effect=Exception)
335
335
336 with resource_patch as resource_mock, json_patch as json_mock:
336 with resource_patch as resource_mock, json_patch as json_mock:
337 result = utils.read_opensource_licenses()
337 result = utils.read_opensource_licenses()
338
338
339 assert resource_mock.call_count == 0
339 assert resource_mock.call_count == 0
340 assert json_mock.call_count == 0
340 assert json_mock.call_count == 0
341 assert result == utils._license_cache
341 assert result == utils._license_cache
342
342
343 def test_licenses_file_contains_no_unknown_licenses(self):
343 def test_licenses_file_contains_no_unknown_licenses(self):
344 utils._license_cache = None
344 utils._license_cache = None
345 result = utils.read_opensource_licenses()
345 result = utils.read_opensource_licenses()
346 license_names = []
346
347 for licenses in result.values():
347 for license_data in result:
348 license_names.extend(licenses.keys())
348 if isinstance(license_data["license"], list):
349 assert 'UNKNOWN' not in license_names
349 for lic_data in license_data["license"]:
350 assert 'UNKNOWN' not in lic_data["fullName"]
351 else:
352 full_name = license_data.get("fullName") or license_data
353 assert 'UNKNOWN' not in full_name
350
354
351
355
352 class TestMakeDbConfig(object):
356 class TestMakeDbConfig(object):
353 def test_data_from_config_data_from_db_returned(self):
357 def test_data_from_config_data_from_db_returned(self):
354 test_data = [
358 test_data = [
355 ('section1', 'option1', 'value1'),
359 ('section1', 'option1', 'value1'),
356 ('section2', 'option2', 'value2'),
360 ('section2', 'option2', 'value2'),
357 ('section3', 'option3', 'value3'),
361 ('section3', 'option3', 'value3'),
358 ]
362 ]
359 with mock.patch.object(utils, 'config_data_from_db') as config_mock:
363 with mock.patch.object(utils, 'config_data_from_db') as config_mock:
360 config_mock.return_value = test_data
364 config_mock.return_value = test_data
361 kwargs = {'clear_session': False, 'repo': 'test_repo'}
365 kwargs = {'clear_session': False, 'repo': 'test_repo'}
362 result = utils.make_db_config(**kwargs)
366 result = utils.make_db_config(**kwargs)
363 config_mock.assert_called_once_with(**kwargs)
367 config_mock.assert_called_once_with(**kwargs)
364 for section, option, expected_value in test_data:
368 for section, option, expected_value in test_data:
365 value = result.get(section, option)
369 value = result.get(section, option)
366 assert value == expected_value
370 assert value == expected_value
367
371
368
372
369 class TestConfigDataFromDb(object):
373 class TestConfigDataFromDb(object):
370 def test_config_data_from_db_returns_active_settings(self):
374 def test_config_data_from_db_returns_active_settings(self):
371 test_data = [
375 test_data = [
372 UiSetting('section1', 'option1', 'value1', True),
376 UiSetting('section1', 'option1', 'value1', True),
373 UiSetting('section2', 'option2', 'value2', True),
377 UiSetting('section2', 'option2', 'value2', True),
374 UiSetting('section3', 'option3', 'value3', False),
378 UiSetting('section3', 'option3', 'value3', False),
375 ]
379 ]
376 repo_name = 'test_repo'
380 repo_name = 'test_repo'
377
381
378 model_patch = mock.patch.object(settings, 'VcsSettingsModel')
382 model_patch = mock.patch.object(settings, 'VcsSettingsModel')
379 hooks_patch = mock.patch.object(
383 hooks_patch = mock.patch.object(
380 utils, 'get_enabled_hook_classes',
384 utils, 'get_enabled_hook_classes',
381 return_value=['pull', 'push', 'repo_size'])
385 return_value=['pull', 'push', 'repo_size'])
382 with model_patch as model_mock, hooks_patch:
386 with model_patch as model_mock, hooks_patch:
383 instance_mock = mock.Mock()
387 instance_mock = mock.Mock()
384 model_mock.return_value = instance_mock
388 model_mock.return_value = instance_mock
385 instance_mock.get_ui_settings.return_value = test_data
389 instance_mock.get_ui_settings.return_value = test_data
386 result = utils.config_data_from_db(
390 result = utils.config_data_from_db(
387 clear_session=False, repo=repo_name)
391 clear_session=False, repo=repo_name)
388
392
389 self._assert_repo_name_passed(model_mock, repo_name)
393 self._assert_repo_name_passed(model_mock, repo_name)
390
394
391 expected_result = [
395 expected_result = [
392 ('section1', 'option1', 'value1'),
396 ('section1', 'option1', 'value1'),
393 ('section2', 'option2', 'value2'),
397 ('section2', 'option2', 'value2'),
394 ]
398 ]
395 assert result == expected_result
399 assert result == expected_result
396
400
397 def _assert_repo_name_passed(self, model_mock, repo_name):
401 def _assert_repo_name_passed(self, model_mock, repo_name):
398 assert model_mock.call_count == 1
402 assert model_mock.call_count == 1
399 call_args, call_kwargs = model_mock.call_args
403 call_args, call_kwargs = model_mock.call_args
400 assert call_kwargs['repo'] == repo_name
404 assert call_kwargs['repo'] == repo_name
401
405
402
406
403 class TestIsDirWritable(object):
407 class TestIsDirWritable(object):
404 def test_returns_false_when_not_writable(self):
408 def test_returns_false_when_not_writable(self):
405 with mock.patch('__builtin__.open', side_effect=OSError):
409 with mock.patch('__builtin__.open', side_effect=OSError):
406 assert not utils._is_dir_writable('/stub-path')
410 assert not utils._is_dir_writable('/stub-path')
407
411
408 def test_returns_true_when_writable(self, tmpdir):
412 def test_returns_true_when_writable(self, tmpdir):
409 assert utils._is_dir_writable(str(tmpdir))
413 assert utils._is_dir_writable(str(tmpdir))
410
414
411 def test_is_safe_against_race_conditions(self, tmpdir):
415 def test_is_safe_against_race_conditions(self, tmpdir):
412 workers = multiprocessing.Pool()
416 workers = multiprocessing.Pool()
413 directories = [str(tmpdir)] * 10
417 directories = [str(tmpdir)] * 10
414 workers.map(utils._is_dir_writable, directories)
418 workers.map(utils._is_dir_writable, directories)
415
419
416
420
417 class TestGetEnabledHooks(object):
421 class TestGetEnabledHooks(object):
418 def test_only_active_hooks_are_enabled(self):
422 def test_only_active_hooks_are_enabled(self):
419 ui_settings = [
423 ui_settings = [
420 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
424 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
421 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
425 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
422 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', False)
426 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', False)
423 ]
427 ]
424 result = utils.get_enabled_hook_classes(ui_settings)
428 result = utils.get_enabled_hook_classes(ui_settings)
425 assert result == ['push', 'repo_size']
429 assert result == ['push', 'repo_size']
426
430
427 def test_all_hooks_are_enabled(self):
431 def test_all_hooks_are_enabled(self):
428 ui_settings = [
432 ui_settings = [
429 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
433 UiSetting('hooks', db.RhodeCodeUi.HOOK_PUSH, 'value', True),
430 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
434 UiSetting('hooks', db.RhodeCodeUi.HOOK_REPO_SIZE, 'value', True),
431 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', True)
435 UiSetting('hooks', db.RhodeCodeUi.HOOK_PULL, 'value', True)
432 ]
436 ]
433 result = utils.get_enabled_hook_classes(ui_settings)
437 result = utils.get_enabled_hook_classes(ui_settings)
434 assert result == ['push', 'repo_size', 'pull']
438 assert result == ['push', 'repo_size', 'pull']
435
439
436 def test_no_enabled_hooks_when_no_hook_settings_are_found(self):
440 def test_no_enabled_hooks_when_no_hook_settings_are_found(self):
437 ui_settings = []
441 ui_settings = []
438 result = utils.get_enabled_hook_classes(ui_settings)
442 result = utils.get_enabled_hook_classes(ui_settings)
439 assert result == []
443 assert result == []
440
444
441
445
442 def test_obfuscate_url_pw():
446 def test_obfuscate_url_pw():
443 from rhodecode.lib.utils2 import obfuscate_url_pw
447 from rhodecode.lib.utils2 import obfuscate_url_pw
444 engine = u'/home/repos/malmö'
448 engine = u'/home/repos/malmö'
445 assert obfuscate_url_pw(engine) No newline at end of file
449 assert obfuscate_url_pw(engine)
General Comments 0
You need to be logged in to leave comments. Login now