2020-01-12 22:55:13 +00:00
|
|
|
"""
|
|
|
|
A script to automatically generate Dockerfiles based on a manifest
|
|
|
|
To run this simply `python generate.py --m manifest.json`
|
|
|
|
For manifest structure see manifest.json
|
|
|
|
|
|
|
|
Written by juniorxsound <https://orfleisher.com>
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import json
|
|
|
|
import getpass
|
|
|
|
import argparse
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
def lookahead(iterable: list):
|
|
|
|
"""Pass through all values from the given iterable, augmented by the
|
|
|
|
information if there are more values to come after the current one
|
|
|
|
(True), or if it is the last value (False).
|
|
|
|
|
|
|
|
Thanks to the fantastic https://stackoverflow.com/questions/1630320/what-is-the-pythonic-way-to-detect-the-last-element-in-a-for-loop
|
|
|
|
"""
|
|
|
|
# Get an iterator and pull the first value.
|
|
|
|
it = iter(iterable)
|
|
|
|
last = next(it)
|
|
|
|
# Run the iterator to exhaustion (starting from the second value).
|
|
|
|
for val in it:
|
|
|
|
# Report the *previous* value (more to come).
|
|
|
|
yield last, True
|
|
|
|
last = val
|
|
|
|
# Report the last value.
|
|
|
|
yield last, False
|
|
|
|
|
|
|
|
|
|
|
|
def create_dockerfile(base_os: str,
|
|
|
|
title: str,
|
|
|
|
author: str,
|
|
|
|
env: list,
|
|
|
|
deps: list,
|
|
|
|
blender_download_url: str,
|
2021-06-09 14:21:05 +00:00
|
|
|
python_download_url: str,
|
|
|
|
python_version: str,
|
2020-01-12 22:55:13 +00:00
|
|
|
workdir: str) -> str:
|
|
|
|
"""
|
|
|
|
Create a stringified Dockerfile based on arguments provided
|
|
|
|
"""
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile = "# Dockerfile autogenerated on {} by {}\n".format(
|
2020-01-12 22:55:13 +00:00
|
|
|
datetime.now().strftime("%m/%d/%Y, %H:%M:%S"), getpass.getuser())
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "# Please do not edit this file directly\n\n"
|
2020-01-12 22:55:13 +00:00
|
|
|
|
|
|
|
dockerfile += "FROM {}\n\n".format(base_os)
|
|
|
|
dockerfile += "LABEL Author=\"{}\"\n".format(author)
|
|
|
|
dockerfile += "LABEL Title=\"{}\"\n\n".format(title)
|
|
|
|
|
2021-12-07 20:39:32 +00:00
|
|
|
dockerfile += "# Environment variables\n"
|
|
|
|
for environment_variable in env:
|
|
|
|
dockerfile += "ENV {}\n".format(environment_variable)
|
2020-01-12 22:55:13 +00:00
|
|
|
dockerfile += "\n"
|
|
|
|
|
|
|
|
dockerfile += "# Install dependencies\n"
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "RUN apt-get update && apt-get install -y \\\n"
|
2020-02-26 07:57:01 +00:00
|
|
|
|
|
|
|
archivetype = blender_download_url.split(".")[-1]
|
|
|
|
archiveflags = "xjvf"
|
2021-06-09 14:21:05 +00:00
|
|
|
|
2020-02-26 07:57:01 +00:00
|
|
|
if archivetype == "xz":
|
|
|
|
archiveflags = "xvf"
|
|
|
|
deps.append("xz-utils")
|
|
|
|
|
2020-01-12 22:55:13 +00:00
|
|
|
for dependency, has_more in lookahead(deps):
|
|
|
|
is_multiline = " \ " if has_more else ""
|
|
|
|
dockerfile += "\u0009{}{}\n".format(dependency, is_multiline)
|
2021-06-09 14:21:05 +00:00
|
|
|
|
2020-06-16 00:20:16 +00:00
|
|
|
# Needed so xz-utils doesn't accumalate
|
|
|
|
if archivetype == "xz":
|
|
|
|
deps.pop()
|
|
|
|
|
2020-01-12 22:55:13 +00:00
|
|
|
dockerfile += "\n"
|
|
|
|
|
|
|
|
dockerfile += "# Download and install Blender\n"
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "RUN wget {} \\\n".format(blender_download_url)
|
|
|
|
dockerfile += "\u0009&& tar -{} {} --strip-components=1 -C /bin \\\n".format(
|
2020-02-25 09:57:45 +00:00
|
|
|
archiveflags, blender_download_url.split("/")[-1])
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "\u0009&& rm -rf {} \\\n".format(
|
2020-01-12 22:55:13 +00:00
|
|
|
blender_download_url.split("/")[-1])
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "\u0009&& rm -rf {}\n\n".format(
|
2020-02-25 09:57:45 +00:00
|
|
|
blender_download_url.split("/")[-1].split(".tar."+archivetype)[0])
|
2020-01-12 22:55:13 +00:00
|
|
|
|
|
|
|
dockerfile += "# Download the Python source since it is not bundled with Blender\n"
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "RUN wget {} \\\n".format(python_download_url)
|
|
|
|
dockerfile += "\u0009&& tar -xzf {} \\\n".format(
|
2021-06-09 14:21:05 +00:00
|
|
|
python_download_url.split("/")[-1])
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "\u0009&& cp -r {}/Include/* $BLENDER_PATH/python/include/{}/ \\\n".format(
|
2021-06-09 14:21:05 +00:00
|
|
|
python_download_url.split("/")[-1].split('.tgz')[0],
|
|
|
|
python_version
|
|
|
|
)
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "\u0009&& rm -rf {} \\\n".format(
|
2021-06-09 14:21:05 +00:00
|
|
|
python_download_url.split("/")[-1])
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "\u0009&& rm -rf {}\n\n".format(
|
2021-06-09 14:21:05 +00:00
|
|
|
python_download_url.split("/")[-1].split('.tgz')[0])
|
2020-01-12 22:55:13 +00:00
|
|
|
|
|
|
|
dockerfile += "# Blender comes with a super outdated version of numpy (which is needed for matplotlib / opencv) so override it with a modern one\n"
|
2021-12-08 15:14:02 +00:00
|
|
|
dockerfile += "RUN rm -rf {}/python/lib/{}/site-packages/numpy\n\n".format(
|
2021-06-09 14:21:05 +00:00
|
|
|
"${BLENDER_PATH}", python_version)
|
2020-01-12 22:55:13 +00:00
|
|
|
|
|
|
|
dockerfile += "# Must first ensurepip to install Blender pip3 and then new numpy\n"
|
|
|
|
dockerfile += "RUN ${BLENDERPY} -m ensurepip && ${BLENDERPIP} install --upgrade pip && ${BLENDERPIP} install numpy\n\n"
|
|
|
|
|
|
|
|
dockerfile += "# Set the working directory\n"
|
|
|
|
dockerfile += "WORKDIR {}".format(workdir)
|
|
|
|
|
|
|
|
return dockerfile
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
|
|
# @todo should be a CLI argument
|
|
|
|
output_folder = "./dist/"
|
|
|
|
|
|
|
|
# Open the manifest file and start generating Dockerfiles
|
|
|
|
with open("./manifest.json", "r") as r:
|
|
|
|
manifest = json.load(r)
|
|
|
|
|
|
|
|
for image in manifest["images"]:
|
|
|
|
# Check if folder exists and if not create one
|
|
|
|
if (os.path.exists(output_folder + image["tag"]) is False):
|
|
|
|
os.mkdir(output_folder + image["tag"])
|
|
|
|
|
|
|
|
dockerfile = create_dockerfile(
|
|
|
|
base_os=image["base_os_image"],
|
|
|
|
title=manifest["title"],
|
|
|
|
author=manifest["author"],
|
|
|
|
env=manifest["env"] + image["env"],
|
|
|
|
deps=manifest["deps"],
|
|
|
|
blender_download_url=image["blender_download_url"],
|
2021-06-09 14:21:05 +00:00
|
|
|
python_download_url=image["python_download_url"],
|
|
|
|
python_version=image["python_version"],
|
2020-01-12 22:55:13 +00:00
|
|
|
workdir="/"
|
|
|
|
)
|
|
|
|
|
|
|
|
with open("{}/Dockerfile".format(output_folder + image["tag"]), "w") as w:
|
|
|
|
w.write(dockerfile)
|