Merge "Add new context functions for version handling"

This commit is contained in:
Jenkins 2016-11-29 14:37:26 +00:00 committed by Gerrit Code Review
commit abc85adaa3
3 changed files with 218 additions and 0 deletions

View File

@ -76,6 +76,57 @@ package name. Using multiple files looks like this::
.. _PEP0508: https://www.python.org/dev/peps/pep-0508/
.. _global-requirements.txt: https://git.openstack.org/cgit/openstack/requirements/tree/global-requirements.txt
Handling the package version
****************************
Distributions handle versions, especially pre-release versions differently.
SUSE for example allows using RPM's tilde ('~) while Fedora doesn't allow that
and uses a combination of RPM `Version` and `Release` tag to express pre-releases.
To support both styles with renderspec, the upstream version and a release
must be available in the context::
{% set upstream_version = '1.2.3.0rc1' %}
{% set rpm_release = '1' %}
This should be done on the first lines in the spec.j2 template. The `rpm_release` is
only used in the fedora style.
Then for the RPM version and release, use::
Version: {{ py2rpmversion() }}
Release: {{ py2rpmrelease() }}
For suse-style, this renders to::
Version: 1.2.3.0~rc1
Release: 0
For fedora-style, this renders to::
Version: 1.2.3
Release: 0.1.0rc1%{?dist}
Note that in case of pre-releases you may need to adjust the version that is used
in the `Source` tag and the `%prep` sections `%setup`. So use e.g. ::
{% set upstream_version = '1.2.3.0rc1' %}
{% set rpm_release = '1' %}
%name oslo.config
Version: {{ py2rpmversion() }}
Release: {{ py2rpmrelease() }}
Source0: https://pypi.io/packages/source/o/%{sname}/%{sname}-{{ upstream_version }}.tar.gz
%prep
%setup -q -n %{sname}-{{upstream_version}}
which would render (with suse-style) to::
%name oslo.config
Version: 1.2.3.0~rc1
Release: 0
Source0: https://pypi.io/packages/source/o/%{sname}/%{sname}-1.2.3rc1.tar.gz
%prep
%setup -q -n %{sname}-1.2.3.0rc1
Template features
=================
@ -164,6 +215,47 @@ With the `suse` spec-style::
License: Apache-2.0
context function `py2rpmversion`
********************************
Python has a semantic version schema (see `PEP0440`_) and converting Python versions
to RPM compatible versions is needed in some cases. For example, in the Python world
the version "1.1.0a3" is lower than "1.1.0" but for RPM the version is higher.
To transform a Python version to a RPM compatible version, use::
{% set upstream_version = '1.1.0a3' %}
{% set rpm_release = '1' %}
Version: {{ py2rpmversion() }}
With the `suse` spec-style it will be translated to::
Version: 1.1.0~xalpha3
Note that you need to set 2 context variables (`upstream_version` and `rpm_release`)
to be able to use the `py2rpmversion()` function.
context function `py2rpmrelease`
********************************
Fedora doesn't allow the usage of `~` (tilde) in the `Version` tag. So for pre-releases
the `Release` tag is used (see `Fedora Packaging Versioning`_)
For the fedora-style::
{% set upstream_version = '1.1.0a3' %}
{% set rpm_release = '1' %}
Version: {{ py2rpmversion() }}
Release: {{ py2rpmrelease() }}
this would render to::
Version: 1.1.0
Release: 0.1a3%{?dist}
Note that you need to set 2 context variables (`upstream_version` and `rpm_release`)
to be able to use the `py2rpmrelease()` function.
distribution specific blocks & child templates
**********************************************
@ -199,3 +291,5 @@ For more information, see current `renderspec/dist-templates` and usage in
.. _pymod2pkg: https://git.openstack.org/cgit/openstack/pymod2pkg
.. _pypi.python.org: https://pypi.python.org/pypi
.. _SPDX: https://spdx.org/licenses/
.. _PEP0440: https://www.python.org/dev/peps/pep-0440/
.. _Fedora Packaging Versioning: https://fedoraproject.org/wiki/Packaging:Versioning#Pre-Release_packages

View File

@ -24,6 +24,8 @@ import sys
from jinja2 import contextfilter
from jinja2 import contextfunction
from jinja2 import Environment
from jinja2.exceptions import TemplateRuntimeError
from packaging.version import parse
import pymod2pkg
import yaml
@ -32,6 +34,66 @@ from renderspec.distloader import RenderspecLoader
from renderspec import versions
# a variable that needs to be set for some functions in the context
CONTEXT_VAR_UPSTREAM_VERSION = "upstream_version"
CONTEXT_VAR_RPM_RELEASE = "rpm_release"
def _context_check_variable(context, var_name, needed_by):
"""check that the context has a given variable"""
if var_name not in context.vars:
raise TemplateRuntimeError("Variable '%s' not available in context but"
" needed for '%s'" % (var_name, needed_by))
def _context_py2rpmversion(context):
"""get a python PEP0440 compatible version and translate it to an RPM
version"""
# the context needs a variable set via {% set upstream_version = 'ver' %}
_context_check_variable(context, CONTEXT_VAR_UPSTREAM_VERSION,
'py2rpmversion')
version = context.vars[CONTEXT_VAR_UPSTREAM_VERSION]
v_python = parse(version)
# fedora does not allow '~' in versions but uses a combination of Version
# and Release
# https://fedoraproject.org/wiki/Packaging:Versioning\#Pre-Release_packages
if context['spec_style'] == 'fedora':
return v_python.base_version
else:
v_rpm = v_python.public
if v_python.is_prerelease:
# we need to add the 'x' in front of alpha/beta releases because
# in the python world, "1.1a10" > "1.1.dev10"
# but in the rpm world, "1.1~a10" < "1.1~dev10"
v_rpm = v_rpm.replace('a', '~xalpha')
v_rpm = v_rpm.replace('b', '~xbeta')
v_rpm = v_rpm.replace('rc', '~rc')
v_rpm = v_rpm.replace('.dev', '~dev')
return v_rpm
def _context_py2rpmrelease(context):
if context['spec_style'] == 'fedora':
# the context needs a var set via {% set upstream_version = 'ver' %}
_context_check_variable(context, CONTEXT_VAR_UPSTREAM_VERSION,
'py2rpmrelease')
# the context needs a var set via {% set rpm_release = 'ver' %}
_context_check_variable(context, CONTEXT_VAR_RPM_RELEASE,
'py2rpmrelease')
upstream_version = context.vars[CONTEXT_VAR_UPSTREAM_VERSION]
rpm_release = context.vars[CONTEXT_VAR_RPM_RELEASE]
v_python = parse(upstream_version)
if v_python.is_prerelease:
_, alphatag = v_python.public.split(v_python.base_version)
return '0.{}.{}%{{?dist}}'.format(rpm_release,
alphatag.lstrip('.'))
else:
return '{}%{{?dist}}'.format(rpm_release)
else:
# SUSE uses just '0'. The OpenBuildService handles the Release tag
return '0'
def _context_epoch(context, pkg_name):
"""get the epoch (or 0 if unknown) for the given pkg name"""
return context['epochs'].get(pkg_name, 0)
@ -115,6 +177,16 @@ def _globals_py2pkg(context, pkg_name, pkg_version=None):
return _context_py2pkg(context, pkg_name, pkg_version)
@contextfunction
def _globals_py2rpmversion(context):
return _context_py2rpmversion(context)
@contextfunction
def _globals_py2rpmrelease(context):
return _context_py2rpmrelease(context)
@contextfunction
def _globals_epoch(context, value):
return _context_epoch(context, value)
@ -133,6 +205,8 @@ def _globals_py2name(context, value):
def _env_register_filters_and_globals(env):
"""register all the jinja2 filters we want in the environment"""
env.filters['epoch'] = _filter_epoch
env.globals['py2rpmversion'] = _globals_py2rpmversion
env.globals['py2rpmrelease'] = _globals_py2rpmrelease
env.globals['py2pkg'] = _globals_py2pkg
env.globals['py2name'] = _globals_py2name
env.globals['epoch'] = _globals_epoch

View File

@ -185,6 +185,56 @@ class RenderspecTemplateFunctionTests(unittest.TestCase):
template.render(**context),
expected_result)
@data(
('suse', '1.1.0', '1.1.0'),
('suse', '1.1.0.post2', '1.1.0.post2'),
('suse', '1.1.0dev10', '1.1.0~dev10'),
('suse', '1.1.0a10', '1.1.0~xalpha10'),
('suse', '1.1.0a10dev5', '1.1.0~xalpha10~dev5'),
('suse', '1.1.0b10', '1.1.0~xbeta10'),
('suse', '1.1.0rc2', '1.1.0~rc2'),
('suse', '1.1.0rc2dev2', '1.1.0~rc2~dev2'),
('fedora', '1.1.0', '1.1.0'),
('fedora', '1.1.0b10', '1.1.0'),
('fedora', '1.1.0rc2dev2', '1.1.0'),
)
@unpack
def test_render_func_py2rpmversion(self, style, py_ver, rpm_ver):
context = {'spec_style': style, 'epochs': {}, 'requirements': {}}
# need to escape '{' and '}' here
s = "{{% set upstream_version = '{}' %}}{{{{ py2rpmversion() }}}}"\
.format(py_ver)
template = self.env.from_string(s)
self.assertEqual(
template.render(**context),
rpm_ver)
@data(
('suse', '1.1.0', '1', '0'),
('suse', '1.1.0.post2', '1', '0'),
('suse', '1.1.0dev10', '2', '0'),
('fedora', '1.1.0', '1', '1%{?dist}'),
# ('fedora', '1.1.0.post2', '1', 'FIXME'),
('fedora', '1.1.0dev10', '1', '0.1.dev10%{?dist}'),
('fedora', '1.1.0a10', '1', '0.1.a10%{?dist}'),
('fedora', '1.1.0a10dev5', '1', '0.1.a10.dev5%{?dist}'),
('fedora', '1.1.0b10', '1', '0.1.b10%{?dist}'),
('fedora', '1.1.0rc2', '5', '0.5.rc2%{?dist}'),
('fedora', '1.1.0rc2dev2', '1', '0.1.rc2.dev2%{?dist}'),
)
@unpack
def test_render_func_py2rpmrelease(self, style, upstream_ver, rpm_release,
rpm_release_expected):
context = {'spec_style': style, 'epochs': {}, 'requirements': {}}
# need to escape '{' and '}' here
s = "{{% set upstream_version = '{}' %}}" \
"{{% set rpm_release = '{}' %}}" \
"{{{{ py2rpmrelease() }}}}".format(upstream_ver, rpm_release)
template = self.env.from_string(s)
self.assertEqual(
template.render(**context),
rpm_release_expected)
class RenderspecVersionsTests(unittest.TestCase):
def test_without_version(self):