Compare commits

4 Commits
Author SHA1 Message Date
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
8 changed files with 288 additions and 78 deletions
+6
View File
@@ -1,3 +1,9 @@
### Logs
**/logs/**
### Config/setup """
*.env
### PyCharm ### ### PyCharm ###
.idea/ .idea/
+26
View File
@@ -0,0 +1,26 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Automation Builder" type="PythonConfigurationType" factoryName="Python">
<module name="rider.module" />
<option name="ENV_FILES" value="$PROJECT_DIR$/config.env" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="DEBUG_JUST_MY_CODE" value="false" />
<option name="RUN_TOOL" value="" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>
+26
View File
@@ -0,0 +1,26 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Clean docker Images " type="PythonConfigurationType" factoryName="Python">
<module name="rider.module" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="DEBUG_JUST_MY_CODE" value="false" />
<option name="RUN_TOOL" value="" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/blender/misc.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>
+14 -16
View File
@@ -1,4 +1,4 @@
FROM python:latest AS b3dock FROM ubuntu:25.10 AS b3dock
# Setup all software version request # Setup all software version request
ARG b3d_vs_major=5.2 ARG b3d_vs_major=5.2
@@ -6,36 +6,34 @@ ARG b3d_vs_minor=0
LABEL Author="stilobique" 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 #ENV TZ=Europe/Paris
#RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone #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 RUN apt-get update && apt-get install -y git git-lfs python3 sudo
# Compile Blender # 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
# Get dependencies RUN cd /opt/blender && \
COPY ./scripts/gen_packages_txt.py /opt/blender/build_files/build_environment/gen_packages_txt.py make update && \
RUN cd /opt/blender/build_files/build_environment && \ make -j$(nproc)
python gen_packages_txt.py
RUN cd /opt/blender/build_files/build_environment && chmod a+x ./install_linux_packages.py && \
python install_linux_packages.py
RUN cd /opt/blender && make update
RUN cd /opt/blender && make -j$(nproc)
## 02. Build optimissed image ## 02. Build optimissed image
## Setup a Multistage optimisation ## Setup a Multistage optimisation
FROM ubuntu:25.04 AS final FROM ubuntu:25.10 AS final
COPY --from=b3dock /opt/blender/build_files/build_environment/packages.txt /tmp/packages.txt #COPY --from=b3dock /opt/blender/build_files/build_environment/packages.txt /tmp/packages.txt
RUN apt-get update && xargs -a /tmp/packages.txt apt-get install -y COPY --from=b3dock /opt/blender/build_files/build_environment/install_linux_packages.py /tmp/install_linux_packages.py
RUN apt-get update && apt-get install -y python3 sudo
RUN python3 /tmp/install_linux_packages.py
RUN useradd -m -s /bin/bash bld RUN useradd -m -s /bin/bash bld
+112 -24
View File
@@ -1,49 +1,104 @@
import os import os
import docker import docker
import logging
from .version import BlenderVersion from .version import BlenderVersion
from pathlib import Path from pathlib import Path
from docker.errors import BuildError from docker.errors import BuildError, APIError, ContainerError, ImageNotFound
class BlenderDocker: class BlenderDocker:
def __init__(self, blender: BlenderVersion): def __init__(self, blender: BlenderVersion, severity: int = logging.WARNING):
self._blender_version = blender self._blender_version = blender
# Todo get a user config # Todo get a user config
# self._docker_client = docker.DockerClient(base_url=f'unix://{Path.home()}/.docker/desktop/docker.sock')
self._docker_client = docker.DockerClient(base_url=f'unix:///var/run/docker.sock') self._docker_client = docker.DockerClient(base_url=f'unix:///var/run/docker.sock')
self.login()
self._docker_image = None self._docker_image = None
self._repository = 'stilobique/blender'
self._docker_image_name = self._blender_version.format_to_tag(self._repository)
self._docker_file = Path(os.getcwd(), 'Dockerfile') self._docker_file = Path(os.getcwd(), 'Dockerfile')
if not self._docker_file.exists(): if not self._docker_file.exists():
print(f'Dockerfile not found: {self._docker_file}') print(f'Dockerfile not found: {self._docker_file}')
exit(1) exit(1)
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}',
} }
def build(self):
self._severity = severity
def _get_logger(self, name: str) -> logging.Logger:
"""
Private Method to set a logger for a specific method set (by his name)
:param name: Make the logger file name.
:return:
"""
log_dir = Path('logs')
log_dir.mkdir(parents=True, exist_ok=True)
logger = logging.getLogger(f'blender_docker.{name}')
logger.setLevel(self._severity)
logger.propagate = False
if not logger.handlers:
handler = logging.FileHandler(log_dir / f'{name}.log')
handler.setFormatter(logging.Formatter(
'%(asctime)s | [%(levelname)s] %(message)s'
))
logger.addHandler(handler)
return logger
def login(self):
"""Login to the Docker Hub"""
try: try:
print('\tBuilding Blender Docker') print('Login to Hub Docker.')
print(f'\tWhere to build: {os.getcwd()}') self._docker_client.login(
print(f'\tArgs: {self.args}') username=os.environ['DOCKER_HUB_USER'],
print(f'\tTage generated: {self._blender_version.format_to_tag()}') password=os.environ['DOCKER_HUB_PASSWORD']
build = self._docker_client.images.build(
path=os.getcwd(),
buildargs=self.args,
tag=self._blender_version.format_to_tag(),
quiet=False,
) )
print(f'\tBuilding Blender Docker Image done: {build}') except APIError as e:
return build print(f'Login failed with error: {e}')
exit(1)
def build(self):
"""Build the Blender Docker Image"""
logger = self._get_logger('build')
try:
print('Building Blender Docker Image')
logger.info('Building Blender Docker Image')
logger.info(f'\tBuilds withs args: {self._args}')
logger.info(f'\tTag generate: {self._docker_image_name}')
self._docker_image, build_logs = self._docker_client.images.build(
path=os.getcwd(),
# nocache=True,
buildargs=self._args,
tag=self._docker_image_name,
quiet=False,
rm=True,
forcerm=True
)
# Debug log:
for chunk in build_logs:
if 'stream' in chunk:
line = chunk['stream'].strip()
if line:
logger.debug(line)
elif 'error' in chunk:
logger.error(chunk['error'])
except BuildError as e: except BuildError as e:
print("Hey something went wrong with image build!") logger.exception(f'Build docker image failed: {e}')
for line in e.build_log: for line in e.build_log:
if 'stream' in line: if 'stream' in line:
print(line['stream'].strip()) logger.debug(line['stream'].strip())
raise raise
@staticmethod @staticmethod
@@ -51,13 +106,46 @@ class BlenderDocker:
print('\tSet Tags version and/or Latest') print('\tSet Tags version and/or Latest')
pass pass
@staticmethod
def test(self): def test(self):
print('\tTesting Blender Docker') """This method request an image (call the method build before) and try to execute the blender inside the container to check if the program can be correctly executed."""
# docker run --rm stilobique/blender:5.1.2 ./blender --version logger = self._get_logger('test')
pass try:
print('Testing your Blender Docker image')
logger.info('Testing your Blender Docker image')
test = self._docker_client.containers.run(
name=f"Blender-Docker-Testing-{self._blender_version.tag_name_slugify()}",
image=self._docker_image_name,
command=['./blender', '--version'],
auto_remove = True,
remove=True,
detach=True,
stdout=True
)
print(f'\tResult: {test.logs()}')
return True
except ContainerError as e:
print(f'Container error: {e}')
exit(1)
except ImageNotFound as e:
print(f'Image not found: {e}')
exit(1)
except APIError as e:
print(f'API error: {e}')
for line in e.build_log:
if 'stream' in line:
print(line['stream'].strip())
exit(1)
def push(self): def push(self):
# TODO Login to hub self._docker_client.images.push(
pass repository=self._repository,
tag=self._blender_version.tag_name(),
stream=True,
)
+79
View File
@@ -0,0 +1,79 @@
import logging
import os
import json
from pathlib import Path
from subprocess import run, CalledProcessError
# def clean_all_dockers():
# # bash docker context use default && docker system prune -a -f
# # bash docker context use podman && docker system prune -a -f
# # bash docker context use desktop-linux && docker system prune -a -f
# docker_client = docker.DockerClient(base_url=f'unix:///var/run/docker.sock')
def clean_all_dockers():
"""
Nettoie Docker pour tous les contextes disponibles (default, podman, ...).
Equivalent a, pour chaque contexte :
docker context use <context>
docker system prune -a -f
Puis remet le contexte d'origine en place a la fin.
"""
logs_dir = Path(os.getcwd(), 'logs')
logs_dir.mkdir(parents=True, exist_ok=True)
logger = logging.getLogger('blender_docker.clean')
logger.setLevel(logging.DEBUG)
logger.propagate = False
if not logger.handlers:
handler = logging.FileHandler(logs_dir / 'clean.log')
handler.setFormatter(logging.Formatter(
'%(asctime)s|%(name)s [%(levelname)s] %(message)s'
))
logger.addHandler(handler)
# Contexte courant, pour le restaurer a la fin
original_context = run(
['docker', 'context', 'show'],
capture_output=True, text=True, check=True
).stdout.strip()
logger.info(f'Contexte d\'origine: {original_context}')
# Liste de tous les contextes disponibles (format json, un objet par ligne)
contexts_raw = run(
['docker', 'context', 'ls', '--format', '{{json .}}'],
capture_output=True, text=True, check=True
).stdout.strip().splitlines()
contexts = [json.loads(line)['Name'] for line in contexts_raw if line]
print(f'\tContextes trouves: {contexts}')
logger.info(f'Contextes trouves: {contexts}')
for context in contexts:
try:
print(f'\tNettoyage du contexte: {context}')
logger.info(f'Nettoyage du contexte: {context}')
run(['docker', 'context', 'use', context], check=True)
result = run(
['docker', 'system', 'prune', '-a', '-f'],
capture_output=True, text=True, check=True
)
print(f'\t{result.stdout.strip()}')
logger.info(result.stdout.strip())
except CalledProcessError as e:
print(f'\tErreur sur le contexte {context}: {e.stderr.strip()}')
logger.error(f'Erreur sur le contexte {context}: {e.stderr.strip()}')
# On remet le contexte d'origine
run(['docker', 'context', 'use', original_context], check=True)
print(f'\tContexte restaure: {original_context}')
logger.info(f'Contexte restaure: {original_context}')
if __name__ == '__main__':
clean_all_dockers()
+8 -1
View File
@@ -18,9 +18,16 @@ class BlenderVersion:
self.minor = version[1] self.minor = version[1]
self.hotfix = version[2] self.hotfix = version[2]
def format_to_tag(self) -> str: def format_to_tag(self, repository: str) -> str:
"""From version information, format to return a string""" """From version information, format to return a string"""
return f'{repository}:{self.major}.{self.minor}.{self.hotfix}'
def tag_name(self) -> str:
"""Return the tag name 'latest', 'X.Y.Z'..."""
return f'{self.major}.{self.minor}.{self.hotfix}' return f'{self.major}.{self.minor}.{self.hotfix}'
def tag_name_slugify(self):
return f'{self.major}-{self.minor}-{self.hotfix}'
def __str__(self): def __str__(self):
return str(self.major + '.' + self.minor + '.' + self.hotfix) return str(self.major + '.' + self.minor + '.' + self.hotfix)
+14 -34
View File
@@ -1,45 +1,25 @@
import os from logging import INFO
from blender.version import BlenderVersion 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 add a check if the image build are stored in the docker register
def build_docker(b3d_version: (str, str), latest: bool = False):
"""
Generate docker image, request the blender version (major, minor) with the keyword b3d_version, and can be
tagged with latest or only blender version info.
"""
client = docker.from_env()
if os.environ.get('docker_hub_password'):
client.login(username=os.environ['docker_hub_user'], password=os.environ['docker_hub_password'])
# Init all data to build the docker image
args = {
'b3d_vs_major': str(b3d_version[0]),
'b3d_vs_minor': str(b3d_version[1]),
}
docker_repo = 'stilobique/blender'
if latest:
tag = 'latest'
else:
tag = f'{args["b3d_vs_major"]}.{args["b3d_vs_minor"]}'
completed_tag_name = f'{docker_repo}:{args["b3d_vs_major"]}.{args["b3d_vs_minor"]}'
print('\t1 -> Build from image file')
client.images.build(path=os.getcwd(), buildargs=args, tag=completed_tag_name, rm=True, forcerm=True)
print(f'\t2 -> Push the new images with tag {tag}')
push = client.images.push(repository=docker_repo, tag=tag, stream=True)
print(f'\t\tDebug push :\n\t\t{push}')
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): "))
# build_docker(b3d_version=(f'{version.major}', f'{version.minor}.{version.hotfix}'),
# latest=False)
print(f"Build a blender version {version}.") print(f"Build a blender version {version}.")
docker = BlenderDocker(version) docker = BlenderDocker(blender=version, severity=INFO)
docker.build() docker.build()
print('Test your image.')
test = docker.test()
if test:
print('Push this image.')
docker.push()
# Todo: Add a latest tag, check if this version are the latest
# Todo: Add a LTS tag, check if this version are a LTS