Skip to content

Commit de07d30

Browse files
authored
Merge pull request #400 from PrestaShop/improve-branch-docker-generation
Improve branch docker generation
2 parents ff2200b + 634b39d commit de07d30

File tree

6 files changed

+636
-208
lines changed

6 files changed

+636
-208
lines changed

Dockerfile-branch.model

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ LABEL maintainer="PrestaShop Core Team <[email protected]>"
44
RUN apt update
55
RUN apt -y install git
66

7-
RUN git clone -b $ps_version https://github.com/PrestaShop/PrestaShop.git /tmp/data-ps
7+
RUN git clone -b $branch_version https://github.com/PrestaShop/PrestaShop.git /tmp/data-ps
88

99
CMD ["/tmp/docker_run.sh"]

HOW-TO-USE.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,44 @@ docker compose up generate
7070
```
7171
7272
This will create new folders for the new version you just added.
73+
74+
## Running tests locally
75+
76+
To run the python tests you need to install requirements
77+
78+
```bash
79+
$ pip install -r requirements.txt
80+
```
81+
82+
Then you can run the tests:
83+
84+
```bash
85+
$ nosetests
86+
```
87+
88+
Locally you may have an error like ``, running these commands may help running tests locally:
89+
90+
```bash
91+
$ pip uninstall -y nose
92+
$ pip install -U nose --no-binary :all:
93+
```
94+
95+
or alternatively:
96+
97+
```bash
98+
$ pip install nose-py3
99+
```
100+
101+
If you need to debug one specific test you first need to run
102+
103+
```
104+
$ nosetests --with-id
105+
```
106+
107+
This will execute tests and each test method will be assigned an ID that you can then use to filter it specifically:
108+
109+
```
110+
$ nosetests --with-id 7
111+
```
112+
113+
This will also generate a `.nodeids` binary file, when you add new test methods you need to remove this file to re-generate the list of IDs.

prestashop_docker/generator.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from string import Template
44
from packaging import version
55
from . import CONTAINERS
6+
from prestashop_docker.version_manager import VersionManager
67

78

89
class Generator:
@@ -27,6 +28,7 @@ def __init__(self, directory_path, template, nightly_template, branch_template):
2728
self.template = Template(template)
2829
self.nightly_template = Template(nightly_template)
2930
self.branch_template = Template(branch_template)
31+
self.version_manager = VersionManager(directory_path)
3032

3133
def create_directory(self, directory_path):
3234
"""Try to create a directory if it's possible
@@ -58,23 +60,35 @@ def generate_image(self, ps_version, container_version):
5860
self.create_directory(directory_path)
5961

6062
file_path = path.join(directory_path, 'Dockerfile')
63+
parsed_version = self.version_manager.get_version_from_string(ps_version)
64+
split_version = self.version_manager.split_prestashop_version(ps_version)
65+
6166
template = self.nightly_template if (
6267
ps_version == self.NIGHTLY
6368
) else self.branch_template if (
64-
ps_version.endswith('.x')
69+
split_version is not None and split_version['patch'] == 'x'
6570
) else self.template
6671

72+
# Get valid PS version (for branch versions it returns to future next patch)
73+
ps_version = parsed_version['ps_version']
74+
branch_version = parsed_version['branch_version']
75+
6776
with open(file_path, 'w+') as f:
68-
# We use 1.7.8.8 as the comparison base because the 1.7.8.9 is not hosted on the .com anymore but until 1.7.8.8 it still works,
69-
# however we can't use 8.0 as the base because 8.0.0-beta is lower than 8.0 and we need beta versions of 8 to use the new url
70-
if version.parse(ps_version) > version.parse('1.7.8.8'):
77+
use_github_url = True
78+
# We use 1.7.8.8 as the comparison base because the 1.7.8.9 is not hosted on the .com anymore but until 1.7.8.8,
79+
# it still works so the .com url is used
80+
if split_version is not None and split_version['major'] == '1.7' and version.parse(ps_version) <= version.parse('1.7.8.8'):
81+
use_github_url = False
82+
83+
if use_github_url:
7184
ps_url = self.download_url_github.format(ps_version, ps_version)
7285
else:
7386
ps_url = self.download_url.format(ps_version)
7487
f.write(
7588
template.substitute(
7689
{
7790
'ps_version': ps_version,
91+
'branch_version': branch_version,
7892
'container_version': container_version,
7993
'ps_url': ps_url
8094
}

prestashop_docker/version_manager.py

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,12 @@ def parse_version(self, version):
8383

8484
def get_version_from_string(self, version):
8585
'''
86-
Split version to find PrestaShop version, PHP version and container type
86+
Split version to find PrestaShop version, branch version, PHP version and container type
8787
8888
@param version: The version you want
8989
@type version: str
90-
@return: A tuple containing ('PS_VERSION', (PHP_VERSIONS), 'CONTAINER_TYPE')
91-
or ('PS_VERSION', 'PHP_VERSION', 'CONTAINER_TYPE')
90+
@return: A tuple containing ('PS_VERSION', 'BRANCH_VERSION', (PHP_VERSIONS), 'CONTAINER_TYPE')
91+
or ('PS_VERSION', 'BRANCH_VERSION', 'PHP_VERSION', 'CONTAINER_TYPE')
9292
@rtype: tuple
9393
'''
9494
matches = self.parse_version_from_string(version)
@@ -108,12 +108,83 @@ def get_version_from_string(self, version):
108108
if matches.group('container'):
109109
container_version = matches.group('container')
110110

111+
split_version = self.split_prestashop_version(ps_version)
112+
if split_version is None:
113+
branch_version = 'develop'
114+
elif split_version['patch'] == 'x':
115+
# ps_version actually already contains the branch
116+
branch_version = ps_version
117+
# We need to transform the branch into the next patch version
118+
patch_index = ps_version.rindex('.')
119+
last_patch = self.get_last_patch_from_version(ps_version)
120+
if last_patch is None:
121+
last_patch = '0'
122+
else:
123+
last_patch = str(int(last_patch) + 1)
124+
ps_version = ps_version[:patch_index + 1] + last_patch
125+
else:
126+
# Transform the last patch version into an x to get the branch, we ignore any -rc that may be present
127+
real_version = split_version['version']
128+
patch_index = real_version.rindex('.')
129+
branch_version = real_version[:patch_index + 1] + 'x'
130+
111131
return {
112132
'ps_version': ps_version,
133+
'branch_version': branch_version,
113134
'php_versions': php_versions,
114135
'container_version': container_version
115136
}
116137

138+
def get_last_patch_from_version(self, version):
139+
'''
140+
Get last patch version for the specified version based on the VERSIONS list
141+
@param version: The version you need the match from
142+
@type version: str
143+
@return: Return None if no patch is found otherwise an int with the patch.
144+
@rtpe: None|int
145+
'''
146+
split_version = self.split_prestashop_version(version)
147+
if (split_version is None):
148+
return None
149+
150+
lastPatch = None
151+
for ps_version, php_versions in VERSIONS.items():
152+
split_ps_version = self.split_prestashop_version(ps_version)
153+
if split_ps_version is None:
154+
continue
155+
if (split_ps_version['major'] != split_version['major'] or split_ps_version['minor'] != split_version['minor']):
156+
continue
157+
if split_ps_version['patch'] == 'x':
158+
continue
159+
if (lastPatch is None or int(split_ps_version['patch']) > int(lastPatch)):
160+
lastPatch = split_ps_version['patch']
161+
return lastPatch
162+
163+
def split_prestashop_version(self, version):
164+
'''
165+
Split the version into major minor patch object, it is a custom-tailed alternative to semver.VersionInfo.parse
166+
that can handle our development branches like 1.7.8.x, 8.0.x, ...
167+
@param version: The version you need to split
168+
@type version: str
169+
@return: Return None if no patch is found otherwise an int with the patch.
170+
@rtpe: None|tuple
171+
'''
172+
regex = r"^(?P<major>(1.)?[0-9]+)\.(?P<minor>[0-9]+)\.(?P<patch>[0-9x]+)(?P<prerelease>-(alpha|beta|rc)(?:\.\d+)?(?:\+\d+)?)?"
173+
matches = re.search(regex, version)
174+
175+
if (matches and matches.group() and matches.group('major') and matches.group('major') and matches.group('major')):
176+
# Remove the initial matched -
177+
prerelease = matches.group('prerelease')[1:] if matches.group('prerelease') else None
178+
179+
return {
180+
'version': matches.group('major') + '.' + matches.group('minor') + '.' + matches.group('patch'),
181+
'major': matches.group('major'),
182+
'minor': matches.group('minor'),
183+
'patch': matches.group('patch'),
184+
'prerelease': prerelease,
185+
}
186+
return None
187+
117188
def parse_version_from_string(self, version):
118189
'''
119190
Parse version from string based on a regex
@@ -122,7 +193,7 @@ def parse_version_from_string(self, version):
122193
@return: Return None if no position in the string matches the pattern otherwise a Match object.
123194
@rtpe: None|Match
124195
'''
125-
regex = r"^(?P<version>(?:[0-9]+\.){0,3}(?:[0-9]+|nightly)(?:-(?:alpha|beta|rc)(?:\.\d+)?(?:\+\d+)?)?)(?:-(?P<php>\d+\.\d+))?(?:-(?P<container>fpm|apache))?$"
196+
regex = r"^(?P<version>(?:[0-9]+\.){0,3}(?:[0-9]+|nightly|x)(?:-(?:alpha|beta|rc)(?:\.\d+)?(?:\+\d+)?)?)(?:-(?P<php>\d+\.\d+))?(?:-(?P<container>fpm|apache))?$"
126197
return re.search(regex, version)
127198

128199
def get_aliases(self):
@@ -150,7 +221,7 @@ def get_aliases(self):
150221
if alias_version != 'latest':
151222
self.append_to_aliases(aliases, ps_version, php_version, PREFERED_CONTAINER, alias_version + '-' + php_version)
152223

153-
# Check prefered container
224+
# Check preferred container
154225
self.append_to_aliases(aliases, ps_version, alias_php_version, PREFERED_CONTAINER, alias_version)
155226

156227
# Check containers
@@ -184,6 +255,14 @@ def get_ps_versions_aliases(self):
184255
}
185256
continue
186257

258+
# Ignore branch versions
259+
split_version = self.split_prestashop_version(ps_version)
260+
if split_version['patch'] == 'x':
261+
aliases[ps_version] = {
262+
'value': ps_version
263+
}
264+
continue
265+
187266
# PrestaShop versions before 8 are in format 1.MAJOR.MINOR.PATCH
188267
# Starting version 8, format is MAJOR.MINOR.PATCH
189268
splitted_version = ps_version.split('.', 1)

tests/prestashop_docker/test_generator.py

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def setUp(self):
2929
contents='''
3030
CONTAINER_VERSION: $container_version
3131
RUN apt -y install git
32-
RUN git clone -b $ps_version https://github.com/PrestaShop/PrestaShop.git /tmp/data-ps
32+
RUN git clone -b $branch_version https://github.com/PrestaShop/PrestaShop.git /tmp/data-ps
3333
'''
3434
)
3535

@@ -46,10 +46,10 @@ def test_create_directory(self):
4646
self.assertTrue(path.exists('/tmp/images/test'))
4747

4848
def test_generate_image(self):
49-
dockerfile = '/tmp/images/1.7.8/7.4-alpine/Dockerfile'
49+
dockerfile = '/tmp/images/1.7.8.0/7.4-alpine/Dockerfile'
5050
self.assertFalse(path.exists(dockerfile))
5151
self.generator.generate_image(
52-
'1.7.8',
52+
'1.7.8.0',
5353
'7.4-alpine'
5454
)
5555
self.assertTrue(path.exists(dockerfile))
@@ -58,29 +58,67 @@ def test_generate_image(self):
5858
content = f.read()
5959
self.assertIn(
6060
'PS_URL: https://www.prestashop.com/download/old/'
61-
'prestashop_1.7.8.zip',
61+
'prestashop_1.7.8.0.zip',
6262
content
6363
)
64-
self.assertIn('PS_VERSION: 1.7.8', content)
64+
self.assertIn('PS_VERSION: 1.7.8.0', content)
65+
self.assertIn('CONTAINER_VERSION: 7.4-alpine', content)
66+
67+
def test_generate_image_1788(self):
68+
dockerfile = '/tmp/images/1.7.8.8/7.4-alpine/Dockerfile'
69+
self.assertFalse(path.exists(dockerfile))
70+
self.generator.generate_image(
71+
'1.7.8.8',
72+
'7.4-alpine'
73+
)
74+
self.assertTrue(path.exists(dockerfile))
75+
76+
with open(dockerfile) as f:
77+
content = f.read()
78+
self.assertIn(
79+
'PS_URL: https://www.prestashop.com/download/old/'
80+
'prestashop_1.7.8.8.zip',
81+
content
82+
)
83+
self.assertIn('PS_VERSION: 1.7.8.8', content)
84+
self.assertIn('CONTAINER_VERSION: 7.4-alpine', content)
85+
86+
def test_generate_image_1789(self):
87+
dockerfile = '/tmp/images/1.7.8.9/7.4-alpine/Dockerfile'
88+
self.assertFalse(path.exists(dockerfile))
89+
self.generator.generate_image(
90+
'1.7.8.9',
91+
'7.4-alpine'
92+
)
93+
self.assertTrue(path.exists(dockerfile))
94+
95+
with open(dockerfile) as f:
96+
content = f.read()
97+
self.assertIn(
98+
'PS_URL: https://github.com/PrestaShop/PrestaShop/releases/download/1.7.8.9/'
99+
'prestashop_1.7.8.9.zip',
100+
content
101+
)
102+
self.assertIn('PS_VERSION: 1.7.8.9', content)
65103
self.assertIn('CONTAINER_VERSION: 7.4-alpine', content)
66104

67105
def test_generate_image_80(self):
68-
dockerfile = '/tmp/images/8.0/7.4-alpine/Dockerfile'
106+
dockerfile = '/tmp/images/8.0.0/7.4-alpine/Dockerfile'
69107
self.assertFalse(path.exists(dockerfile))
70108
self.generator.generate_image(
71-
'8.0',
109+
'8.0.0',
72110
'7.4-alpine'
73111
)
74112
self.assertTrue(path.exists(dockerfile))
75113

76114
with open(dockerfile) as f:
77115
content = f.read()
78116
self.assertIn(
79-
'PS_URL: https://github.com/PrestaShop/PrestaShop/releases/download/8.0/'
80-
'prestashop_8.0.zip',
117+
'PS_URL: https://github.com/PrestaShop/PrestaShop/releases/download/8.0.0/'
118+
'prestashop_8.0.0.zip',
81119
content
82120
)
83-
self.assertIn('PS_VERSION: 8.0', content)
121+
self.assertIn('PS_VERSION: 8.0.0', content)
84122
self.assertIn('CONTAINER_VERSION: 7.4-alpine', content)
85123

86124
def test_generate_nightly_image(self):
@@ -116,29 +154,42 @@ def test_generate_branch_image(self):
116154
'PS_URL',
117155
content
118156
)
119-
self.assertNotIn('PS_VERSION', content)
120157
self.assertIn('CONTAINER_VERSION: 8.1-alpine', content)
121158
self.assertIn('RUN apt -y install git', content)
122159
self.assertIn('RUN git clone -b 9.0.x https://github.com/PrestaShop/PrestaShop.git /tmp/data-ps', content)
123160

124161
def test_generate_all(self):
125162
files = (
126-
'/tmp/images/7.0/7.3-apache/Dockerfile',
127-
'/tmp/images/7.0/7.3-fpm/Dockerfile',
128-
'/tmp/images/7.0/7.2-apache/Dockerfile',
129-
'/tmp/images/7.0/7.2-fpm/Dockerfile',
130-
'/tmp/images/8.0/7.1-apache/Dockerfile',
131-
'/tmp/images/8.0/7.1-fpm/Dockerfile',
132-
'/tmp/images/8.0/5.6-apache/Dockerfile',
133-
'/tmp/images/8.0/5.6-fpm/Dockerfile',
163+
'/tmp/images/1.7.8.8/7.3-apache/Dockerfile',
164+
'/tmp/images/1.7.8.8/7.3-fpm/Dockerfile',
165+
'/tmp/images/1.7.8.8/7.2-apache/Dockerfile',
166+
'/tmp/images/1.7.8.8/7.2-fpm/Dockerfile',
167+
'/tmp/images/8.0.0/7.2-apache/Dockerfile',
168+
'/tmp/images/8.0.0/7.2-fpm/Dockerfile',
169+
'/tmp/images/8.0.0/8.1-apache/Dockerfile',
170+
'/tmp/images/8.0.0/8.1-fpm/Dockerfile',
171+
'/tmp/images/9.0.x/8.1-apache/Dockerfile',
172+
'/tmp/images/9.0.x/8.1-fpm/Dockerfile',
173+
'/tmp/images/9.0.x/8.2-apache/Dockerfile',
174+
'/tmp/images/9.0.x/8.2-fpm/Dockerfile',
175+
'/tmp/images/9.0.x/8.3-apache/Dockerfile',
176+
'/tmp/images/9.0.x/8.3-fpm/Dockerfile',
177+
'/tmp/images/nightly/8.1-apache/Dockerfile',
178+
'/tmp/images/nightly/8.1-fpm/Dockerfile',
179+
'/tmp/images/nightly/8.2-apache/Dockerfile',
180+
'/tmp/images/nightly/8.2-fpm/Dockerfile',
181+
'/tmp/images/nightly/8.3-apache/Dockerfile',
182+
'/tmp/images/nightly/8.3-fpm/Dockerfile',
134183
)
135184
for f in files:
136185
self.assertFalse(path.exists(f))
137186

138187
self.generator.generate_all(
139188
{
140-
'7.0': ('7.2', '7.3'),
141-
'8.0': ('7.1', '5.6'),
189+
'1.7.8.8': ('7.2', '7.3'),
190+
'8.0.0': ('7.2', '8.1'),
191+
'9.0.x': ('8.1', '8.2', '8.3'),
192+
'nightly': ('8.1', '8.2', '8.3'),
142193
}
143194
)
144195

0 commit comments

Comments
 (0)