Files
jellyfin-packaging/build.py

320 lines
11 KiB
Python
Raw Normal View History

2024-02-07 02:43:35 -05:00
#!/usr/bin/env python3
# build.py - Build packages in a Docker wrapper
#
# Part of the Jellyfin CI system
###############################################################################
from datetime import datetime
from email.utils import format_datetime, localtime
from os import system
import os.path
from subprocess import run, PIPE
import sys
from git import Repo
try:
target_release = sys.argv[1]
except IndexError:
target_release = "master"
# Determine top level directory of this repository ("jellyfin-packaging")
revparse = run(["git", "rev-parse", "--show-toplevel"], stdout=PIPE)
repo_root_dir = revparse.stdout.decode().strip()
def build_package_deb(jvers, btype, barch, bvers):
try:
ostype = btype if btype in configurations.keys() else None
if ostype is None:
raise ValueError(f"{btype} is not a valid OS type in {configurations.keys()}")
osversion = bvers if bvers in configurations[btype]['releases'] else None
if osversion is None:
raise ValueError(f"{bvers} is not a valid {btype} version in {configurations[btype]['releases']}")
PARCH = configurations[btype]['archmaps'][barch]['PARCH'] if barch in configurations[btype]['archmaps'].keys() else None
if PARCH is None:
raise ValueError(f"{barch} is not a valid {btype} {bvers} architecture in {configurations[btype]['archmaps'].keys()}")
2024-02-07 02:43:35 -05:00
except Exception as e:
print(f"Invalid/unsupported arguments: {e}")
exit(1)
# Set the dockerfile
dockerfile = configurations[btype]["dockerfile"]
2024-02-07 02:43:35 -05:00
# Set the cross-gcc version
crossgccvers = configurations[btype]['cross-gcc'][bvers]
# Prepare the debian changelog file
changelog_src = f"{repo_root_dir}/debian/changelog.in"
changelog_dst = f"{repo_root_dir}/debian/changelog"
with open(changelog_src) as fh:
changelog = fh.read()
if "v" in jvers:
comment = f"Jellyfin release {jvers}, see https://github.com/jellyfin/jellyfin/releases/{jvers} for details."
else:
comment = f"Jellyin unstable release {jvers}."
changelog = changelog.format(
package_version=jvers.replace('v', ''),
package_build=f"{btype[:3]}{bvers.replace('.', '')}",
release_comment=comment,
release_date=format_datetime(localtime())
)
with open(changelog_dst, "w") as fh:
fh.write(changelog)
# Use a unique docker image name for consistency
imagename = f"{configurations[btype]['imagename']}-{jvers}_{barch}-{btype}-{bvers}"
2024-02-07 02:43:35 -05:00
# Build the dockerfile and packages
os.system(f"docker build --progress=plain --build-arg PTYPE={ostype} --build-arg PVERSION={osversion} --build-arg PARCH={PARCH} --build-arg GCC_VERSION={crossgccvers} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}")
2024-02-11 14:21:47 -05:00
os.system(f"docker run --rm --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{btype}:/dist --name {imagename} {imagename}")
2024-02-07 02:43:35 -05:00
def build_package(jvers, btype, barch, bvers):
2024-02-07 02:43:35 -05:00
pass
2024-02-11 14:34:21 -05:00
def build_linux(jvers, btype, barch, _bvers):
try:
PARCH = configurations[btype]['archmaps'][barch]['PARCH'] if barch in configurations[btype]['archmaps'].keys() else None
if PARCH is None:
raise ValueError(f"{barch} is not a valid {btype} {bvers} architecture in {configurations[btype]['archmaps'].keys()}")
DARCH = configurations[btype]['archmaps'][barch]['DARCH']
except Exception as e:
print(f"Invalid/unsupported arguments: {e}")
exit(1)
# Set the dockerfile
dockerfile = configurations[btype]["dockerfile"]
# Use a unique docker image name for consistency
imagename = f"{configurations[btype]['imagename']}-{jvers}_{barch}-{btype}"
# Build the dockerfile and packages
os.system(f"docker build --progress=plain --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}")
os.system(f"docker run --rm --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{btype}:/dist --env JVERS={jvers} --env PARCH={PARCH} --env DARCH={DARCH} --name {imagename} {imagename}")
2024-02-11 14:21:47 -05:00
def build_portable(jvers, btype, _barch, _bvers):
# Set the dockerfile
dockerfile = configurations[btype]["dockerfile"]
# Use a unique docker image name for consistency
imagename = f"{configurations[btype]['imagename']}-{jvers}_{btype}"
# Set the archive type (tar-gz or zip)
archivetypes = f"{configurations[btype]['archivetypes']}"
# Build the dockerfile and packages
os.system(f"docker build --progress=plain --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}")
os.system(f"docker run --rm --volume {repo_root_dir}:/jellyfin --volume {repo_root_dir}/out/{btype}:/dist --env JVERS={jvers} --env ARCHIVE_TYPES={archivetypes} --name {imagename} {imagename}")
def build_docker(jvers, btype, _barch, _bvers):
print("> Building Docker images...")
print()
# We build all architectures simultaneously to push a single tag, so no conditional checks
architectures = configurations['docker']['archmaps'].keys()
# Set the dockerfile
dockerfile = configurations[btype]["dockerfile"]
# Determine if this is a "latest"-type image (v in jvers) or not
if "v" in jvers:
is_latest = True
version_suffix = True
else:
is_latest = False
version_suffix = False
# Set today's date in a convenient format for use as an image suffix
date = datetime.now().strftime("%Y%m%d-%H%M%S")
images = list()
for _barch in architectures:
print(f">> Building Docker image for {_barch}...")
print()
# Get our ARCH variables from the archmaps
PARCH = configurations['docker']['archmaps'][_barch]['PARCH']
DARCH = configurations['docker']['archmaps'][_barch]['DARCH']
QARCH = configurations['docker']['archmaps'][_barch]['QARCH']
BARCH = configurations['docker']['archmaps'][_barch]['BARCH']
# Use a unique docker image name for consistency
if version_suffix:
imagename = f"{configurations['docker']['imagename']}:{jvers}-{_barch}.{date}"
else:
imagename = f"{configurations['docker']['imagename']}:{jvers}-{_barch}"
# Clean up any existing qemu static image
os.system(f"docker run --rm --privileged multiarch/qemu-user-static:register --reset")
print()
# Build the dockerfile
os.system(f"docker build --no-cache --progress=plain --build-arg PARCH={PARCH} --build-arg DARCH={DARCH} --build-arg QARCH={QARCH} --build-arg BARCH={BARCH} --file {repo_root_dir}/{dockerfile} --tag {imagename} {repo_root_dir}")
images.append(imagename)
print()
# Build the manifests
print(f">> Building Docker manifests...")
if version_suffix:
print(f">>> Building dated version manifest...")
os.system(f"docker manifest create --amend {configurations['docker']['imagename']}:{jvers}.{date} {' '.join(images)}")
print(f">>> Building version manifest...")
os.system(f"docker manifest create --amend {configurations['docker']['imagename']}:{jvers} {' '.join(images)}")
if is_latest:
print(f">>> Building latest manifest...")
os.system(f"docker manifest create --amend {configurations['docker']['imagename']}:latest {' '.join(images)}")
2024-02-07 02:43:35 -05:00
# Define a map of possible configurations
configurations = {
"debian": {
"def": build_package_deb,
"dockerfile": "debian/docker/Dockerfile",
"imagename": "jellyfin-builder",
"archmaps": {
"amd64": {
"PARCH": "amd64",
},
"arm64": {
"PARCH": "arm64",
},
"armhf": {
"PARCH": "armhf",
},
2024-02-07 02:43:35 -05:00
},
"releases": [ "11", "12" ],
"cross-gcc": {
"11": "10",
"12": "12",
},
},
"ubuntu": {
"def": build_package_deb,
"dockerfile": "debian/docker/Dockerfile",
"imagename": "jellyfin-builder",
"archmaps": {
"amd64": {
"PARCH": "amd64",
},
"arm64": {
"PARCH": "arm64",
},
"armhf": {
"PARCH": "armhf",
},
2024-02-07 02:43:35 -05:00
},
"releases": [ "20.04", "22.04" ],
"cross-gcc": {
"20.04": "10",
"22.04": "12",
"24.04": "12",
},
},
"fedora": {
"def": build_package,
},
"centos": {
"def": build_package,
},
"linux": {
2024-02-11 14:34:21 -05:00
"def": build_linux,
"dockerfile": "linux/Dockerfile",
"imagename": "jellyfin-builder",
"archmaps": {
"amd64": {
"PARCH": "amd64",
"DARCH": "x64",
},
"amd64-musl": {
"PARCH": "amd64-musl",
"DARCH": "musl-x64",
},
"arm64": {
"PARCH": "arm64",
"DARCH": "arm64",
},
"arm64-musl": {
"PARCH": "arm64-musl",
"DARCH": "musl-arm64",
},
"armhf": {
"PARCH": "armhf",
"DARCH": "arm",
},
},
2024-02-07 02:43:35 -05:00
},
"windows": {
"def": build_package,
},
"macos": {
"def": build_package,
},
"portable": {
2024-02-11 14:21:47 -05:00
"def": build_portable,
"dockerfile": "portable/Dockerfile",
"imagename": "jellyfin-builder",
"archivetypes": "tar,zip",
2024-02-07 02:43:35 -05:00
},
"docker": {
"def": build_docker,
"dockerfile": "docker/Dockerfile",
"imagename": "jellyfin/jellyfin",
"archmaps": {
# This insanity is needed because no one can agree on how to name architectures :-(
"amd64": {
"PARCH": "amd64",
"DARCH": "x64",
"QARCH": "x86_64",
"BARCH": "amd64",
},
"arm64": {
"PARCH": "arm64",
"DARCH": "arm64",
"QARCH": "aarch64",
"BARCH": "arm64v8",
},
"armhf": {
"PARCH": "armhf",
"DARCH": "arm",
"QARCH": "arm",
"BARCH": "arm32v7",
},
}
2024-02-07 02:43:35 -05:00
},
}
def usage():
print(f"{sys.argv[0]} JVERS BTYPE [BARCH] [BVERS]")
2024-02-07 02:43:35 -05:00
print(f" JVERS: The Jellyfin version being built")
print(f" BTYPE: A valid build OS type")
print(f" BARCH: A valid build OS CPU architecture (packaged OS types only)")
2024-02-07 02:43:35 -05:00
print(f" BVERS: A valid build OS version (packaged OS types only)")
try:
jvers = sys.argv[1]
btype = sys.argv[2]
2024-02-07 02:43:35 -05:00
except IndexError:
usage()
exit(1)
try:
barch = sys.argv[3]
except IndexError:
barch = None
2024-02-07 02:43:35 -05:00
try:
bvers = sys.argv[4]
except IndexError:
bvers = None
configurations[btype]['def'](jvers, btype, barch, bvers)