Build, test and push
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import os
|
||||
import requests
|
||||
import tempfile
|
||||
import importlib.util
|
||||
|
||||
from blender.version import BlenderVersion
|
||||
|
||||
# "Wayland Protocols" has no "Library" in its name but is still a
|
||||
# build-critical dependency, so it's matched by name too.
|
||||
_EXTRA_PACKAGE_NAMES = {'wayland protocols'}
|
||||
|
||||
|
||||
class BlenderDependency:
|
||||
def __init__(self, blender: BlenderVersion):
|
||||
self._blender_version = blender
|
||||
self._url = rf'https://projects.blender.org/blender/blender/raw/tag/v{self._blender_version.tag_name()}/build_files/build_environment/install_linux_packages.py'
|
||||
self.module = self._get_source_file()
|
||||
|
||||
self.packages = list()
|
||||
self._get_list()
|
||||
|
||||
self.packages_complete = list()
|
||||
self._get_complete_list()
|
||||
|
||||
def _get_source_file(self):
|
||||
"""Download the Blender dependency script and import it at runtime."""
|
||||
r = requests.get(self._url)
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='wb', suffix='.py', delete=False) as t:
|
||||
t.write(r.content)
|
||||
path = t.name
|
||||
|
||||
try:
|
||||
spec = importlib.util.spec_from_file_location('install_linux_packages', path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
finally:
|
||||
os.remove(path)
|
||||
|
||||
def _get_list(self):
|
||||
"""Keep the mandatory 'library' packages (and Wayland Protocols) as Debian/Ubuntu apt names."""
|
||||
sources = self.module.BUILD_MANDATORY_SUBPACKAGES + self.module.DEPS_CRITICAL_SUBPACKAGES
|
||||
for package in self._flatten(sources):
|
||||
if 'library' not in package.name.lower() and package.name.lower() not in _EXTRA_PACKAGE_NAMES:
|
||||
continue
|
||||
name = package.distro_package_names.get(self.module.DISTRO_ID_DEBIAN)
|
||||
if isinstance(name, str):
|
||||
self.packages.append(name)
|
||||
|
||||
def _get_complete_list(self):
|
||||
"""All packages required to build Blender (PACKAGES_BASICS_BUILD) as Debian/Ubuntu apt names."""
|
||||
for package in self._flatten(self.module.PACKAGES_BASICS_BUILD):
|
||||
name = package.distro_package_names.get(self.module.DISTRO_ID_DEBIAN)
|
||||
if isinstance(name, str):
|
||||
self.packages_complete.append(name)
|
||||
|
||||
def _flatten(self, packages):
|
||||
"""Yield every leaf Package, descending into is_group sub_packages."""
|
||||
for package in packages:
|
||||
if package.is_group:
|
||||
yield from self._flatten(package.sub_packages)
|
||||
else:
|
||||
yield package
|
||||
|
||||
def __str__(self):
|
||||
return ' '.join(self.packages)
|
||||
|
||||
def complete_str(self):
|
||||
return ' '.join(self.packages_complete)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bld = BlenderVersion(version='5.0.0')
|
||||
dep = BlenderDependency(blender=bld)
|
||||
|
||||
print('Show All packages:')
|
||||
print(dep.__str__())
|
||||
+60
-10
@@ -3,6 +3,7 @@ import re
|
||||
import docker
|
||||
import logging
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
from .version import BlenderVersion
|
||||
from .dependency import BlenderDependency
|
||||
@@ -35,15 +36,31 @@ class BlenderDocker:
|
||||
exit(1)
|
||||
|
||||
self._packages = BlenderDependency(blender)
|
||||
|
||||
|
||||
self._args = {
|
||||
'b3d_vs_major': f'{self._blender_version.major}',
|
||||
'b3d_vs_minor': f'{self._blender_version.minor}.{self._blender_version.hotfix}',
|
||||
'b3d_dependency': self._packages.__str__()
|
||||
'b3d_dependency': self._packages.__str__(),
|
||||
'python_version': self._get_required_python_version(),
|
||||
}
|
||||
|
||||
|
||||
self._severity = severity
|
||||
|
||||
|
||||
def _get_required_python_version(self) -> str:
|
||||
"""
|
||||
Fetch the exact Python version (major.minor) Blender's own CMake pins for this
|
||||
release, so the build stage's base image can match it instead of drifting with
|
||||
whatever a floating `python` tag currently resolves to.
|
||||
"""
|
||||
url = (f'https://projects.blender.org/blender/blender/raw/tag/'
|
||||
f'v{self._blender_version.tag_name()}/build_files/cmake/Modules/FindPythonLibsUnix.cmake')
|
||||
r = requests.get(url)
|
||||
match = re.search(r'set\(_PYTHON_VERSION_SUPPORTED\s+([0-9]+\.[0-9]+)\)', r.text)
|
||||
if not match:
|
||||
print(f'Could not determine required Python version from {url}')
|
||||
exit(1)
|
||||
return match.group(1)
|
||||
|
||||
def _get_logger(self, name: str) -> logging.Logger:
|
||||
"""
|
||||
Private Method to set a logger for a specific method set (by his name)
|
||||
@@ -206,9 +223,42 @@ class BlenderDocker:
|
||||
pass
|
||||
|
||||
def push(self):
|
||||
self._docker_client.images.push(
|
||||
repository=self._repository,
|
||||
tag=self._blender_version.tag_name(),
|
||||
stream=True,
|
||||
)
|
||||
|
||||
"""Push the Blender Docker Image"""
|
||||
time = datetime.datetime.now()
|
||||
logger = self._get_logger(f'push')
|
||||
collected_logs = []
|
||||
try:
|
||||
print(f'Pushing Blender Docker image {self._docker_image_name}')
|
||||
logger.info(f'Pushing Blender Docker Image')
|
||||
logger.info(f'\tTag pushed: {self._docker_image_name}')
|
||||
|
||||
# Use the low-level API: images.push() only returns once the whole
|
||||
# push is finished, so it gives no live progress. api.push() with
|
||||
# decode=True streams each layer's progress as it happens.
|
||||
push_logs = self._docker_client.api.push(
|
||||
repository=self._repository,
|
||||
tag=self._blender_version.tag_name(),
|
||||
stream=True,
|
||||
decode=True,
|
||||
)
|
||||
|
||||
for chunk in push_logs:
|
||||
collected_logs.append(chunk)
|
||||
|
||||
if 'status' in chunk:
|
||||
status = _clean_log_line(chunk['status'])
|
||||
progress = _clean_log_line(chunk.get('progress', ''))
|
||||
line = f"{status} {chunk.get('id', '')} {progress}".strip()
|
||||
if 'Layer already exists' in status:
|
||||
line = f'[CACHE] {line}'
|
||||
logger.info(line)
|
||||
|
||||
elif 'error' in chunk:
|
||||
logger.error(chunk['error'])
|
||||
raise APIError(chunk['error'])
|
||||
|
||||
logger.info(f'Push succeeded for image "{self._docker_image_name}".')
|
||||
|
||||
except APIError as e:
|
||||
logger.exception(f'Push docker image failed: {e}')
|
||||
raise
|
||||
Reference in New Issue
Block a user