Merge the FiledVersionTagger into the base VersionTagger.

This allows the behaviour to be used in just about any tagger sub-class.

Behaviour will be triggered on the presence of the 'version_template'
section in tito.props.

Renamed config:
  version -> version_template (section)
  file -> destination_file

Separated the replacement of version and release, as caller may just
want one or the other. (it works fine if only one of the variables is
present in your template file) Release is more of an rpm concept so may
not always be wanted in this context.

Removed the notion of default template files, if you want to use this
you need to specify the configuration for it.

Added a functional test to make sure I keep it working.
This commit is contained in:
Devan Goodwin 2013-11-12 12:16:12 -04:00
parent f695047a5c
commit a3363cb93f
5 changed files with 150 additions and 114 deletions

View file

@ -47,7 +47,7 @@ class VersionTagger(ConfigObject):
"""
def __init__(self, global_config=None, keep_version=False, offline=False, user_config=None, pkg_config=None):
"""
"""
pkg_config - Package specific configuration.
global_config - Global configuration from rel-eng/tito.props.
@ -303,6 +303,8 @@ class VersionTagger(ConfigObject):
"""
If this project has a setup.py, attempt to update it's version.
"""
self._update_version_file(new_version)
setup_file = os.path.join(self.full_project_dir, "setup.py")
if not os.path.exists(setup_file):
return
@ -537,6 +539,62 @@ class VersionTagger(ConfigObject):
suffix = self.config.get("globalconfig", "tag_suffix")
return "%s-%s%s" % (self.project_name, new_version, suffix)
def _update_version_file (self, new_version):
"""
land this new_version in the designated file
and stages that file for a git commit
"""
version_file = self._version_file_path()
if not version_file:
debug("No destination version file found, skipping.")
return
debug("Found version file to write: %s" % version_file)
version_file_template = self._version_file_template()
if version_file_template is None:
error_out("Version file specified but without corresponding template.")
t = Template(version_file_template)
f = open(version_file, 'w')
(new_ver, new_rel) = new_version.split('-')
f.write(t.safe_substitute(
version = new_ver,
release = new_rel))
f.close()
run_command("git add %s" % version_file)
def _version_file_template (self):
"""
"$version_name = $version"
or provide a configuration in tito.props to a file that is a
python string.Template conforming blob, like
[version]
template_file = ./rel-eng/templates/my_java_properties
see also, http://docs.python.org/2/library/string.html#template-strings
"""
if self.config.has_option("version_template", "template_file"):
f = open(os.path.join(self.git_root,
self.config.get("version_template", "template_file")), 'r')
buf = f.read()
f.close()
return buf
return None
def _version_file_path (self):
"""
standard ${project_name}-version.conf
or provide a configuration in tito.props, like
[version]
file = ./foo.rb
"""
if self.config.has_option("version_template", "destination_file"):
return self.config.get("version_template", "destination_file")
return None
class ReleaseTagger(VersionTagger):
"""
@ -580,92 +638,4 @@ class ForceVersionTagger(VersionTagger):
self._update_setup_py(new_version)
self._update_package_metadata(new_version)
class FiledVersionTagger(VersionTagger):
"""
Default VersionTagger,
but land a file with the version of tagged
"""
# override
def _update_setup_py (self, new_version):
"""
I would rather have hooked into _tag_release(), but there
would not be easy access to the +new_version+
So we'll hook onto one of the things called that receives that argument
"""
self._update_version_file(new_version)
super(FiledVersionTagger, self)._update_setup_py(new_version)
# added
def _update_version_file (self, new_version):
"""
land this new_version in the designated file
and stages that file for a git commit
"""
version_file = self._version_file_path()
print "writing version file out to %s" % version_file
t = Template(self._version_file_template())
f = open(version_file, 'w')
f.write(t.safe_substitute(
version_name = self._version_file_variable_name(),
version = new_version
))
f.close()
run_command("git add %s" % version_file)
def _version_file_template (self):
"""
"$version_name = $version"
or provide a configuration in tito.props to a file that is a
python string.Template conforming blob, like
[version]
template_file = ./rel-eng/templates/my_java_properties
see also, http://docs.python.org/2/library/string.html#template-strings
"""
if self.config.has_option("version", "template_file"):
f = open(self.config.get("version", "template_file"), 'r')
buf = f.read()
f.close()
return buf
return "$version_name = $version"
# added
def _version_file_variable_name (self):
"""
"TAGGED_VERSION"
or provide a configuration in tito.props, like
[version]
variable_name = MY_TAGGED_VERSION
"""
if self.config.has_option("version", "variable_name"):
return self.config.get("version", "variable_name")
return "TAGGED_VERSION"
# added
def _version_file_path (self):
"""
standard ${project_name}-version.conf
or provide a configuration in tito.props, like
[version]
file = ./foo.rb
"""
if self.config.has_option("version", "file"):
return self.config.get("version", "file")
return "%s-version.conf" % (self.project_name)
# added: helper for relative path of this package
def _path(self):
if self.relative_project_dir != None: return self.relative_project_dir
return self.git_root

View file

@ -128,6 +128,12 @@ class TitoGitTestFixture(unittest.TestCase):
index.add(['rel-eng/tito.props'])
index.commit('Setting offline.')
def write_file(self, path, contents):
print path
out_f = open(path, 'w')
out_f.write(contents)
out_f.close()
def create_project(self, pkg_name, pkg_dir=''):
"""
Create a test project at the given location, assumed to be within

View file

@ -18,25 +18,20 @@ NOTE: These tests require a makeshift git repository created in /tmp.
"""
import os
from os.path import join
import unittest
from tito.common import check_tag_exists, commands, run_command, \
from tito.common import run_command, \
get_latest_tagged_version, tag_exists_locally
from fixture import *
# A location where we can safely create a test git repository.
# WARNING: This location will be destroyed if present.
TEST_PKG_1 = 'titotestpkg'
TEST_PKG_1_DIR = "%s/" % TEST_PKG_1
TEST_PKG_2 = 'titotestpkg2'
TEST_PKG_2_DIR = "%s/" % TEST_PKG_2
TEST_PKG_3 = 'titotestpkg3'
TEST_PKG_3_DIR = "blah/whatever/%s/" % TEST_PKG_3
TEST_PKGS = [TEST_PKG_1, TEST_PKG_2, TEST_PKG_3]
@ -46,6 +41,23 @@ def release_bumped(initial_version, new_version):
new_release = new_version.split('-')[-1]
return new_release == str(int(first_release) + 1)
TEMPLATE_TAGGER_TITO_PROPS = """
[buildconfig]
tagger = tito.tagger.VersionTagger
builder = tito.builder.Builder
[version_template]
destination_file = version.txt
template_file = rel-eng/templates/version.rb
"""
VERSION_TEMPLATE_FILE = """
module Iteng
module Util
VERSION = "$version-$release"
end
end
"""
class MultiProjectTests(TitoGitTestFixture):
@ -70,6 +82,37 @@ class MultiProjectTests(TitoGitTestFixture):
index.add(['pkg2/tito.props'])
index.commit("Adding tito.props for pkg2.")
def test_template_version_tagger(self):
"""
Make sure the template is applied and results in the correct file
being included in the tag.
"""
pkg_dir = join(self.repo_dir, 'pkg3')
filename = join(pkg_dir, "tito.props")
self.write_file(filename, TEMPLATE_TAGGER_TITO_PROPS)
run_command('mkdir -p %s' % join(self.repo_dir, 'rel-eng/templates'))
self.write_file(join(self.repo_dir,
'rel-eng/templates/version.rb'), VERSION_TEMPLATE_FILE)
index = self.repo.index
index.add(['pkg3/tito.props'])
index.commit("Adding tito.props for pkg3.")
# Create another pkg3 tag and make sure we got a generated
# template file.
os.chdir(os.path.join(self.repo_dir, 'pkg3'))
tito('tag --debug --accept-auto-changelog')
new_ver = get_latest_tagged_version(TEST_PKG_3)
self.assertEquals("0.0.2-1", new_ver)
dest_file = os.path.join(self.repo_dir, 'pkg3', "version.txt")
self.assertTrue(os.path.exists(dest_file))
f = open(dest_file, 'r')
contents = f.read()
f.close()
self.assertTrue("VERSION = \"0.0.2-1\"" in contents)
def test_initial_tag_keep_version(self):
# Tags were actually created in setup code:
for pkg_name in TEST_PKGS:

View file

@ -14,12 +14,9 @@
import os
import shutil
import unittest
import git
from tito.common import *
from fixture import TitoGitTestFixture, TEST_SPEC, tito
from fixture import TitoGitTestFixture, tito
#TITO_REPO = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
PKG_NAME = "titotestpkg"

View file

@ -21,9 +21,9 @@ Project settings can be stored in files:
`GITROOT/SOME/PACKAGE/tito.props`
The global rel-eng/tito.props is generally where settings are defined. For
some multi-project git repositories, individual packages can override these
settings by placing a tito.props in the project directory. (i.e. same location
The global rel-eng/tito.props is generally where settings are defined. For
some multi-project git repositories, individual packages can override these
settings by placing a tito.props in the project directory. (i.e. same location
as it's .spec file)
@ -39,19 +39,19 @@ default builder and tagger for you project. You can use following variables:
default_builder::
The fully qualified Builder class implementation to use.
You can either specify builders shipped with tito(5) (see BUILDERS section
below), or a custom builder located within the directory your `lib_dir` option
You can either specify builders shipped with tito(5) (see BUILDERS section
below), or a custom builder located within the directory your `lib_dir` option
points to.
default_tagger::
The fully qualified Tagger class implementation to use.
You can either specify builders shipped with tito(5) (see TAGGERS section
below), or a custom builder located within the directory your `lib_dir` option
You can either specify builders shipped with tito(5) (see TAGGERS section
below), or a custom builder located within the directory your `lib_dir` option
points to.
lib_dir::
Optional property defining a directory to be added to the Python path when
executing tito. Allows you to store custom implementations of Builder, Tagger,
Optional property defining a directory to be added to the Python path when
executing tito. Allows you to store custom implementations of Builder, Tagger,
and Releaser.
changelog_format::
@ -70,9 +70,9 @@ If set to 0, it will not remove from cherry picked commits the part "(cherry
picked from commit ...)"
tag_suffix::
An optional specification of a suffix to append to all tags created by tito
for this repo. Can be useful for situations where one git repository is
inheriting from another, but tags are created in both. The suffix will be an
An optional specification of a suffix to append to all tags created by tito
for this repo. Can be useful for situations where one git repository is
inheriting from another, but tags are created in both. The suffix will be an
indicator as to which repo the tag originated in. (i.e. tag_suffix = -mysuffix)
@ -106,6 +106,26 @@ for example:
This will run "make zstreams" in BASE directory and will then use EUS-VARIANT
branch.
VERSION_TEMPLATE
----------------
Allows the user to write out a template file containing version and/or release and add it to git during the tagging process.
template_file::
Path to a file conforming to a Python string template. Path is relative to root of the entire git checkout, as this is likely to be stored in the top level rel-eng directory.
destination_file::
Specifies a file to write, relative to the directory for the package being tagged.
Example:
----
[version_template]
destination_file = version.txt
template_file = rel-eng/templates/version.rb
----
REQUIREMENTS
------------
tito::
@ -114,11 +134,11 @@ If tito is older then specified version, it will refuse to continue.
BUILDCONFIG
-----------
builder::
This option is used in package specific tito.props only, and allows that
This option is used in package specific tito.props only, and allows that
project to override the default_builder defined in rel-eng/tito.props.
tagger::
This option is used in package specific tito.props only, and allows that
This option is used in package specific tito.props only, and allows that
project to override the default_tagger defined in rel-eng/tito.props.