Passed
Pull Request — master (#12)
by Konstantinos
01:15
created

process_sem_ver   A

Complexity

Total Complexity 0

Size/Duplication

Total Lines 227
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 78
dl 0
loc 227
rs 10
c 0
b 0
f 0
wmc 0
1
#!/usr/bin/env python
2
3
# 'Assumptions': input string follows Sem Ver 2.0
4
# with 2 'Limitations' on pre-release metadata and build metadata:
5
6
# 1) if user wants to include pre-release info, they must
7
#     - separate with dash (-) from M.m.p (ie 1.0.0-dev)
8
#     - only include characters from [a-z]
9
# 2) no build metadata are supported and string MUST end with patch or pre-release metadata
10
11
import re
12
import sys
13
14
if len(sys.argv) != 2:
15
    print("Usage: process_sem_ver.py <version>")
16
    print("Example: process_sem_ver.py 1.0.0-dev")
17
    sys.exit(1)
18
19
semver: str = sys.argv[1]
20
21
22
VERSION_PATTERN = r"""
23
    v?
24
    (?:
25
        (?:(?P<epoch>[0-9]+)!)?                           # epoch
26
        (?P<release>[0-9]+(?:\.[0-9]+)*)                  # release segment
27
        (?P<pre>                                          # pre-release
28
            [-_\.]?
29
            (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
30
            [-_\.]?
31
            (?P<pre_n>[0-9]+)?
32
        )?
33
        (?P<post>                                         # post release
34
            (?:-(?P<post_n1>[0-9]+))
35
            |
36
            (?:
37
                [-_\.]?
38
                (?P<post_l>post|rev|r)
39
                [-_\.]?
40
                (?P<post_n2>[0-9]+)?
41
            )
42
        )?
43
        (?P<dev>                                          # dev release
44
            [-_\.]?
45
            (?P<dev_l>dev)
46
            [-_\.]?
47
            (?P<dev_n>[0-9]+)?
48
        )?
49
    )
50
    (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
51
"""
52
53
_regex = re.compile(
54
    r"^\s*" + VERSION_PATTERN + r"\s*$",
55
    re.VERBOSE | re.IGNORECASE,
56
)
57
58
59
# Verify input string meets our 'Hard Requirements', otherwise show message:
60
# - indicating what happened and crashed the program
61
# - what caused the crash
62
# - how the user input is part of the cause
63
# -  what the user should try to do, to fix the issue
64
65
# 'Hard Requirements', based on 'Assumptions' and 'Limitations' (see above):
66
# - string must be at least 5 characters long
67
# - string mush have 2 dots
68
# - string must end with a patch or pre-release metadata
69
# - string must have a dash (-) if pre-release metadata is included
70
# - if prerelase metadata is included, it must be only characheters from [a-z]
71
72
# Verify string is at least 5 characters long
73
if len(semver) < 5:
74
    print("[ERROR]: Sem Ver Version string must be at least 5 characters long")
75
    print(f"Your input: {semver}")
76
    print(f"Your input is only {len(semver)} characters long")
77
    print("Please try again with a version string that is at least 5 characters long")
78
    sys.exit(1)
79
80
# Verify string has 2 dots
81
if semver.count(".") != 2:
82
    print("[ERROR]: Version string must have 2 dots")
83
    print(f"Your input: {semver}")
84
    print(f"Your input has {semver.count('.')} dots")
85
    print("Please try again with a version string that has 2 dots")
86
    sys.exit(1)
87
88
89
# Interact with the Sem Ver 2.0 Regular Expression at https://regex101.com/r/Ly7O1x/3/
90
# Sem Ver 2.0 Docs related section at: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
91
92
# Note: Sem Ver 2.0 requires dash (-) to separate pre-release metadata
93
94
# Verify string ends with a patch or pre-release metadata
95
## Sem Ver 2.0 requires dash (-) to separate pre-release metadata
96
97
if "-" not in semver:  # if no Sem Ver 2.0 prerelease separator found in the string
98
    # for us now it is impossible to have pre-release metadata
99
    # and the string MUST be in Major.Minor.Patch format only
100
101
    if semver[-1] not in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]:
102
        # ERROR: Should be dealing with M.m.p case, but last character is not digit
103
        print("[ERROR]: Version string must end with a patch or pre-release metadata")
104
        print(f"Your input: {semver}")
105
        print(f"Your input ends with {semver[-1]}")
106
        print(
107
            "Please try again with a version string that ends with a patch or pre-release metadata"
108
        )
109
        print(
110
            "EXPLANATION: Since we did not find a dash (-) in the input, we expect to",
111
            " the Input Version String to be of 'Major.Minor.Patch' format."
112
            " So, 'Patch' must be the last part of the string, thus the last digit must be a number."
113
            f"But we found {semver[-1]} instead\n."
114
            "If you intended to include 'pre-release' metadata,"
115
            " please concatenate a dash (-) to the mandatory starting 'Major.Minor.Patch' part"
116
            " and then add your 'pre-release' metadata, ie '1.0.0-dev'.",
117
        )
118
        sys.exit(1)
119
120
    # more reg ex checks can go here, but that not really the purpose of this script
121
122
else:  # if Sem Ver 2.0 prerelease separator found in the string
123
    # 1) Given the above condition
124
    # 2) Given scripts 'Limitations':
125
    #    - only characters [a-z] are allowed for prerelease metadata
126
    #    - we do not support 'build metadata' of Sem Ver 2.0
127
128
    # Then
129
    #  - expect to find exactly 1 dash (-),
130
    #  - the dash is right after Patch (ie Major.Minor.Patch-Prerelaese)
131
    #  - only [a-z] characters are found in prerelease substring
132
    #  - prerelease substring is not empty
133
134
    if semver.count("-") != 1:
135
        # ERROR: Should be dealing with M.m.p-prerelase case, but more than 1 dash found
136
        print("[ERROR]: Version string must have exactly 1 dash (-)")
137
        print(f"Your input: {semver}")
138
        print(f"Your input has {semver.count('-')} dashes")
139
        print("Please try again with a version string that has exactly 1 dash (-)")
140
        print(
141
            "EXPLANATION: Since, we found a dash (-) in the input, and given the script 'Limitation' that we do not support build-metadata, we expect",
142
            " the Input Version String to be of 'Major.Minor.Patch-Prerelease' format.",
143
        )
144
        sys.exit(1)
145
146
    prerelease: str = semver.split("-")[1]  # get the prerelease substring
147
148
    if prerelease == "":
149
        # ERROR: Should be dealing with M.m.p-prerelase case, but prerelease substring is empty
150
        print("[ERROR]: Version string must have a non-empty prerelease substring")
151
        print(f"Your input: {semver}")
152
        print("Your input has an empty prerelease substring")
153
        print(
154
            "Please try again with a version string that has a non-empty prerelease substring"
155
        )
156
        print(
157
            "EXPLANATION: Since, we found a dash (-) in the input, and given the script 'Limitation' that we do not support build-metadata, we expect",
158
            " the Input Version String to be of 'Major.Minor.Patch-Prerelease' format.",
159
        )
160
        sys.exit(1)
161
162
    # english alphabet has 26 characters
163
    # lowercase_chars = set(chr(ord("a") + i) for i in range(26))
164
    # if lowercase_chars.issuperset(prerelease) is False:
165
    #     # ERROR: Should be dealing with M.m.p-prerelase case, but found non [a-z] characters in prerelease substring
166
    #     print("[ERROR]: Version string's prerelease must have only [a-z] characters")
167
    #     print(f"Your input: {semver}")
168
    #     print(f"Your input has {prerelease}")
169
    #     print(
170
    #         "Please try again with a version string that has only [a-z] characters in prerelease substring"
171
    #     )
172
    #     print(
173
    #         "EXPLANATION: Since we found a dash (-) in the input, we expect to",
174
    #         " the Input Version String to be of 'Major.Minor.Patch-Prerelease' format.",
175
    #     )
176
    #     sys.exit(1)
177
178
    # more reg ex checks can go here, but that not really the purpose of this script
179
180
181
# If we got here, then the input string is a valid Sem Ver 2.0 string
182
# And valid as input to the rest of the script
183
184
185
# CRITICAL to be in par with Pip sdist /wheel and python -m build operations
186
# https://peps.python.org/pep-0440/#compatibility-with-other-version-schemes
187
188
189
parsed_versions_string = _regex.match(semver)
190
191
prerelease = parsed_versions_string.group("pre") or parsed_versions_string.group("dev") or ''
192
193
sep = ''
194
195
to_add = ''
196
if prerelease:
197
    prerelease = prerelease.replace('.', '').replace('-', '').replace('_', '')
198
199
    if prerelease.startswith('rc'):
200
        sep = ''
201
    elif prerelease.startswith('dev'):
202
        sep = '.'
203
    else:
204
        # don't have code hre to handle yet
205
        print('ERROR: Our current limitation is that prerelease must start with rc or dev')
206
        print(f'Your input: {semver}')
207
        print(f'Your input has {prerelease}')
208
        sys.exit(1)
209
210
    string = prerelease.replace('rc', '').replace('dev', '')
211
212
    try:
213
        int(string)
214
        # last part is a number already, so we keep that as it is
215
        to_add = ''
216
    except ValueError:
217
        to_add = '0'
218
219
220
print(
221
    f'{parsed_versions_string.group("epoch") or ""}'
222
    f'{parsed_versions_string.group("release")}'
223
    f'{sep}{prerelease}{to_add}'
224
)
225
226
assert sys.argv[1] == semver
227
228
# here it safe to implement the logic simply as:
229
# if there is a dash then convert to dot and add trailing zero (0), else return as it is
230
# print(sys.argv[1] if "-" not in sys.argv[1] else sys.argv[1].replace("-", ".") + "0")
231
232
233
# ALT Format 1
234
# we provide oneliner with exa same print statement, except we skip all checks
235
236
# print(sys.argv[1] if "-" not in sys.argv[1] else sys.argv[1].replace("-", ".") + "0")
237
238
239
# ALT Format 2
240
# We provide python script, as shell command, with same input and output as this script
241
# ( we skip all the checks, and provide the shell starting with python -c '' )
242
243
# python -c 'import sys; print(sys.argv[1] if "-" not in sys.argv[1] else sys.argv[1].replace("-", ".") + "0")' 1.0.0-dev
244
245
246
# PARSED_DISTRO_SEMVER=$(python -c 'import sys; print(sys.argv[1] if "-" not in sys.argv[1] else sys.argv[1].replace("-", ".") + "0")' "${PARSED_VERSION}")
247
248
#         if [[ "${PARSED_DISTRO_SEMVER}" != "${TAG_SEM_VER}" ]]; then
249
#   echo "ERROR: Version in __init__.py (${PARSED_DISTRO_SEMVER}) does not match tag (${TAG_SEM_VER})"
250
#   exit 1
251
# fi
252
253
254
# PARSER="scripts/parse_version.py"
255
# PARSED_VERSION=$(python "${PARSER}")
256