mirror of
https://github.com/rpm-software-management/tito.git
synced 2025-02-23 20:22:46 +00:00
Refactor upstream patching to strategy.
Will allow projects to swap in more advanced strategies for patching their upstream projects. Also renaming SatelliteBuilder to UpstreamBuilder.
This commit is contained in:
parent
784698e46f
commit
e0f3e82ea3
5 changed files with 169 additions and 105 deletions
|
@ -15,7 +15,6 @@ Code for building Spacewalk/Satellite tarballs, srpms, and rpms.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
import commands
|
import commands
|
||||||
|
|
||||||
|
@ -23,6 +22,7 @@ from tito.common import (debug, run_command, error_out, find_git_root,
|
||||||
create_tgz, get_build_commit, find_spec_file, get_script_path,
|
create_tgz, get_build_commit, find_spec_file, get_script_path,
|
||||||
get_relative_project_dir, check_tag_exists,
|
get_relative_project_dir, check_tag_exists,
|
||||||
get_commit_count, get_latest_commit)
|
get_commit_count, get_latest_commit)
|
||||||
|
from tito.common import get_class_by_name
|
||||||
|
|
||||||
DEFAULT_KOJI_OPTS = "build --nowait"
|
DEFAULT_KOJI_OPTS = "build --nowait"
|
||||||
DEFAULT_CVS_BUILD_DIR = "cvswork"
|
DEFAULT_CVS_BUILD_DIR = "cvswork"
|
||||||
|
@ -583,6 +583,8 @@ class Builder(object):
|
||||||
commands.getoutput("rm -rf %s" % self.rpmbuild_dir)
|
commands.getoutput("rm -rf %s" % self.rpmbuild_dir)
|
||||||
debug("Cleaning up [%s]" % self.cvs_package_workdir)
|
debug("Cleaning up [%s]" % self.cvs_package_workdir)
|
||||||
run_command("rm -rf %s" % self.cvs_package_workdir)
|
run_command("rm -rf %s" % self.cvs_package_workdir)
|
||||||
|
else:
|
||||||
|
print("Leaving rpmbuild files in: %s" % self.rpmbuild_dir)
|
||||||
|
|
||||||
def _create_build_dirs(self):
|
def _create_build_dirs(self):
|
||||||
"""
|
"""
|
||||||
|
@ -814,11 +816,11 @@ class CvsBuilder(NoTgzBuilder):
|
||||||
return rpms
|
return rpms
|
||||||
|
|
||||||
|
|
||||||
class SatelliteBuilder(NoTgzBuilder):
|
class UpstreamBuilder(NoTgzBuilder):
|
||||||
"""
|
"""
|
||||||
Builder for packages that are based off some upstream version in Spacewalk
|
Builder for packages that are based off an upstream git tag.
|
||||||
git. Commits applied in Satellite git become patches applied to the
|
Commits applied in downstream git become patches applied to the
|
||||||
upstream Spacewalk tarball.
|
upstream tarball.
|
||||||
|
|
||||||
i.e. satellite-java-0.4.0-5 built from spacewalk-java-0.4.0-1 and any
|
i.e. satellite-java-0.4.0-5 built from spacewalk-java-0.4.0-1 and any
|
||||||
patches applied in satellite git.
|
patches applied in satellite git.
|
||||||
|
@ -845,8 +847,6 @@ class SatelliteBuilder(NoTgzBuilder):
|
||||||
# Need to assign these after we've exported a copy of the spec file:
|
# Need to assign these after we've exported a copy of the spec file:
|
||||||
self.upstream_version = None
|
self.upstream_version = None
|
||||||
self.upstream_tag = None
|
self.upstream_tag = None
|
||||||
self.patch_filename = None
|
|
||||||
self.patch_file = None
|
|
||||||
|
|
||||||
# When syncing files with CVS, only copy files with these extensions:
|
# When syncing files with CVS, only copy files with these extensions:
|
||||||
self.cvs_copy_extensions = (".spec", ".patch")
|
self.cvs_copy_extensions = (".spec", ".patch")
|
||||||
|
@ -899,80 +899,15 @@ class SatelliteBuilder(NoTgzBuilder):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._generate_patches()
|
self._generate_patches()
|
||||||
self._insert_patches_into_spec_file()
|
|
||||||
|
|
||||||
def _generate_patches(self):
|
def _generate_patches(self):
|
||||||
"""
|
"""
|
||||||
Generate patches for any differences between our tag and the
|
Generate patches for any differences between our tag and the
|
||||||
upstream tag.
|
upstream tag.
|
||||||
"""
|
"""
|
||||||
self.patch_filename = "%s-to-%s-%s.patch" % (self.upstream_tag,
|
from tito.strategy.patcher import DefaultPatcher
|
||||||
self.project_name, self.build_version)
|
patcher = DefaultPatcher(builder=self)
|
||||||
self.patch_file = os.path.join(self.rpmbuild_gitcopy,
|
patcher.run()
|
||||||
self.patch_filename)
|
|
||||||
os.chdir(os.path.join(self.git_root, self.relative_project_dir))
|
|
||||||
print("Generating patch [%s]" % self.patch_filename)
|
|
||||||
debug("Patch: %s" % self.patch_file)
|
|
||||||
patch_command = "git diff --relative %s..%s > %s" % \
|
|
||||||
(self.upstream_tag, self.git_commit_id, self.patch_file)
|
|
||||||
debug("Generating patch with: %s" % patch_command)
|
|
||||||
output = run_command(patch_command)
|
|
||||||
print(output)
|
|
||||||
# Creating two copies of the patch here in the temp build directories
|
|
||||||
# just out of laziness. Some builders need sources in SOURCES and
|
|
||||||
# others need them in the git copy. Being lazy here avoids one-off
|
|
||||||
# hacks and both copies get cleaned up anyhow.
|
|
||||||
run_command("cp %s %s" % (self.patch_file, self.rpmbuild_sourcedir))
|
|
||||||
|
|
||||||
def _insert_patches_into_spec_file(self):
|
|
||||||
"""
|
|
||||||
Insert the generated patches into the copy of the spec file we'll be
|
|
||||||
building with.
|
|
||||||
"""
|
|
||||||
f = open(self.spec_file, 'r')
|
|
||||||
lines = f.readlines()
|
|
||||||
|
|
||||||
patch_pattern = re.compile('^Patch(\d+):')
|
|
||||||
source_pattern = re.compile('^Source\d+:')
|
|
||||||
|
|
||||||
# Find the largest PatchX: line, or failing that SourceX:
|
|
||||||
patch_number = 0 # What number should we use for our PatchX line
|
|
||||||
patch_insert_index = 0 # Where to insert our PatchX line in the list
|
|
||||||
patch_apply_index = 0 # Where to insert our %patchX line in the list
|
|
||||||
array_index = 0 # Current index in the array
|
|
||||||
for line in lines:
|
|
||||||
match = source_pattern.match(line)
|
|
||||||
if match:
|
|
||||||
patch_insert_index = array_index + 1
|
|
||||||
|
|
||||||
match = patch_pattern.match(line)
|
|
||||||
if match:
|
|
||||||
patch_insert_index = array_index + 1
|
|
||||||
patch_number = int(match.group(1)) + 1
|
|
||||||
|
|
||||||
if line.startswith("%prep"):
|
|
||||||
# We'll apply patch right after prep if there's no %setup line
|
|
||||||
patch_apply_index = array_index + 2
|
|
||||||
elif line.startswith("%setup"):
|
|
||||||
patch_apply_index = array_index + 2 # already added a line
|
|
||||||
|
|
||||||
array_index += 1
|
|
||||||
|
|
||||||
debug("patch_insert_index = %s" % patch_insert_index)
|
|
||||||
debug("patch_apply_index = %s" % patch_apply_index)
|
|
||||||
if patch_insert_index == 0 or patch_apply_index == 0:
|
|
||||||
error_out("Unable to insert PatchX or %patchX lines in spec file")
|
|
||||||
|
|
||||||
lines.insert(patch_insert_index, "Patch%s: %s\n" % (patch_number,
|
|
||||||
self.patch_filename))
|
|
||||||
lines.insert(patch_apply_index, "%%patch%s -p1\n" % (patch_number))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
# Now write out the modified lines to the spec file copy:
|
|
||||||
f = open(self.spec_file, 'w')
|
|
||||||
for line in lines:
|
|
||||||
f.write(line)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def _get_upstream_version(self):
|
def _get_upstream_version(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1008,3 +943,9 @@ class SatelliteBuilder(NoTgzBuilder):
|
||||||
'--define "_srcrpmdir %s" --define "_rpmdir %s" ' % (
|
'--define "_srcrpmdir %s" --define "_rpmdir %s" ' % (
|
||||||
self.rpmbuild_sourcedir, self.rpmbuild_builddir,
|
self.rpmbuild_sourcedir, self.rpmbuild_builddir,
|
||||||
self.rpmbuild_basedir, self.rpmbuild_basedir))
|
self.rpmbuild_basedir, self.rpmbuild_basedir))
|
||||||
|
|
||||||
|
|
||||||
|
# Legacy class name for backward compatability:
|
||||||
|
class SatelliteBuilder(UpstreamBuilder):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import ConfigParser
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
from tito.common import DEFAULT_BUILD_DIR
|
from tito.common import DEFAULT_BUILD_DIR
|
||||||
from tito.common import (find_git_root, run_command,
|
from tito.common import (find_git_root, run_command, get_class_by_name,
|
||||||
error_out, debug, get_project_name, get_relative_project_dir,
|
error_out, debug, get_project_name, get_relative_project_dir,
|
||||||
check_tag_exists, get_latest_tagged_version, normalize_class_name)
|
check_tag_exists, get_latest_tagged_version, normalize_class_name)
|
||||||
|
|
||||||
|
@ -45,34 +45,6 @@ tagger = tito.tagger.ReleaseTagger
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def get_class_by_name(name):
|
|
||||||
"""
|
|
||||||
Get a Python class specified by it's fully qualified name.
|
|
||||||
|
|
||||||
NOTE: Does not actually create an instance of the object, only returns
|
|
||||||
a Class object.
|
|
||||||
"""
|
|
||||||
name = normalize_class_name(name)
|
|
||||||
# Split name into module and class name:
|
|
||||||
tokens = name.split(".")
|
|
||||||
class_name = tokens[-1]
|
|
||||||
module = ""
|
|
||||||
|
|
||||||
for s in tokens[0:-1]:
|
|
||||||
if module:
|
|
||||||
module = module + "."
|
|
||||||
module = module + s
|
|
||||||
|
|
||||||
mod = __import__(tokens[0])
|
|
||||||
components = name.split('.')
|
|
||||||
for comp in components[1:-1]:
|
|
||||||
mod = getattr(mod, comp)
|
|
||||||
|
|
||||||
debug("Importing %s" % name)
|
|
||||||
c = getattr(mod, class_name)
|
|
||||||
return c
|
|
||||||
|
|
||||||
|
|
||||||
def read_user_config():
|
def read_user_config():
|
||||||
config = {}
|
config = {}
|
||||||
file_loc = os.path.expanduser("~/.spacewalk-build-rc")
|
file_loc = os.path.expanduser("~/.spacewalk-build-rc")
|
||||||
|
|
|
@ -60,7 +60,7 @@ def find_git_root():
|
||||||
error_out(["%s does not appear to be within a git checkout." % \
|
error_out(["%s does not appear to be within a git checkout." % \
|
||||||
os.getcwd()])
|
os.getcwd()])
|
||||||
|
|
||||||
if cdup == "":
|
if cdup.strip() == "":
|
||||||
cdup = "./"
|
cdup = "./"
|
||||||
return os.path.abspath(cdup)
|
return os.path.abspath(cdup)
|
||||||
|
|
||||||
|
@ -294,3 +294,33 @@ def get_script_path(scriptname):
|
||||||
bin_dir = os.environ['TITO_SRC_BIN_DIR']
|
bin_dir = os.environ['TITO_SRC_BIN_DIR']
|
||||||
scriptpath = os.path.join(bin_dir, scriptname)
|
scriptpath = os.path.join(bin_dir, scriptname)
|
||||||
return scriptpath
|
return scriptpath
|
||||||
|
|
||||||
|
|
||||||
|
def get_class_by_name(name):
|
||||||
|
"""
|
||||||
|
Get a Python class specified by it's fully qualified name.
|
||||||
|
|
||||||
|
NOTE: Does not actually create an instance of the object, only returns
|
||||||
|
a Class object.
|
||||||
|
"""
|
||||||
|
name = normalize_class_name(name)
|
||||||
|
# Split name into module and class name:
|
||||||
|
tokens = name.split(".")
|
||||||
|
class_name = tokens[-1]
|
||||||
|
module = ""
|
||||||
|
|
||||||
|
for s in tokens[0:-1]:
|
||||||
|
if module:
|
||||||
|
module = module + "."
|
||||||
|
module = module + s
|
||||||
|
|
||||||
|
mod = __import__(tokens[0])
|
||||||
|
components = name.split('.')
|
||||||
|
for comp in components[1:-1]:
|
||||||
|
mod = getattr(mod, comp)
|
||||||
|
|
||||||
|
debug("Importing %s" % name)
|
||||||
|
c = getattr(mod, class_name)
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
|
15
src/tito/strategy/__init__.py
Normal file
15
src/tito/strategy/__init__.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2008-2009 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed to you under the GNU General Public License,
|
||||||
|
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
|
||||||
|
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
|
||||||
|
# along with this software; if not, see
|
||||||
|
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
|
||||||
|
#
|
||||||
|
# Red Hat trademarks are not licensed under GPLv2. No permission is
|
||||||
|
# granted to use or replicate Red Hat trademarks that are incorporated
|
||||||
|
# in this software or its documentation.
|
||||||
|
"""
|
||||||
|
Tito Strategies
|
||||||
|
"""
|
106
src/tito/strategy/patcher.py
Normal file
106
src/tito/strategy/patcher.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
# Copyright (c) 2008-2010 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This software is licensed to you under the GNU General Public License,
|
||||||
|
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
|
||||||
|
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
|
||||||
|
# along with this software; if not, see
|
||||||
|
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
|
||||||
|
#
|
||||||
|
# Red Hat trademarks are not licensed under GPLv2. No permission is
|
||||||
|
# granted to use or replicate Red Hat trademarks that are incorporated
|
||||||
|
# in this software or its documentation.
|
||||||
|
"""
|
||||||
|
Tito strategies for patching upstream projects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from tito.common import (debug, run_command)
|
||||||
|
|
||||||
|
class DefaultPatcher(object):
|
||||||
|
"""
|
||||||
|
Default Patcher
|
||||||
|
|
||||||
|
Simple generates a single patch by diffing the upstream git tag with the
|
||||||
|
tag being build.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, builder):
|
||||||
|
self.builder = builder
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
patch_filename = "%s-to-%s-%s.patch" % (self.builder.upstream_tag,
|
||||||
|
self.builder.project_name, self.builder.build_version)
|
||||||
|
patch_file = os.path.join(self.builder.rpmbuild_gitcopy,
|
||||||
|
patch_filename)
|
||||||
|
patch_dir = self.builder.git_root
|
||||||
|
if self.builder.relative_project_dir != "/":
|
||||||
|
patch_dir = os.path.join(self.builder.git_root,
|
||||||
|
self.builder.relative_project_dir)
|
||||||
|
os.chdir(patch_dir)
|
||||||
|
debug("patch dir = %s" % patch_dir)
|
||||||
|
print("Generating patch [%s]" % patch_filename)
|
||||||
|
debug("Patch: %s" % patch_file)
|
||||||
|
patch_command = "git diff --relative %s..%s > %s" % \
|
||||||
|
(self.builder.upstream_tag, self.builder.git_commit_id,
|
||||||
|
patch_file)
|
||||||
|
debug("Generating patch with: %s" % patch_command)
|
||||||
|
output = run_command(patch_command)
|
||||||
|
print(output)
|
||||||
|
# Creating two copies of the patch here in the temp build directories
|
||||||
|
# just out of laziness. Some builders need sources in SOURCES and
|
||||||
|
# others need them in the git copy. Being lazy here avoids one-off
|
||||||
|
# hacks and both copies get cleaned up anyhow.
|
||||||
|
run_command("cp %s %s" % (patch_file, self.builder.rpmbuild_sourcedir))
|
||||||
|
|
||||||
|
# Insert patches into the spec file we'll be building:
|
||||||
|
f = open(self.builder.spec_file, 'r')
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
patch_pattern = re.compile('^Patch(\d+):')
|
||||||
|
source_pattern = re.compile('^Source\d+:')
|
||||||
|
|
||||||
|
# Find the largest PatchX: line, or failing that SourceX:
|
||||||
|
patch_number = 0 # What number should we use for our PatchX line
|
||||||
|
patch_insert_index = 0 # Where to insert our PatchX line in the list
|
||||||
|
patch_apply_index = 0 # Where to insert our %patchX line in the list
|
||||||
|
array_index = 0 # Current index in the array
|
||||||
|
for line in lines:
|
||||||
|
match = source_pattern.match(line)
|
||||||
|
if match:
|
||||||
|
patch_insert_index = array_index + 1
|
||||||
|
|
||||||
|
match = patch_pattern.match(line)
|
||||||
|
if match:
|
||||||
|
patch_insert_index = array_index + 1
|
||||||
|
patch_number = int(match.group(1)) + 1
|
||||||
|
|
||||||
|
if line.startswith("%prep"):
|
||||||
|
# We'll apply patch right after prep if there's no %setup line
|
||||||
|
patch_apply_index = array_index + 2
|
||||||
|
elif line.startswith("%setup"):
|
||||||
|
patch_apply_index = array_index + 2 # already added a line
|
||||||
|
|
||||||
|
array_index += 1
|
||||||
|
|
||||||
|
debug("patch_insert_index = %s" % patch_insert_index)
|
||||||
|
debug("patch_apply_index = %s" % patch_apply_index)
|
||||||
|
if patch_insert_index == 0 or patch_apply_index == 0:
|
||||||
|
error_out("Unable to insert PatchX or %patchX lines in spec file")
|
||||||
|
|
||||||
|
lines.insert(patch_insert_index, "Patch%s: %s\n" % (patch_number,
|
||||||
|
patch_filename))
|
||||||
|
lines.insert(patch_apply_index, "%%patch%s -p1\n" % (patch_number))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# Now write out the modified lines to the spec file copy:
|
||||||
|
f = open(self.builder.spec_file, 'w')
|
||||||
|
for line in lines:
|
||||||
|
f.write(line)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue