16 Commits
Author SHA1 Message Date
stilobique 61f00647f4 Small doc about all log and add more todo 2026-07-21 23:08:44 +02:00
stilobique ce68a96440 Setup test logging 2026-07-21 23:03:08 +02:00
stilobique 1457177f6b Add more debug/print info 2026-07-20 23:44:57 +02:00
stilobique b66f62a3f3 Update logs setup.
Add clean dockers env.
Fix Dockerfile with the latest step.
2026-07-20 23:34:59 +02:00
stilobique 5e154cf654 WIP Docker building 2026-07-19 23:17:51 +02:00
stilobique fafb076f2b Ignore .env file 2026-07-19 10:47:53 +02:00
stilobique 693afc76fd Setup python builder 2026-07-18 16:45:19 +02:00
stilobique 82653b4fba Update dependencies get 2026-07-18 11:28:16 +02:00
stilobique dc2dc84f4f Add Jetbrains project icon 2026-07-17 08:45:55 +02:00
stilobique db823ebe3f Fix installation with blender 5.2.0 2026-07-16 16:36:23 +02:00
stilobique 1ad119e7d2 Clear old files 2026-06-25 16:02:17 +02:00
stilobique 816e8f5c12 Update readme file, and clear docker file 2026-06-25 13:52:02 +02:00
stilobique 39b44c9a08 REfactoring to work with blender 5.1.x 2026-06-25 13:42:25 +02:00
stilobique 029a7c3b38 Fix build and documente check info 2026-06-19 18:02:24 +02:00
stilobique 5ed95e455c Fix image build 2026-06-19 16:58:32 +02:00
stilobique 204c1da235 Fix the build with latest release 2026-06-19 16:39:09 +02:00
13 changed files with 109 additions and 425 deletions
+1 -2
View File
@@ -8,9 +8,8 @@
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
</envs> </envs>
<option name="SDK_HOME" value="" /> <option name="SDK_HOME" value="" />
<option name="SDK_NAME" value="Python 3.12 (BlenderDocker)" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="false" /> <option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" /> <option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" />
<option name="DEBUG_JUST_MY_CODE" value="false" /> <option name="DEBUG_JUST_MY_CODE" value="false" />
+1 -1
View File
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build BlenderDocker" type="docker-deploy" factoryName="dockerfile" server-name="Docker"> <configuration default="false" name="Build BlenderDocker" type="docker-deploy" factoryName="dockerfile" server-name="Docker Desktop">
<deployment type="dockerfile"> <deployment type="dockerfile">
<settings> <settings>
<option name="imageTag" value="stilobique/blender:4.5.11" /> <option name="imageTag" value="stilobique/blender:4.5.11" />
+1 -2
View File
@@ -8,9 +8,8 @@
<env name="PYTHONUNBUFFERED" value="1" /> <env name="PYTHONUNBUFFERED" value="1" />
</envs> </envs>
<option name="SDK_HOME" value="" /> <option name="SDK_HOME" value="" />
<option name="SDK_NAME" value="Python 3.12 (BlenderDocker)" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/" />
<option name="IS_MODULE_SDK" value="false" /> <option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" /> <option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" />
<option name="DEBUG_JUST_MY_CODE" value="false" /> <option name="DEBUG_JUST_MY_CODE" value="false" />
+16 -72
View File
@@ -1,11 +1,4 @@
# Pinned to the exact Python version Blender's own CMake requires for this FROM ubuntu:25.10 AS b3dock
# release (build_files/cmake/Modules/FindPythonLibsUnix.cmake,
# _PYTHON_VERSION_SUPPORTED) - a floating `python` tag drifts to whatever
# "latest" currently is, which stops matching once it moves past what an
# older/LTS Blender release hard-pins (it has no matching apt package to
# install after the fact, unlike a missing library).
ARG python_version=3.13
FROM python:${python_version} AS b3dock
# Setup all software version request # Setup all software version request
ARG b3d_vs_major=5.2 ARG b3d_vs_major=5.2
@@ -15,81 +8,32 @@ LABEL Author="stilobique"
LABEL Title="Blender Docker for Unit Test" LABEL Title="Blender Docker for Unit Test"
LABEL Maintainer="Aurelien Vaillant contact@aurelien-vaillant.net" LABEL Maintainer="Aurelien Vaillant contact@aurelien-vaillant.net"
#ENV TZ=Europe/Paris
#RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
## 01. Start First stage to build blender ## 01. Start First stage to build blender
# Install dependencies # Install dependencies
RUN apt-get update && apt-get install -y git git-lfs sudo RUN apt-get update && apt-get install -y git git-lfs python3 sudo
# Get source code # Compile Blender
RUN git clone --depth 1 --branch v${b3d_vs_major}.${b3d_vs_minor} \ RUN git clone --depth 1 --branch v${b3d_vs_major}.${b3d_vs_minor} \
https://projects.blender.org/blender/blender.git /opt/blender https://projects.blender.org/blender/blender.git /opt/blender && \
chmod a+x /opt/blender/build_files/build_environment/install_linux_packages.py && \
python3 /opt/blender/build_files/build_environment/install_linux_packages.py
# Start build RUN cd /opt/blender && \
# install_linux_packages.py (default, no --all) skips a handful of packages make update && \
# that are only provided via the precompiled-libs bundle or via --all: make -j$(nproc)
# libepoxy-dev, Boost (all components), OpenImageIO. --all itself is not an
# option here: it also checks its own apt catalog for a python3-dev matching
# [3.11, 3.13[, which fails on Debian trixie regardless of which Python the
# base image runs (that check queries apt's package catalog, not the actual
# interpreter on PATH). Since `make update` below skips the precompiled
# bundle on Linux by default, these are needed from apt regardless - install
# them explicitly instead.
RUN cd /opt/blender/build_files/build_environment && \
chmod a+x ./install_linux_packages.py && \
python install_linux_packages.py && \
apt-get install -y libepoxy-dev libboost-all-dev libopenimageio-dev libembree-dev libpugixml-dev && \
pip install numpy cython && \
cd /opt/blender && make update && make -j$(nproc)
# Discover Blender's *actual* runtime shared-library needs from the compiled
# binary itself (ldd), instead of guessing from install_linux_packages.py
# (that script only covers build-time deps; libSM/libICE were never listed
# there but the binary needs them at runtime). Every resolved .so is mapped
# back to the apt package that owns it, so stage 02 can install exactly that.
RUN ldd /opt/build_linux/bin/blender | tee /dev/stderr | grep 'not found' && \
(echo 'Unresolved libs in the build stage itself, investigate first' && exit 1) || true
# `realpath` is required: /lib is a merged-usr symlink to /usr/lib, and dpkg's
# file database is keyed on the canonical /usr/lib/... path (with the full
# soname suffix, e.g. libX11.so.6.4.0) — querying the /lib/....so.6 symlink
# path directly returns "no path found matching pattern".
# These names are resolved against Debian (this stage's distro), but stage 02
# installs them on Ubuntu - the two occasionally disagree on a package name
# for the same library, and not just cosmetically: Debian's `libjpeg62-turbo`
# provides libjpeg.so.62, but Ubuntu's identically-ABI-sounding
# `libjpeg-turbo8` actually provides a DIFFERENT soname (libjpeg.so.8) -
# Ubuntu ships the old libjpeg.so.62 ABI as a separate `libjpeg62` package.
# Debian's generic `libxml2` is Ubuntu's soname-suffixed `libxml2-16`.
# Remap the known cases here rather than in stage 02.
RUN ldd /opt/build_linux/bin/blender \
| awk '{print $3}' | grep '^/' | sort -u \
| xargs -r realpath | sort -u \
| xargs -r dpkg -S 2>/dev/null | cut -d: -f1 | tr ',' '\n' | sed 's/^ //' | sort -u \
| sed -e 's/^libjpeg62-turbo$/libjpeg62/' -e 's/^libxml2$/libxml2-16/' \
| tee /opt/build_linux/runtime_packages.txt
## 02. Build optimissed image ## 02. Build optimissed image
## Setup a Multistage optimisation ## Setup a Multistage optimisation
FROM ubuntu:25.10 AS final FROM ubuntu:25.10 AS final
# Re-declared: ARGs before the first FROM only apply to that FROM line, not #COPY --from=b3dock /opt/blender/build_files/build_environment/packages.txt /tmp/packages.txt
# to instructions in later stages. COPY --from=b3dock /opt/blender/build_files/build_environment/install_linux_packages.py /tmp/install_linux_packages.py
ARG python_version=3.13 RUN apt-get update && apt-get install -y python3 sudo
RUN python3 /tmp/install_linux_packages.py
# Install exactly the runtime packages ldd found the compiled binary needs
# (see the b3dock stage above) instead of guessing from Blender's build-time
# package list. Ubuntu and Debian share package names for these core libs.
COPY --from=b3dock /opt/build_linux/runtime_packages.txt /tmp/runtime_packages.txt
RUN apt-get update && \
xargs -a /tmp/runtime_packages.txt apt-get install -y --no-install-recommends && \
rm -rf /tmp/runtime_packages.txt /var/lib/apt/lists/*
# libpython<version>.so isn't an apt package: the b3dock base image builds
# Python from source into /usr/local, so ldd/dpkg-S (above) can never find an
# owning package for it - Ubuntu's apt only ships whatever Python versions it
# currently defaults to, which won't match an older/LTS Blender's pin. Copy
# the .so Blender actually linked against directly from the build stage.
COPY --from=b3dock /usr/local/lib/libpython${python_version}.so.1.0 /usr/local/lib/libpython${python_version}.so.1.0
RUN ldconfig
RUN useradd -m -s /bin/bash bld RUN useradd -m -s /bin/bash bld
+6 -44
View File
@@ -5,33 +5,7 @@
This repository is an automated blender docker image file generated, it's usefully with blender addon unit test. This repository is an automated blender docker image file generated, it's usefully with blender addon unit test.
# Building # Adding an Addon
## Docker Hub credentials
`main.py` logs into Docker Hub before building, so it needs `DOCKER_HUB_USER` and `DOCKER_HUB_PASSWORD` in the environment.
Create a `config.env` file at the repo root (gitignored):
```shell
DOCKER_HUB_USER=your_docker_hub_username
DOCKER_HUB_PASSWORD=your_docker_hub_password
```
It's loaded automatically by the `Automation Builder` run configuration (`ENV_FILES`) when running `main.py` from the IDE. Running it another way? Export those two variables yourself first.
## Start your build
You can use Invoke to easily start a build, a clean or more.
```shell
invoke --list
```
## Debug and logs
Some log are generated inside a `logs` folder.
- `build`: all operation on build docker file.
- `clean`: from the dedicated function to clean all dockers images and cache.
- `test`: simple class method to try a specific docker image.
## Check if the build work
# Use case
## Adding an Addon
Mount a new volume with your addon inside a folder (ex `/blender-plugin/plugin-archive.zip`), when you run your docker container add this volume and execute a script to add-it with the blender installed. Mount a new volume with your addon inside a folder (ex `/blender-plugin/plugin-archive.zip`), when you run your docker container add this volume and execute a script to add-it with the blender installed.
Python function to install an addon with Blender. The file his named `install.py` Python function to install an addon with Blender. The file his named `install.py`
@@ -68,20 +42,8 @@ docker run --rm stilobique/blender:5.1.2 ./blender -b --python-expr "import bpy;
``` ```
# CLI helper tasks # Logs
Reusable helpers (`tasks.py`) are exposed as [invoke](https://www.pyinvoke.org/) tasks. Some log are generated inside a `logs` folder.
- `build`: all operation on build docker file.
```shell - `clean`: from the dedicated function to clean all dockers images and cache.
# List available tasks - `test`: simple class method to try a specific docker image.
invoke clean
# Clean Docker (all contexts: default, podman, ...)
python -m blender.cli clean
# List Blender tags (Gitea)
python -m blender.cli get-tags-release
# List stilobique/blender tags (Docker Hub)
python -m blender.cli get-tags-docker
```
-83
View File
@@ -1,83 +0,0 @@
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/api/v1/repos/blender/blender/raw/build_files/build_environment/install_linux_packages.py?ref=v{self._blender_version.tag_name()}'
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)
r.raise_for_status()
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
if hasattr(self.module, 'DEPS_CRITICAL_SUBPACKAGES'):
sources += 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."""
if hasattr(self.module, 'PACKAGES_BASICS_BUILD'):
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__())
+28 -127
View File
@@ -1,29 +1,17 @@
import os import os
import re
import docker import docker
import logging import logging
import datetime
import requests
from .version import BlenderVersion from .version import BlenderVersion
from .dependency import BlenderDependency
from pathlib import Path from pathlib import Path
from docker.errors import BuildError, APIError, ContainerError, ImageNotFound from docker.errors import BuildError, APIError, ContainerError, ImageNotFound
_ANSI_ESCAPE_RE = re.compile(r'\x1b\[[0-9;]*[a-zA-Z]')
def _clean_log_line(text: str) -> str:
"""Strip ANSI escape codes and keep only the last update of a \r-rewritten line."""
text = _ANSI_ESCAPE_RE.sub('', text)
return text.split('\r')[-1].strip()
class BlenderDocker: class BlenderDocker:
def __init__(self, blender: BlenderVersion, severity: int = logging.WARNING): def __init__(self, blender: BlenderVersion, severity: int = logging.WARNING):
self._blender_version = blender self._blender_version = blender
self._docker_client = docker.from_env() # Todo get a user config
self._docker_client = docker.DockerClient(base_url=f'unix:///var/run/docker.sock')
self.login() self.login()
self._docker_image = None self._docker_image = None
self._repository = 'stilobique/blender' self._repository = 'stilobique/blender'
@@ -34,33 +22,13 @@ class BlenderDocker:
print(f'Dockerfile not found: {self._docker_file}') print(f'Dockerfile not found: {self._docker_file}')
exit(1) exit(1)
self._packages = BlenderDependency(blender)
self._args = { self._args = {
'b3d_vs_major': f'{self._blender_version.major}', 'b3d_vs_major': f'{self._blender_version.major}',
'b3d_vs_minor': f'{self._blender_version.minor}.{self._blender_version.hotfix}', 'b3d_vs_minor': f'{self._blender_version.minor}.{self._blender_version.hotfix}',
'b3d_dependency': self._packages.__str__(),
'python_version': self._get_required_python_version(),
} }
self._severity = severity 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/api/v1/repos/blender/blender/raw/'
f'build_files/cmake/Modules/FindPythonLibsUnix.cmake?ref=v{self._blender_version.tag_name()}')
r = requests.get(url)
r.raise_for_status()
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: def _get_logger(self, name: str) -> logging.Logger:
""" """
Private Method to set a logger for a specific method set (by his name) Private Method to set a logger for a specific method set (by his name)
@@ -75,7 +43,7 @@ class BlenderDocker:
logger.propagate = False logger.propagate = False
if not logger.handlers: if not logger.handlers:
handler = logging.FileHandler(log_dir / f'{name}.log', encoding='utf-8') handler = logging.FileHandler(log_dir / f'{name}.log')
handler.setFormatter(logging.Formatter( handler.setFormatter(logging.Formatter(
'%(asctime)s | [%(levelname)s] %(message)s' '%(asctime)s | [%(levelname)s] %(message)s'
)) ))
@@ -98,56 +66,39 @@ class BlenderDocker:
def build(self): def build(self):
"""Build the Blender Docker Image""" """Build the Blender Docker Image"""
time = datetime.datetime.now() logger = self._get_logger('build')
logger = self._get_logger(f'build-{time.strftime("%Y-%m-%d-%H-%M")}')
collected_logs = []
try: try:
print('Building Blender Docker Image') print('Building Blender Docker Image')
logger.info('Building Blender Docker Image') logger.info('Building Blender Docker Image')
logger.info(f'\tBuilds withs args: {self._args}') logger.info(f'\tBuilds withs args: {self._args}')
logger.info(f'\tTag generate: {self._docker_image_name}') logger.info(f'\tTag generate: {self._docker_image_name}')
# Use the low-level API: images.build() only returns once the whole self._docker_image, build_logs = self._docker_client.images.build(
# build is finished, so it gives no live progress. api.build() with
# decode=True streams each step (including cache hits) as it happens.
build_logs = self._docker_client.api.build(
path=os.getcwd(), path=os.getcwd(),
# nocache=True, # nocache=True,
buildargs=self._args, buildargs=self._args,
tag=self._docker_image_name, tag=self._docker_image_name,
quiet=False,
rm=True, rm=True,
forcerm=True, forcerm=True
decode=True,
) )
# Debug log:
for chunk in build_logs: for chunk in build_logs:
collected_logs.append(chunk)
if 'stream' in chunk: if 'stream' in chunk:
for raw_line in chunk['stream'].split('\n'): line = chunk['stream'].strip()
line = _clean_log_line(raw_line) if line:
if not line: logger.debug(line)
continue
if 'Using cache' in line:
line = f'[CACHE] {line}'
logger.info(line)
elif '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 'Already exists' in status:
line = f'[CACHE] {line}'
logger.info(line)
elif 'error' in chunk: elif 'error' in chunk:
logger.error(chunk['error']) logger.error(chunk['error'])
raise BuildError(chunk['error'], collected_logs)
self._docker_image = self._docker_client.images.get(self._docker_image_name)
except BuildError as e: except BuildError as e:
logger.exception(f'Build docker image failed: {e}') logger.exception(f'Build docker image failed: {e}')
for line in e.build_log:
if 'stream' in line:
logger.debug(line['stream'].strip())
raise raise
@staticmethod @staticmethod
@@ -163,102 +114,52 @@ class BlenderDocker:
:return: True if no issue are returned :return: True if no issue are returned
""" """
logger = self._get_logger('test') logger = self._get_logger('test')
container_name = f"Blender-Docker-Testing-{self._blender_version.tag_name_slugify()}"
command = ['./blender', '--version']
container = None
try: try:
print(f'Testing your Blender Docker image {self._docker_image_name}') print(f'Testing your Blender Docker image {self._docker_image_name}')
logger.info(f'Testing image "{self._docker_image_name}" in container "{container_name}" ' logger.info(f'Testing your Blender Docker image {self._docker_image_name}.')
f'with command: {" ".join(command)}')
# auto_remove is intentionally NOT used: Docker can remove the container test = self._docker_client.containers.run(
# right after it exits, racing with the logs() call below and turning a name=f"Blender-Docker-Testing-{self._blender_version.tag_name_slugify()}",
# real container-side error into an unrelated 409 "dead or marked for
# removal" API error. We remove the container ourselves once logs are read.
container = self._docker_client.containers.run(
name=container_name,
image=self._docker_image_name, image=self._docker_image_name,
command=command, command=['./blender', '--version'],
auto_remove=True,
remove=True,
detach=True, detach=True,
stdout=True, stdout=True,
stderr=True, stderr=True,
stream=True,
) )
result = container.wait() result = test.wait()
exit_code = result.get('StatusCode') exit_code = result.get('StatusCode')
container_logs = container.logs(stdout=True, stderr=True).decode('utf-8', errors='replace').strip() logger.info(f'Exit code return: {exit_code}')
for raw_line in container_logs.split('\n'): logger.info(test.logs(stdout=True, stderr=True).decode('utf-8', errors='replace').strip())
line = _clean_log_line(raw_line)
if line:
logger.info(f'[container] {line}')
if exit_code == 0: if exit_code == 0:
logger.info(f'Test succeeded (exit code {exit_code}) for image "{self._docker_image_name}".')
return True return True
logger.error(f'Test failed (exit code {exit_code}) for image "{self._docker_image_name}".')
print(f'Testing your Blender Docker image {self._docker_image_name} failed.') print(f'Testing your Blender Docker image {self._docker_image_name} failed.')
return False return False
except ContainerError as e: except ContainerError as e:
logger.exception(f'Container error while testing "{self._docker_image_name}": {e}')
print(f'Container error: {e}') print(f'Container error: {e}')
exit(1) exit(1)
except ImageNotFound as e: except ImageNotFound as e:
logger.exception(f'Image "{self._docker_image_name}" not found: {e}')
print(f'Image not found: {e}') print(f'Image not found: {e}')
exit(1) exit(1)
except APIError as e: except APIError as e:
logger.exception(f'API error while testing "{self._docker_image_name}": {e}')
print(f'API error: {e}') print(f'API error: {e}')
for line in e.build_log:
if 'stream' in line:
print(line['stream'].strip())
exit(1) exit(1)
finally:
if container is not None:
try:
container.remove(force=True)
except APIError:
pass
def push(self): def push(self):
"""Push the Blender Docker Image""" self._docker_client.images.push(
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, repository=self._repository,
tag=self._blender_version.tag_name(), tag=self._blender_version.tag_name(),
stream=True, 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
-25
View File
@@ -1,7 +1,6 @@
import logging import logging
import os import os
import json import json
import requests
from pathlib import Path from pathlib import Path
from subprocess import run, CalledProcessError from subprocess import run, CalledProcessError
@@ -70,29 +69,5 @@ def clean_all_dockers():
logger.info(f'Contexte restaure: {original_context}') logger.info(f'Contexte restaure: {original_context}')
def get_all_tags_from_blender():
"""From the Gitea api, get all blender tags release"""
r = requests.get(r"https://projects.blender.org/api/v1/repos/blender/blender/tags")
print(r.text)
j = r.json()
# All tags data:
for release in j:
print(release)
def get_all_tags_from_docker():
"""From the Gitea api, get all blender tags release"""
r = requests.get(r"https://hub.docker.com/v2/namespaces/stilobique/repositories/blender/tags")
print(r.text)
j = r.json()
# All tags data:
for release in j:
print(release)
if __name__ == '__main__': if __name__ == '__main__':
print('Clean docker')
clean_all_dockers() clean_all_dockers()
print('Done')
-10
View File
@@ -1,4 +1,3 @@
import re
class BlenderVersion: class BlenderVersion:
@@ -7,7 +6,6 @@ class BlenderVersion:
Need an object like 'X.Y.Z' to work correctly, build a pythonic object about a blender version. Need an object like 'X.Y.Z' to work correctly, build a pythonic object about a blender version.
:param version: :param version:
""" """
self._check_pattern(version)
self.major : str = '' self.major : str = ''
self.minor : str = '' self.minor : str = ''
self.hotfix : str = '' self.hotfix : str = ''
@@ -31,13 +29,5 @@ class BlenderVersion:
def tag_name_slugify(self): def tag_name_slugify(self):
return f'{self.major}-{self.minor}-{self.hotfix}' return f'{self.major}-{self.minor}-{self.hotfix}'
@staticmethod
def _check_pattern(tested: str):
"""Simple test to look if the string give can be a blender version name."""
test = re.match(r'^[0-9]{1}.[0-9]+.[0-9]+$', tested)
if test is None:
print(f'Bad version name set: {tested}')
exit(1)
def __str__(self): def __str__(self):
return str(self.major + '.' + self.minor + '.' + self.hotfix) return str(self.major + '.' + self.minor + '.' + self.hotfix)
+4 -2
View File
@@ -4,9 +4,11 @@ from blender.version import BlenderVersion
from blender.docker import BlenderDocker from blender.docker import BlenderDocker
# Todo Update readme file badge with blender version # Todo Update readme file badge with blender version
# Todo
if __name__ == "__main__": if __name__ == "__main__":
# Todo use the manual entry can be error prone (need to make a basic regex to check this entry)
version = BlenderVersion(input("Give the blender version (X.Y.Z): ")) version = BlenderVersion(input("Give the blender version (X.Y.Z): "))
print(f"Build a blender version {version}.") print(f"Build a blender version {version}.")
@@ -17,8 +19,8 @@ if __name__ == "__main__":
test = docker.test() test = docker.test()
if test: if test:
print('Test work, push this image.') print('Push this image.')
docker.push() # docker.push()
# Todo: Add a latest tag, check if this version are the latest # Todo: Add a latest tag, check if this version are the latest
# Todo: Add a LTS tag, check if this version are a LTS # Todo: Add a LTS tag, check if this version are a LTS
-2
View File
@@ -1,3 +1 @@
docker==7.2.0 docker==7.2.0
requests==2.34.2
invoke==3.0.3
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
"""
Lit install_linux_packages.py et génère un fichier .txt avec les paquets
Debian/Ubuntu à installer.
"""
import re
import sys
READER="install_linux_packages.py"
OUT="packages.txt"
def main():
with open(READER, "r", encoding="utf-8") as f:
content = f.read()
# Cherche toutes les lignes du type: DISTRO_ID_DEBIAN: "nom-du-paquet"
packages = re.findall(r'DISTRO_ID_DEBIAN:\s*"([^"]+)"', content)
# Enlève les doublons, trie
packages = sorted(set(packages))
with open(OUT, "w", encoding="utf-8") as f:
for pkg in packages:
f.write(pkg + "\n")
print(f"{len(packages)} paquets écrits dans {OUT}")
if __name__ == "__main__":
main()
-34
View File
@@ -1,34 +0,0 @@
"""
Invoke CLI entry point for the reusable helpers in `blender/misc.py`.
Ex: python -m blender.cli clean
Ex: python -m blender.cli get-tags-release
"""
from invoke import Collection, Program, task
from blender.misc import clean_all_dockers, get_all_tags_from_blender, get_all_tags_from_docker
@task(name='clean')
def clean(ctx):
"""Nettoie Docker pour tous les contextes disponibles (default, podman, ...)."""
clean_all_dockers()
@task(name='get-tags-release')
def get_tags_release(ctx):
"""Recupere tous les tags de release Blender depuis Gitea."""
get_all_tags_from_blender()
@task(name='get-tags-docker')
def get_tags_docker(ctx):
"""Recupere tous les tags de l'image Docker stilobique/blender depuis Docker Hub."""
get_all_tags_from_docker()
namespace = Collection(clean, get_tags_release, get_tags_docker)
program = Program(namespace=namespace, version='0.1.0')
if __name__ == '__main__':
program.run()