Skip to content

Commit 3538d49

Browse files
Anna Thorntoncf-buildpacks-eng
Anna Thornton
authored andcommitted
Use semver when parsing version from composer.json
- replace '>=' with '~>' to maintain backwards compatibility [#140517213] Signed-off-by: Sam Smith <[email protected]>
1 parent 6c3a65d commit 3538d49

26 files changed

+2031
-26
lines changed

Diff for: .gitallowed

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi
22
f7d573650a295b94e0938d32b323fde775e5f32b
33
ABCEGHJKLMNPRSTVWXYZ
4+
PRERELEASEIDENTIFIER
5+
NONNUMERICIDENTIFIER

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ binaries/trusty/*
1010
*_buildpack*.zip
1111
/log/
1212
vendor
13+
!/vendor
1314
*.DS_Store
1415
.bundle
1516
DebugKit

Diff for: extensions/composer/extension.py

+16-15
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
from build_pack_utils import stream_output
2828
from extension_helpers import ExtensionHelper
2929

30+
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'vendor', 'node-semver'))
31+
from semver import max_satisfying
32+
3033
from build_pack_utils.compile_extensions import CompileExtensions
3134

3235

@@ -95,22 +98,20 @@ def read_exts_from_path(self, path):
9598

9699
def pick_php_version(self, requested):
97100
selected = None
98-
if requested is None:
99-
selected = self._ctx['PHP_VERSION']
100-
elif requested == '5.6.*' or requested == '>=5.6':
101-
selected = self._ctx['PHP_56_LATEST']
102-
elif requested == '7.0.*' or requested == '>=7.0':
103-
selected = self._ctx['PHP_70_LATEST']
104-
elif requested == '7.1.*' or requested == '>=7.1':
105-
selected = self._ctx['PHP_71_LATEST']
106-
elif requested.startswith('5.6.'):
107-
selected = requested
108-
elif requested.startswith('7.0.'):
109-
selected = requested
110-
elif requested.startswith('7.1.'):
111-
selected = requested
112-
else:
101+
102+
if requested is None or requested is '':
103+
return self._ctx['PHP_VERSION']
104+
105+
# requested is coming from the composer.json file and is a unicode string type.
106+
# Since it's just a semver string, it shouldn't actually contain any unicode
107+
# characters. So it should be safe to turn it into an ASCII string
108+
translated_requirement = str(requested.replace('>=', '~>'))
109+
110+
selected = max_satisfying(self._ctx['ALL_PHP_VERSIONS'], translated_requirement, loose=False)
111+
112+
if selected is None:
113113
selected = self._ctx['PHP_VERSION']
114+
114115
return selected
115116

116117
def get_composer_contents(self, file_path):

Diff for: tests/test_composer.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ def test_configure_composer_with_php_version(self):
225225
ctx = utils.FormattedDict({
226226
'BUILD_DIR': 'tests/data/composer',
227227
'WEBDIR': '',
228-
'PHP_56_LATEST': '5.6.31'
228+
'PHP_56_LATEST': '5.6.31',
229+
'ALL_PHP_VERSIONS': ['5.6.31', '5.6.29', '7.0.13', '7.0.14', '7.1.3', '7.1.4']
229230
})
230231
config = self.extension_module.ComposerConfiguration(ctx)
231232
config.configure()
@@ -244,7 +245,8 @@ def test_configure_composer_with_php_version_and_base_extensions(self):
244245
'BUILD_DIR': 'tests/data/composer',
245246
'WEBDIR': '',
246247
'PHP_EXTENSIONS': ['a', 'b'],
247-
'PHP_56_LATEST': '5.6.31'
248+
'PHP_56_LATEST': '5.6.31',
249+
'ALL_PHP_VERSIONS': ['5.6.31', '5.6.29', '7.0.13', '7.0.14', '7.1.3', '7.1.4']
248250
})
249251
config = self.extension_module.ComposerConfiguration(ctx)
250252
config.configure()
@@ -306,7 +308,8 @@ def fcp_test_none(path):
306308
ctx = utils.FormattedDict({
307309
'BUILD_DIR': 'tests/data/composer',
308310
'WEBDIR': '',
309-
'PHP_56_LATEST': '5.6.31'
311+
'PHP_56_LATEST': '5.6.31',
312+
'ALL_PHP_VERSIONS': ['5.6.31', '5.6.29', '7.0.13', '7.0.14', '7.1.3', '7.1.4']
310313
})
311314
fcp_orig = self.extension_module.find_composer_paths
312315
# test when no composer.json or composer.lock files found
@@ -361,29 +364,33 @@ def test_pick_php_version(self):
361364
'PHP_56_LATEST': '5.6.29',
362365
'PHP_70_LATEST': '7.0.14',
363366
'PHP_71_LATEST': '7.1.4',
364-
'WEBDIR': ''
367+
'WEBDIR': '',
368+
'ALL_PHP_VERSIONS': ['5.6.28', '5.6.29', '7.0.13', '7.0.14', '7.1.3', '7.1.4']
365369
}
366370
pick_php_version = \
367371
self.extension_module.ComposerConfiguration(ctx).pick_php_version
368372
# default to 5.6
369373
# latest PHP 5.6 version
370374
eq_('5.6.29', pick_php_version('>=5.6'))
375+
eq_('5.6.29', pick_php_version('>=5.6.0'))
371376
eq_('5.6.29', pick_php_version('5.6.*'))
372377
# exact PHP 5.6 versions
373-
eq_('5.6.29', pick_php_version('5.6.29'))
374-
eq_('5.6.6', pick_php_version('5.6.6'))
378+
eq_('5.6.28', pick_php_version('5.6.28'))
375379
# latest PHP 7.0 version
376380
eq_('7.0.14', pick_php_version('>=7.0'))
381+
eq_('7.0.14', pick_php_version('>=7.0.0'))
377382
eq_('7.0.14', pick_php_version('7.0.*'))
378383
# exact PHP 7.0 versions
379-
eq_('7.0.1', pick_php_version('7.0.1'))
380-
eq_('7.0.2', pick_php_version('7.0.2'))
384+
eq_('7.0.13', pick_php_version('7.0.13'))
381385
# PHP 7.1 versions
382-
eq_('7.1.1', pick_php_version('7.1.1'))
383-
eq_('7.1.2', pick_php_version('7.1.2'))
386+
eq_('7.1.3', pick_php_version('7.1.3'))
384387
eq_('7.1.4', pick_php_version('>=7.1'))
388+
eq_('7.1.4', pick_php_version('>=7.1.0'))
385389
eq_('7.1.4', pick_php_version('7.1.*'))
386-
# not understood, should default to PHP_VERSION
390+
# not in buildpack, should default to PHP_VERSION
391+
eq_('5.6.29', pick_php_version('7.1.2'))
392+
eq_('5.6.29', pick_php_version('7.0.2'))
393+
eq_('5.6.29', pick_php_version('5.6.6'))
387394
eq_('5.6.29', pick_php_version(''))
388395
eq_('5.6.29', pick_php_version(None))
389396
eq_('5.6.29', pick_php_version('5.61.1'))

Diff for: vendor/node-semver/.gitignore

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
5+
# C extensions
6+
*.so
7+
8+
# Distribution / packaging
9+
.Python
10+
env/
11+
bin/
12+
build/
13+
develop-eggs/
14+
dist/
15+
eggs/
16+
lib/
17+
lib64/
18+
parts/
19+
sdist/
20+
var/
21+
*.egg-info/
22+
.installed.cfg
23+
*.egg
24+
25+
# Installer logs
26+
pip-log.txt
27+
pip-delete-this-directory.txt
28+
29+
# Unit test / coverage reports
30+
htmlcov/
31+
.tox/
32+
.coverage
33+
.cache
34+
nosetests.xml
35+
coverage.xml
36+
37+
# Translations
38+
*.mo
39+
40+
# Mr Developer
41+
.mr.developer.cfg
42+
.project
43+
.pydevproject
44+
45+
# Rope
46+
.ropeproject
47+
48+
# Django stuff:
49+
*.log
50+
*.pot
51+
52+
# Sphinx documentation
53+
docs/_build/
54+

Diff for: vendor/node-semver/CHANGES.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

Diff for: vendor/node-semver/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2016 podhmo
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Diff for: vendor/node-semver/Makefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
DST=README.rst
2+
default:
3+
echo semver > ${DST}
4+
echo ================= >> ${DST}
5+
echo "" >> ${DST}
6+
echo "python version of [node-semver](https://github.com/isaacs/node-semver)" >> ${DST}
7+
echo "" >> ${DST}
8+
echo ".. code:: python\n" >> ${DST}
9+
cat ./demo.py | gsed 's/^\(.\)/ \1/g' >> ${DST}

Diff for: vendor/node-semver/README.rst

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
semver
2+
=================
3+
4+
python version of [node-semver](https://github.com/isaacs/node-semver)
5+
6+
.. code:: python
7+
8+
# -*- coding:utf-8 -*-
9+
from semver import max_satisfying
10+
11+
versions = ['1.2.3', '1.2.4', '1.2.5', '1.2.6', '2.0.1']
12+
range_ = '~1.2.3'
13+
assert max_satisfying(versions, range_, loose=False) == '1.2.6'
14+
15+
16+
versions = ['1.1.0', '1.2.0', '1.2.1', '1.3.0', '2.0.0b1', '2.0.0b2', '2.0.0b3', '2.0.0', '2.1.0']
17+
range_ = '~2.0.0'
18+
assert max_satisfying(versions, range_, loose=True) == '2.0.0'
19+
20+
try:
21+
(max_satisfying(versions, range_, loose=False) == '2.0.0')
22+
except ValueError as e:
23+
assert e.args[0] == "Invalid Version: 2.0.0b1"

Diff for: vendor/node-semver/demo.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# -*- coding:utf-8 -*-
2+
from semver import max_satisfying
3+
4+
versions = ['1.2.3', '1.2.4', '1.2.5', '1.2.6', '2.0.1']
5+
range_ = '~1.2.3'
6+
assert max_satisfying(versions, range_, loose=False) == '1.2.6'
7+
8+
9+
versions = ['1.1.0', '1.2.0', '1.2.1', '1.3.0', '2.0.0b1', '2.0.0b2', '2.0.0b3', '2.0.0', '2.1.0']
10+
range_ = '~2.0.0'
11+
assert max_satisfying(versions, range_, loose=True) == '2.0.0'
12+
13+
try:
14+
(max_satisfying(versions, range_, loose=False) == '2.0.0')
15+
except ValueError as e:
16+
assert e.args[0] == "Invalid Version: 2.0.0b1"

0 commit comments

Comments
 (0)