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 ###
.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>
+15 -17
View File
@@ -1,4 +1,4 @@
FROM python:latest AS b3dock
FROM ubuntu:25.10 AS b3dock
# Setup all software version request
ARG b3d_vs_major=5.2
@@ -6,36 +6,34 @@ ARG b3d_vs_minor=0
LABEL Author="stilobique"
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
# 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
RUN git clone --depth 1 --branch v${b3d_vs_major}.${b3d_vs_minor} \
https://projects.blender.org/blender/blender.git /opt/blender
# Get dependencies
COPY ./scripts/gen_packages_txt.py /opt/blender/build_files/build_environment/gen_packages_txt.py
RUN cd /opt/blender/build_files/build_environment && \
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)
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
RUN cd /opt/blender && \
make update && \
make -j$(nproc)
## 02. Build optimissed image
## 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
RUN apt-get update && xargs -a /tmp/packages.txt apt-get install -y
#COPY --from=b3dock /opt/blender/build_files/build_environment/packages.txt /tmp/packages.txt
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
+113 -25
View File
@@ -1,49 +1,104 @@
import os
import docker
import logging
from .version import BlenderVersion
from pathlib import Path
from docker.errors import BuildError
from docker.errors import BuildError, APIError, ContainerError, ImageNotFound
class BlenderDocker:
def __init__(self, blender: BlenderVersion):
def __init__(self, blender: BlenderVersion, severity: int = logging.WARNING):
self._blender_version = blender
# 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.login()
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')
if not self._docker_file.exists():
print(f'Dockerfile not found: {self._docker_file}')
exit(1)
self.args = {
self._args = {
'b3d_vs_major': f'{self._blender_version.major}',
'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:
print('\tBuilding Blender Docker')
print(f'\tWhere to build: {os.getcwd()}')
print(f'\tArgs: {self.args}')
print(f'\tTage generated: {self._blender_version.format_to_tag()}')
build = self._docker_client.images.build(
path=os.getcwd(),
buildargs=self.args,
tag=self._blender_version.format_to_tag(),
quiet=False,
print('Login to Hub Docker.')
self._docker_client.login(
username=os.environ['DOCKER_HUB_USER'],
password=os.environ['DOCKER_HUB_PASSWORD']
)
print(f'\tBuilding Blender Docker Image done: {build}')
return build
except APIError as e:
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:
print("Hey something went wrong with image build!")
logger.exception(f'Build docker image failed: {e}')
for line in e.build_log:
if 'stream' in line:
print(line['stream'].strip())
logger.debug(line['stream'].strip())
raise
@staticmethod
@@ -51,13 +106,46 @@ class BlenderDocker:
print('\tSet Tags version and/or Latest')
pass
@staticmethod
def test(self):
print('\tTesting Blender Docker')
# docker run --rm stilobique/blender:5.1.2 ./blender --version
pass
"""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."""
logger = self._get_logger('test')
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):
# TODO Login to hub
pass
self._docker_client.images.push(
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.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"""
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}'
def tag_name_slugify(self):
return f'{self.major}-{self.minor}-{self.hotfix}'
def __str__(self):
return str(self.major + '.' + self.minor + '.' + self.hotfix)
+15 -35
View File
@@ -1,45 +1,25 @@
import os
from logging import INFO
from blender.version import BlenderVersion
from blender.docker import BlenderDocker
# 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}')
# Todo Update readme file badge with blender version
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): "))
# build_docker(b3d_version=(f'{version.major}', f'{version.minor}.{version.hotfix}'),
# latest=False)
print(f"Build a blender version {version}.")
docker = BlenderDocker(version)
docker.build()
docker = BlenderDocker(blender=version, severity=INFO)
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