Completed
Push — master ( 5a85d6...535813 )
by
unknown
17s queued 14s
created

gvm.version.get_version_from_pyproject_toml()   B

Complexity

Conditions 6

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 13
nop 1
dl 0
loc 21
rs 8.6666
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2020 Greenbone Networks GmbH
3
#
4
# SPDX-License-Identifier: GPL-3.0-or-later
5
#
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
import argparse
20
import re
21
import sys
22
23
from pathlib import Path
24
25
import tomlkit
26
27
from packaging.version import Version, InvalidVersion
28
29
30
from gvm import get_version
31
32
33
def strip_version(version: str) -> str:
34
    """
35
    Strips a leading 'v' from a version string
36
37
    E.g. v1.2.3 will be converted to 1.2.3
38
    """
39
    if version and version[0] == 'v':
40
        return version[1:]
41
42
    return version
43
44
45
def safe_version(version: str) -> str:
46
    """
47
    Returns the version as a string in `PEP440`_ compliant
48
    format.
49
50
    .. _PEP440:
51
       https://www.python.org/dev/peps/pep-0440
52
    """
53
    try:
54
        return str(Version(version))
55
    except InvalidVersion:
56
        version = version.replace(' ', '.')
57
        return re.sub('[^A-Za-z0-9.]+', '-', version)
58
59
60
def get_version_from_pyproject_toml(pyproject_toml_path: Path = None) -> str:
61
    """
62
    Return the version information from the [tool.poetry] section of the
63
    pyproject.toml file. The version may be in non standardized form.
64
    """
65
    if not pyproject_toml_path:
66
        path = Path(__file__)
67
        pyproject_toml_path = path.parent.parent / 'pyproject.toml'
68
69
    if not pyproject_toml_path.exists():
70
        raise RuntimeError('pyproject.toml file not found.')
71
72
    pyproject_toml = tomlkit.parse(pyproject_toml_path.read_text())
73
    if (
74
        'tool' in pyproject_toml
75
        and 'poetry' in pyproject_toml['tool']
76
        and 'version' in pyproject_toml['tool']['poetry']
77
    ):
78
        return pyproject_toml['tool']['poetry']['version']
79
80
    raise RuntimeError('Version information not found in pyproject.toml file.')
81
82
83
def get_version_string(version: tuple) -> str:
84
    """Create a version string from a version tuple
85
86
    Arguments:
87
        version: version as tuple e.g. (1, 2, 0, dev, 5)
88
89
    Returns:
90
        The version tuple converted into a string representation
91
    """
92
    if len(version) > 4:
93
        ver = ".".join(str(x) for x in version[:4])
94
        ver += str(version[4])
95
96
        if len(version) > 5:
97
            # support (1, 2, 3, 'beta', 2, 'dev', 1)
98
            ver += ".{0}{1}".format(str(version[5]), str(version[6]))
99
100
        return ver
101
    else:
102
        return ".".join(str(x) for x in version)
103
104
105
def print_version(pyproject_toml_path: Path = None) -> None:
106
    pyproject_version = get_version_from_pyproject_toml(
107
        pyproject_toml_path=pyproject_toml_path
108
    )
109
110
    print(pyproject_version)
111
112
113
def versions_equal(new_version: str, old_version: str) -> bool:
114
    """
115
    Checks if new_version and old_version are equal
116
    """
117
    return safe_version(old_version) == safe_version(new_version)
118
119
120
def is_version_pep440_compliant(version: str) -> bool:
121
    """
122
    Checks if the provided version is a PEP 440 compliant version string
123
    """
124
    return version == safe_version(version)
125
126
127
def update_pyproject_version(
128
    new_version: str, pyproject_toml_path: Path,
129
) -> None:
130
    """
131
    Update the version in the pyproject.toml file
132
    """
133
    version = safe_version(new_version)
134
135
    pyproject_toml = tomlkit.parse(pyproject_toml_path.read_text())
136
137
    if 'tool' not in pyproject_toml:
138
        tool_table = tomlkit.table()
139
        pyproject_toml['tool'] = tool_table
140
141
    if 'poetry' not in pyproject_toml['tool']:
142
        poetry_table = tomlkit.table()
143
        pyproject_toml['tool'].add('poetry', poetry_table)
144
145
    pyproject_toml['tool']['poetry']['version'] = version
146
147
    pyproject_toml_path.write_text(tomlkit.dumps(pyproject_toml))
148
149
150
def update_version_file(new_version: str, version_file_path: Path) -> None:
151
    """
152
    Update the version file with the new version
153
    """
154
    version = safe_version(new_version)
155
156
    text = """# pylint: disable=invalid-name
157
158
# THIS IS AN AUTOGENERATED FILE. DO NOT TOUCH!
159
160
__version__ = "{}"\n""".format(
161
        version
162
    )
163
    version_file_path.write_text(text)
164
165
166
def _update_python_gvm_version(
167
    new_version: str, pyproject_toml_path: Path, *, force: bool = False
168
):
169
    if not pyproject_toml_path.exists():
170
        sys.exit(
171
            'Could not find pyproject.toml file in the current working dir.'
172
        )
173
174
    cwd_path = Path.cwd()
175
    python_gvm_version = get_version()
176
    pyproject_version = get_version_from_pyproject_toml(
177
        pyproject_toml_path=pyproject_toml_path
178
    )
179
    version_file_path = cwd_path / 'gvm' / '__version__.py'
180
181
    if not pyproject_toml_path.exists():
182
        sys.exit(
183
            'Could not find __version__.py file at {}.'.format(
184
                version_file_path
185
            )
186
        )
187
188
    if not force and versions_equal(new_version, python_gvm_version):
189
        print('Version is already up-to-date.')
190
        sys.exit(0)
191
192
    update_pyproject_version(
193
        new_version=new_version, pyproject_toml_path=pyproject_toml_path
194
    )
195
196
    update_version_file(
197
        new_version=new_version, version_file_path=version_file_path,
198
    )
199
200
    print(
201
        'Updated version from {} to {}'.format(
202
            pyproject_version, safe_version(new_version)
203
        )
204
    )
205
206
207
def _verify_version(version: str, pyproject_toml_path: Path) -> None:
208
    python_gvm_version = get_version()
209
    pyproject_version = get_version_from_pyproject_toml(
210
        pyproject_toml_path=pyproject_toml_path
211
    )
212
    if not is_version_pep440_compliant(python_gvm_version):
213
        sys.exit("The version in gvm/__version__.py is not PEP 440 compliant.")
214
215
    if pyproject_version != python_gvm_version:
216
        sys.exit(
217
            "The version set in the pyproject.toml file \"{}\" doesn't "
218
            "match the python-gvm version \"{}\"".format(
219
                pyproject_version, python_gvm_version
220
            )
221
        )
222
223
    if version != 'current':
224
        provided_version = strip_version(version)
225
        if provided_version != python_gvm_version:
226
            sys.exit(
227
                "Provided version \"{}\" does not match the python-gvm "
228
                "version \"{}\"".format(provided_version, python_gvm_version)
229
            )
230
231
    print('OK')
232
233
234
def main():
235
    parser = argparse.ArgumentParser(
236
        description='Version handling utilities for python-gvm.', prog='version'
237
    )
238
239
    subparsers = parser.add_subparsers(
240
        title='subcommands',
241
        description='valid subcommands',
242
        help='additional help',
243
        dest='command',
244
    )
245
246
    verify_parser = subparsers.add_parser('verify')
247
    verify_parser.add_argument('version', help='version string to compare')
248
249
    subparsers.add_parser('show')
250
251
    update_parser = subparsers.add_parser('update')
252
    update_parser.add_argument('version', help='version string to use')
253
    update_parser.add_argument(
254
        '--force',
255
        help="don't check if version is already set",
256
        action="store_true",
257
    )
258
259
    args = parser.parse_args()
260
261
    if not getattr(args, 'command', None):
262
        parser.print_usage()
263
        sys.exit(0)
264
265
    pyproject_toml_path = Path.cwd() / 'pyproject.toml'
266
267
    if args.command == 'update':
268
        _update_python_gvm_version(
269
            args.version,
270
            pyproject_toml_path=pyproject_toml_path,
271
            force=args.force,
272
        )
273
    elif args.command == 'show':
274
        print_version(pyproject_toml_path=pyproject_toml_path)
275
    elif args.command == 'verify':
276
        _verify_version(args.version, pyproject_toml_path=pyproject_toml_path)
277
278
279
if __name__ == '__main__':
280
    main()
281