Skip to content

Commit 7a99ab9

Browse files
authored
Merge pull request #12 from nexB/adopt-vers-spec
Adopt "vers" version range spec
2 parents 12ebed2 + 588ef39 commit 7a99ab9

36 files changed

Lines changed: 1500 additions & 622 deletions

AUTHORS.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The following organizations or individuals have contributed to this repo:
22

3-
- Shivam Sandbhor
4-
- nexB Inc.
5-
- Philippe Ombredanne
3+
- Shivam Sandbhor @sbs2001
4+
- Philippe Ombredanne @pombredanne
5+
- Hritik Vijay @Hritik14
66

README.rst

Lines changed: 100 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,70 +5,116 @@ univers: mostly universal version and version ranges comparison and conversion
55

66
.. |Build Status| image:: https://api.travis-ci.com/sbs2001/univers.svg?branch=main&status=passed
77
.. |License| image:: https://img.shields.io/badge/License-Apache%202.0-blue.svg
8-
:target: https://opensource.org/licenses/Apache-2.0
8+
:target: https://scancode-licensedb.aboutcode.org/apache-2.0.html
99
.. |Python 3.6+| image:: https://img.shields.io/badge/python-3.6+-blue.svg
1010
:target: https://www.python.org/downloads/release/python-380/
1111

1212

13-
14-
univers was born out of the need for a mostly univeral way to perform software
15-
package version comparisons in VulnerableCode.
13+
**univers** was born out of the need for a mostly univeral way to store version
14+
ranges and to compare two software package versions in VulnerableCode.
1615

1716
Package version ranges and version constraints are useful and essential:
1817

18+
- When relating a known vulnerability or bug to a range of affected package
19+
versions. For instance a statement such as "vulnerability 123 affects
20+
package bar, version 3.1 and version 4.2 but not version 5" defines a
21+
range of bar versions affected by a vulnerability.
22+
1923
- When resolving the dependencies of a package to express which subset of the
2024
versions are supported. For instance a dependency requirement statement such
2125
as "I require package foo, version 2.0 and later versions" defines a range of
2226
acceptable foo versions.
2327

24-
- When relating a known vulnerability or bug to a range of affected package
25-
versions. For instance a statement such as "vulnerability 123 affects
26-
package bar, version 3.1 and version 4.2 but not version 5" also defines a
27-
range of affected bar versions.
28+
Version syntaxes and range notations are quite different across ecosystems,
29+
making it is difficult to process versions and version ranges across ecosystems
30+
in a consistent way.
2831

29-
Existing tools support typically a single algorithm to parse and compare
30-
versions and this is not accurate across different ecosystems, since each
31-
follow different versioning rules. For example there's no concept of 'epoch' in
32-
semver versioning as used in package types and ecosystem such as npm or
33-
rubygems, but epochs do exist in debian versions. A tool designed for semver or
34-
dpkg versions processing would not be able to handle correctly the other version
35-
scheme.
32+
Existing tools and libraries typically support a single algorithms to parse and
33+
compare versions with a single version range notation for a single package
34+
ecosystem.
3635

37-
univers is different and considers the ecosystem-specific version scheme used.
3836

37+
**univers** is different:
3938

40-
How does univers work ?
41-
=========================
39+
- It tracks each ecosystem versionning scheme and how two versions are compared.
4240

43-
univers wraps, embeds or implements multiple version comparision libraries, each
44-
focused on specific ecosystem version scheme.
41+
- It support a growing number of package ecosystems versioning in a single
42+
library.
4543

46-
It also implements an experimental unified syntax for version ranges specifier
47-
and can parse and convert existing version range strings to this unified syntax.
44+
- It can parse version range strings using their native notation (such as an npm
45+
range) into the common "vers" notation and internal object model and can
46+
return back a native version range string rebuilt from a "vers" range.
4847

48+
- It is designed to work with `Package URLs (purl) <https://github.com/package-url>`_.
4949

50-
The supported package ecosystems versioning schemes and underlying libraries are:
5150

52-
- semver: npm, golang, PHP composer, rubygems and others that follow the semver
53-
spec, using `semantic_version <https://github.com/rbarrois/python-semanticversion>`_ library.
54-
- debian: handled by the
55-
`debian-inspector <https://github.com/sbs2001/univers/blob/main/src/univers/debian.py.ABOUT>`_
56-
library.
51+
How does **univers** work ?
52+
============================
53+
54+
**univers** wraps, embeds and implements multiple version comparison libraries,
55+
each focused on a specific ecosystem versionning scheme.
56+
57+
For each scheme, **univers** provides an implementation for:
58+
59+
- the version comparison procedure e.g, how to compare two versions,
60+
- parsing and converting from a native version range notation to the
61+
**univers** normalized and unified internal model,
62+
- converting a range back to its scheme-native range syntax and to the
63+
``vers`` syntax.
64+
65+
**univers** implements ``vers``, an experimental unified and mostly universal
66+
version range syntax. It can parse and convert an existing native version range
67+
strings to this unified syntax. For example, this means:
68+
69+
- converting ">1.2.3" as used in a Python package into ``vers:pypi/>1.2.3``,
70+
71+
- or converting "^1.0.2" as used in an npm package dependency declartion into
72+
``vers:npm/>=1.0.2,<2.0.0``
73+
74+
The supported package ecosystems versioning schemes and underlying libraries
75+
include:
76+
77+
- npm that use the "node-semver" ranges notation and the semver versions syntax
78+
This is supported in part by the `semantic_version <https://github.com/rbarrois/python-semanticversion>`_ library.
79+
5780
- pypi: handled by Python's packaging library and the standard ``packaging.version`` module.
58-
- maven: handled by the embedded `rpm_vercmp <https://github.com/sbs2001/univers/blob/main/src/univers/rpm.py.ABOUT>`_ library.
59-
- ebuild/gentoo: handled by the embedded `gentoo_vercmp <https://github.com/sbs2001/univers/blob/main/src/univers/gentoo.py.ABOUT>`_ module.
6081

61-
As we grow, new schemes will be implemented accordingly.
82+
- Rubygems which use a semver-like but not-quite-semver scheme and there can be
83+
commonly more than three version segments.
84+
Gems also use a slightly different range notation from node-semver with
85+
different operators and slightly different semantics: for instance it uses "~>"
86+
as a pessimistic operator and supports exclusion with != and does not support
87+
"OR" between constraints (that it call requirements).
88+
89+
- debian: handled by the `debian-inspector <https://github.com/nexB/univers/blob/main/src/univers/debian.py.ABOUT>`_
90+
library.
91+
92+
- maven: handled by the embedded `pymaven <https://github.com/nexB/univers/blob/main/src/univers/pymaven.py.ABOUT>`_ library.
93+
94+
- rpm: handled by the embedded `rpm_vercmp <https://github.com/nexB/univers/blob/main/src/univers/rpm.py.ABOUT>`_ library.
95+
96+
- golang (using semver)
97+
98+
- PHP composer
99+
100+
- ebuild/gentoo: handled by the embedded `gentoo_vercmp <https://github.com/nexB/univers/blob/main/src/univers/gentoo.py.ABOUT>`_ module.
101+
102+
- arch linux : handled by the embedded `arch utility borrowed from msys2 <https://github.com/nexB/univers/blob/main/src/univers/arch.py.ABOUT>`_ module.
103+
104+
The level of support for each ecosystem may not be even for now and new schemes
105+
and support for more package types are implemented on a continuous basis.
62106

63107

64108
Alternative
65109
============
66110

67111
Rather than using ecosystem-specific version schemes and code, another approach
68112
is to use a single procedure for all the versions as implemented in `libversion
69-
<https://github.com/repology/libversion>`_. This works in the most common case
70-
but may not work correctly for specific tasks that demand accurate version
71-
comparison such as for dependency resolution and vulnerabilities checks.
113+
<https://github.com/repology/libversion>`_. ``libversion`` works in the most
114+
common case but may not work correctly when a task that demand precise version
115+
comparisons such as for dependency resolution and vulnerability lookup where
116+
a "good enough" comparison accuracy is not acceptable. ``libversion`` does not
117+
handle version range notations.
72118

73119

74120
Installation
@@ -80,35 +126,40 @@ Installation
80126
Examples
81127
========
82128

83-
Compare two versions using the Python comparison operators:
129+
Compare two native Python versions:
130+
131+
.. code:: python
132+
133+
from univers.version import PypiVersion
134+
assert PypiVersion("1.2.3") < PypiVersion("1.2.4")
135+
136+
137+
Normalize a version range from an npm:
84138

85139
.. code:: python
86140
87-
from univers.version import PYPIVersion
88-
v1 = PYPIVersion("1.2.3")
89-
v2 = PYPIVersion("1.2.4")
90-
assert v1 < v2 == True
141+
from univers.version_range import NpmVersionRange
142+
range = NpmVersionRange.from_native("^1.0.2")
143+
assert str(range) == "vers:npm/>=1.0.2,<2.0.0"
91144
92145
93-
Test if a version is within or outside of a version range:
146+
Test if a version is within or outside a version range:
94147

95148
.. code:: python
96149
97-
from univers.version import PYPIVersion
98-
from univers.version_specifier import VersionSpecifier
150+
from univers.version import PypiVersion
151+
from univers.version_range import VersionRange
99152
100-
vs = VersionSpecifier.from_scheme_version_spec_string("pypi", ">=1.2.4")
101-
v1 = PYPIVersion("1.2.4")
102-
v2 = PYPIVersion("1.2.3")
153+
range = VersionRange.from_string("vers:pypi/>=1.2.4")
103154
104-
assert (v1 in vs ) == True
105-
assert (v2 in vs ) == False
155+
assert PypiVersion("1.2.4") in range
156+
assert PypiVersion("1.2.3") not in range
106157
107158
108159
Development
109160
============
110161

111-
Starting from a git clone of https://github.com/nexB/univers run these::
162+
Run these commands, starting from a git clone of https://github.com/nexB/univers ::
112163

113164
$ configure --dev
114165
$ source venv/bin/active
@@ -121,5 +172,6 @@ Visit https://github.com/nexB/univers and
121172
https://gitter.im/aboutcode-org/vulnerablecode and
122173
https://gitter.im/aboutcode-org/aboutcode for support and chat.
123174

175+
124176
Primary license: Apache-2.0
125177
SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause AND MIT

conftest.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,6 @@
22
# Copyright (c) nexB Inc. and others.
33
# SPDX-License-Identifier: Apache-2.0
44
#
5-
# Visit https://aboutcode.org and https://github.com/nexB/ for support and download.
6-
#
7-
# Licensed under the Apache License, Version 2.0 (the "License");
8-
# you may not use this file except in compliance with the License.
9-
# You may obtain a copy of the License at
10-
#
11-
# http://www.apache.org/licenses/LICENSE-2.0
12-
#
13-
# Unless required by applicable law or agreed to in writing, software
14-
# distributed under the License is distributed on an "AS IS" BASIS,
15-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16-
# See the License for the specific language governing permissions and
17-
# limitations under the License.
5+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
186

197
collect_ignore = ["setup.py"]

src/univers/arch.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#
2-
# Copyright 2016-2020 Christoph Reiter
2+
# Copyright (c) Christoph Reiter
33
# SPDX-License-Identifier: MIT
4-
# Version comparision utility extracted from msys2 and further stripped down.
4+
# Version utility extracted from msys2 https://github.com/msys2/msys2-web/
5+
# and further stripped down.
6+
#
7+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
58

69
import re
710
from itertools import zip_longest

src/univers/arch.py.ABOUT

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ license_expression: MIT
77
homepage_url: https://github.com/msys2/msys2-web/
88

99
notes: |
10-
The version comparision utility is extracted from msys2 and further stripped down.
10+
The version comparison utility is extracted from msys2 and further stripped down.
1111

1212
notice_file: arch.py.NOTICE

src/univers/debian.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#
22
# Copyright (c) nexB Inc. and others.
3-
# Exatrcted from http://nexb.com and https://github.com/nexB/debian_inspector/
3+
# Extracted from http://nexb.com and https://github.com/nexB/debian_inspector/
44
# Copyright (c) Peter Odding
55
# Author: Peter Odding <peter@peterodding.com>
66
# URL: https://github.com/xolox/python-deb-pkg-tools
77
# SPDX-License-Identifier: MIT
8+
#
9+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
810

911
import logging
1012
import operator as operator_module

src/univers/gentoo.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
#
22
# Copyright (c) 2006-2019, pkgcore contributors
33
# SPDX-License-Identifier: BSD-3-Clause
4-
# Version comparision utility extracted from pkgcore and further stripped down.
4+
# Version comparison utility extracted from pkgcore and further stripped down.
5+
#
6+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
57

68
import re
79

810
from univers.utils import cmp
11+
from univers.utils import remove_spaces
912

13+
_is_gentoo_version = re.compile(
14+
r"^(?:\d+)(?:\.\d+)*[a-zA-Z]?(?:_(p(?:re)?|beta|alpha|rc)\d*)*$"
15+
).match
1016

1117
suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
1218

@@ -19,6 +25,11 @@
1925
"""
2026

2127

28+
def is_valid(string):
29+
version, _ = parse_version_and_revision(remove_spaces(string))
30+
return _is_gentoo_version(version)
31+
32+
2233
def parse_version_and_revision(version_string):
2334
"""
2435
Return a tuple of (version string, revision int) given a ``version_string``.

src/univers/gentoo.py.ABOUT

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ copyright: |
66
license_expression: BSD-3-Clause
77
homepage_url: https://github.com/pkgcore/pkgcore/blob/master/src/pkgcore/ebuild/cpv.py
88

9-
notes: The version comparision utility is extracted from pkgcore and further stripped down.
9+
notes: The version comparison utility is extracted from pkgcore and further stripped down.
1010

1111
notice_file: gentoo.py.NOTICE

src/univers/maven.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#
22
# Copyright (c) SAS Institute Inc.
33
# SPDX-License-Identifier: Apache-2.0
4-
# Version comparision utility extracted from pymaven and further stripped down
4+
# Version comparison utility extracted from pymaven and further stripped down
55
# and significantly modified from the original at pymaven
6+
#
7+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
68

79
import functools
810
from itertools import zip_longest

src/univers/rpm.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#
22
# Copyright (c) SAS Institute Inc.
33
# SPDX-License-Identifier: Apache-2.0
4-
# Version comparision utility extracted from python-rpm-vercmp and further
4+
# Version comparison utility extracted from python-rpm-vercmp and further
55
# stripped down and significantly modified from the original at python-rpm-vercmp
6+
#
7+
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.
68

79
import re
810

911

10-
class Vercmp(object):
12+
class Vercmp:
1113
R_NONALNUMTILDE = re.compile(br"^([^a-zA-Z0-9~]*)(.*)$")
1214
R_NUM = re.compile(br"^([\d]+)(.*)$")
1315
R_ALPHA = re.compile(br"^([a-zA-Z]+)(.*)$")

0 commit comments

Comments
 (0)