Introduce upper constraints
This is a global setting that will apply across every project, a constraint on the versions of things that can be installed via pip. This is a first pass, adding basic functionality, by allowing a list of constraint URLs to be provided. Future iterations of this feature may be tied to a "superrepo" that contains all the projects _and_ the constraints for those projects all in one place. Change-Id: Ib5c2dd441b9294b775e755e595486a671d8de57f
This commit is contained in:
parent
6b40cee8bf
commit
235f6bb6f4
@ -18,6 +18,8 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from giftwrap.gerrit import GerritReview
|
from giftwrap.gerrit import GerritReview
|
||||||
from stevedore.driver import DriverManager
|
from stevedore.driver import DriverManager
|
||||||
from stevedore.extension import ExtensionManager
|
from stevedore.extension import ExtensionManager
|
||||||
@ -36,6 +38,7 @@ class Builder(object):
|
|||||||
self._temp_src_dir = None
|
self._temp_src_dir = None
|
||||||
self._spec = spec
|
self._spec = spec
|
||||||
self._thread_exit = []
|
self._thread_exit = []
|
||||||
|
self._constraints = []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def builder_names(ext_mgr=None):
|
def builder_names(ext_mgr=None):
|
||||||
@ -55,6 +58,30 @@ class Builder(object):
|
|||||||
"Error was: %s", e)
|
"Error was: %s", e)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def _get_constraints(self):
|
||||||
|
cfiles = []
|
||||||
|
try:
|
||||||
|
for count, constraint_url in \
|
||||||
|
enumerate(self._spec.settings.constraints, 1):
|
||||||
|
response = requests.get(constraint_url)
|
||||||
|
|
||||||
|
# Raise an error if we got a bad URL
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
constraints = response.text.encode('utf-8')
|
||||||
|
cfilepath = os.path.join(self._temp_dir,
|
||||||
|
'constraints-%s.txt' % count)
|
||||||
|
|
||||||
|
with open(cfilepath, 'w') as cfile:
|
||||||
|
cfile.write(constraints)
|
||||||
|
|
||||||
|
cfiles.append(cfilepath)
|
||||||
|
|
||||||
|
return cfiles
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Unable to construct constraints. Error: %s" % e)
|
||||||
|
|
||||||
def _build_project(self, project):
|
def _build_project(self, project):
|
||||||
try:
|
try:
|
||||||
self._prepare_project_build(project)
|
self._prepare_project_build(project)
|
||||||
@ -105,6 +132,9 @@ class Builder(object):
|
|||||||
self._temp_src_dir = os.path.join(self._temp_dir, 'src')
|
self._temp_src_dir = os.path.join(self._temp_dir, 'src')
|
||||||
LOG.debug("Temporary working directory: %s", self._temp_dir)
|
LOG.debug("Temporary working directory: %s", self._temp_dir)
|
||||||
|
|
||||||
|
# get constraints paths
|
||||||
|
self._constraints = self._get_constraints()
|
||||||
|
|
||||||
threads = []
|
threads = []
|
||||||
for project in spec.projects:
|
for project in spec.projects:
|
||||||
if spec.settings.parallel_build:
|
if spec.settings.parallel_build:
|
||||||
|
@ -84,8 +84,11 @@ class DockerBuilder(Builder):
|
|||||||
|
|
||||||
def _install_pip_dependencies(self, venv_path, dependencies):
|
def _install_pip_dependencies(self, venv_path, dependencies):
|
||||||
pip_path = self._get_venv_pip_path(venv_path)
|
pip_path = self._get_venv_pip_path(venv_path)
|
||||||
|
install = "install"
|
||||||
|
for constraint in self._constraints:
|
||||||
|
install = "%s -c %s" % (install, constraint)
|
||||||
for dependency in dependencies:
|
for dependency in dependencies:
|
||||||
self._execute("%s install %s" % (pip_path, dependency))
|
self._execute("%s %s %s" % (pip_path, install, dependency))
|
||||||
|
|
||||||
def _copy_sample_config(self, src_clone_dir, project):
|
def _copy_sample_config(self, src_clone_dir, project):
|
||||||
src_config = os.path.join(src_clone_dir, 'etc')
|
src_config = os.path.join(src_clone_dir, 'etc')
|
||||||
@ -96,7 +99,10 @@ class DockerBuilder(Builder):
|
|||||||
|
|
||||||
def _install_project(self, venv_path, src_clone_dir):
|
def _install_project(self, venv_path, src_clone_dir):
|
||||||
pip_path = self._get_venv_pip_path(venv_path)
|
pip_path = self._get_venv_pip_path(venv_path)
|
||||||
self._execute("%s install %s" % (pip_path, src_clone_dir))
|
install = "install"
|
||||||
|
for constraint in self._constraints:
|
||||||
|
install = "%s -c %s" % (install, constraint)
|
||||||
|
self._execute("%s %s %s" % (pip_path, install, src_clone_dir))
|
||||||
|
|
||||||
def _finalize_project_build(self, project):
|
def _finalize_project_build(self, project):
|
||||||
self._commands.append("rm -rf %s" % self._temp_dir)
|
self._commands.append("rm -rf %s" % self._temp_dir)
|
||||||
|
@ -70,8 +70,11 @@ class PackageBuilder(Builder):
|
|||||||
|
|
||||||
def _install_pip_dependencies(self, venv_path, dependencies):
|
def _install_pip_dependencies(self, venv_path, dependencies):
|
||||||
pip_path = self._get_venv_pip_path(venv_path)
|
pip_path = self._get_venv_pip_path(venv_path)
|
||||||
|
install = "install"
|
||||||
|
for constraint in self._constraints:
|
||||||
|
install = "%s -c %s" % (install, constraint)
|
||||||
for dependency in dependencies:
|
for dependency in dependencies:
|
||||||
self._execute("%s install %s" % (pip_path, dependency))
|
self._execute("%s %s %s" % (pip_path, install, dependency))
|
||||||
|
|
||||||
def _copy_sample_config(self, src_clone_dir, project):
|
def _copy_sample_config(self, src_clone_dir, project):
|
||||||
src_config = os.path.join(src_clone_dir, 'etc')
|
src_config = os.path.join(src_clone_dir, 'etc')
|
||||||
@ -87,7 +90,10 @@ class PackageBuilder(Builder):
|
|||||||
|
|
||||||
def _install_project(self, venv_path, src_clone_dir):
|
def _install_project(self, venv_path, src_clone_dir):
|
||||||
pip_path = self._get_venv_pip_path(venv_path)
|
pip_path = self._get_venv_pip_path(venv_path)
|
||||||
self._execute("%s install %s" % (pip_path, src_clone_dir))
|
install = "install"
|
||||||
|
for constraint in self._constraints:
|
||||||
|
install = "%s -c %s" % (install, constraint)
|
||||||
|
self._execute("%s %s %s" % (pip_path, install, src_clone_dir))
|
||||||
|
|
||||||
def _finalize_project_build(self, project):
|
def _finalize_project_build(self, project):
|
||||||
# build the package
|
# build the package
|
||||||
|
@ -31,9 +31,11 @@ class Settings(object):
|
|||||||
package_name_format=None, version=None,
|
package_name_format=None, version=None,
|
||||||
base_path=None, install_path=None, gerrit_dependencies=True,
|
base_path=None, install_path=None, gerrit_dependencies=True,
|
||||||
force_overwrite=False, output_dir=None, include_config=True,
|
force_overwrite=False, output_dir=None, include_config=True,
|
||||||
parallel_build=True):
|
parallel_build=True, constraints=[]):
|
||||||
if not version:
|
if not version:
|
||||||
raise Exception("'version' is a required settings")
|
raise Exception("'version' is a required settings")
|
||||||
|
if not isinstance(constraints, list):
|
||||||
|
raise Exception("'constraints' is required to be a list")
|
||||||
self.build_type = build_type
|
self.build_type = build_type
|
||||||
self._package_name_format = package_name_format
|
self._package_name_format = package_name_format
|
||||||
self.version = version
|
self.version = version
|
||||||
@ -44,6 +46,7 @@ class Settings(object):
|
|||||||
self._output_dir = output_dir
|
self._output_dir = output_dir
|
||||||
self.include_config = include_config
|
self.include_config = include_config
|
||||||
self.parallel_build = parallel_build
|
self.parallel_build = parallel_build
|
||||||
|
self.constraints = constraints
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def package_name_format(self):
|
def package_name_format(self):
|
||||||
|
@ -22,7 +22,8 @@ from giftwrap import settings
|
|||||||
SAMPLE_SETTINGS = {
|
SAMPLE_SETTINGS = {
|
||||||
'package_name_format': 'my-package-name',
|
'package_name_format': 'my-package-name',
|
||||||
'version': '1.2',
|
'version': '1.2',
|
||||||
'base_path': '/basepath'
|
'base_path': '/basepath',
|
||||||
|
'constraints': ['http://example.txt'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ class TestSettings(unittest.TestCase):
|
|||||||
self.assertEquals('my-package-name', s.package_name_format)
|
self.assertEquals('my-package-name', s.package_name_format)
|
||||||
self.assertEquals('1.2', s.version)
|
self.assertEquals('1.2', s.version)
|
||||||
self.assertEquals('/basepath', s.base_path)
|
self.assertEquals('/basepath', s.base_path)
|
||||||
|
self.assertEquals('http://example.txt', s.constraints[0])
|
||||||
|
|
||||||
def test_factory_has_default_base_path(self):
|
def test_factory_has_default_base_path(self):
|
||||||
settings_dict = {'version': 'version'}
|
settings_dict = {'version': 'version'}
|
||||||
@ -48,3 +50,10 @@ class TestSettings(unittest.TestCase):
|
|||||||
|
|
||||||
with self.assertRaises(Exception):
|
with self.assertRaises(Exception):
|
||||||
settings.Settings.factory(settings_dict)
|
settings.Settings.factory(settings_dict)
|
||||||
|
|
||||||
|
def test_factory_raises_when_constraints_invalid(self):
|
||||||
|
settings_dict = SAMPLE_SETTINGS
|
||||||
|
settings_dict['constraints'] = 'notalist'
|
||||||
|
|
||||||
|
with self.assertRaises(Exception):
|
||||||
|
settings.Settings.factory(settings_dict)
|
||||||
|
Loading…
Reference in New Issue
Block a user