base-image-forge/build.py

301 lines
9.7 KiB
Python
Raw Permalink Normal View History

2024-11-05 14:54:29 +03:00
#!/usr/bin/python3
import argparse
2024-12-11 11:47:49 +03:00
from os import walk
2024-11-05 14:54:29 +03:00
from pathlib import Path
import urllib.request
import shutil
import subprocess
2024-12-11 11:47:49 +03:00
from datetime import datetime
2024-11-05 14:54:29 +03:00
from jinja2 import Template
import json
LIC_C="ALT-SP-Container"
LIC_P="ALT-Container or GPLv3"
LIC_SIS="GPLv3"
builddir= "./"
dockerfile = "Dockerfile"
arch_map= {'amd64': "x86_64", '386': "i586", 'arm64': "aarch64", "riscv64": "riscv64", "loong64": "loongarch64" }
2024-11-05 14:54:29 +03:00
def parse_args():
2024-12-11 11:47:49 +03:00
registry = "gitea.basealt.ru/alt"
arches = ["amd64", "386", "arm64", "loong64", "riscv64"]
2024-11-05 14:54:29 +03:00
names=["alt"]
latest_tag="p10"
2024-12-11 11:47:49 +03:00
#org="alt"
2024-11-05 14:54:29 +03:00
branches=["p11", "p10", "Sisyphus"]
exclusions='{"p10": ["loong64", "riscv64"], "p11": ["loong64", "riscv64"]}'
2024-11-05 14:54:29 +03:00
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"-b",
"--branches",
nargs="+",
default=branches,
choices=branches,
help="list of branches",
)
parser.add_argument(
"-e",
"--exclusions",
default=exclusions,
choices=exclusions,
type=json.loads,
help="list of branches with exclusion arches which no need built",
)
parser.add_argument(
"-n",
"--names",
nargs="+",
default=names,
choices=names,
help="list of names images in templates directory",
)
parser.add_argument(
"-a",
"--arches",
nargs="+",
default=arches,
choices=arches,
help="build images for these architectures",
)
parser.add_argument(
"-r",
"--registry",
default=registry,
)
2024-12-11 11:47:49 +03:00
#parser.add_argument(
# "--org",
# default=org,
#)
2024-11-05 14:54:29 +03:00
parser.add_argument(
"--no-push",
2024-12-11 11:47:49 +03:00
action=argparse.BooleanOptionalAction,
default=False,
2024-11-05 14:54:29 +03:00
)
parser.add_argument(
"--use-local-source",
2024-12-11 11:47:49 +03:00
action=argparse.BooleanOptionalAction,
default=False,
2024-11-05 14:54:29 +03:00
)
parser.add_argument(
"-l",
"--latest",
default=latest_tag,
help="tag images in this branch as latest",
)
parser.add_argument(
2024-12-11 11:47:49 +03:00
"--old-structure",
action=argparse.BooleanOptionalAction,
default=False,
2024-11-05 14:54:29 +03:00
)
parser.add_argument(
"--new-structure",
2024-12-11 11:47:49 +03:00
action=argparse.BooleanOptionalAction,
default=True,
2024-11-05 14:54:29 +03:00
)
args = parser.parse_args()
return args
def run(args):
result = subprocess.run(
args,
capture_output = True,
text = True
)
if result.stderr is not None:
print(result.stderr)
return False
print(result.stdout)
return True
def render_templ(path, lic, source):
templatepath = Path(path)
if templatepath.exists():
template = templatepath.read_text()
rendered = Template(template).render(
source=source,
lic=lic,
)
Path(f'{builddir}{dockerfile}').write_text(rendered + "\n")
def create_dir(name):
try:
path = Path(name)
path.mkdir(parents=True, exist_ok=True)
print(f"Directory '{name}' created successfully.")
except FileExistsError:
print(f"Directory '{name}' already exists.")
except PermissionError:
print(f"Permission denied: Unable to create '{name}'.")
except Exception as e:
print(f"An error occurred: {e}")
def main():
args = parse_args()
#temp dir to dowdload images from ftp or copy locals
tempdir = Path.home().joinpath(".local/share/images")
create_dir(tempdir)
#directory with templates of base inages
walkdir = Path("./base-img")
print(f'walk path = {walkdir.as_posix()}')
#go walk throw directory with templates
for img in walkdir.rglob('*'):
#get template file name
imgname = img.name.replace(".yaml", "")
#if img is file and its file name in templates input list
if img.is_file() and imgname in args.names:
print(f'start building image {img.name}')
for br in args.branches:
#set license string for containers label
print(f'branch {br}')
LIC=LIC_SIS
if "p1" in br:
LIC=LIC_P
elif "c" in br:
LIC=LIC_C
tag = br.lower()
2024-12-11 11:47:49 +03:00
new_tag = datetime.now().strftime("%Y%m%d")
manifest = f'{args.registry}/{imgname}'
new_manifest = f'{args.registry}/{br}/{imgname}'
2024-11-05 14:54:29 +03:00
excl_arches = []
if args.exclusions is not None and args.exclusions.get(br) is not None:
excl_arches = args.exclusions.get(br)
for a in args.arches:
if a in excl_arches:
continue
print(f'arch {a}')
file_arch = arch_map.get(a)
if file_arch is None:
print(f'not found arch name {a} for file in map {arch_map}')
continue
source = f"alt-{tag}-rootfs-minimal-{file_arch}.tar.xz"
if args.use_local_source:
print('use local source mode')
#version for cloud image forge with getting source from local filesystem
source_path=f"/home/builder/proto/{br}/cloud/{file_arch}"
shutil.copy2(source_path + "/" + source, builddir)
else:
print('use download mode from ftp')
sourceurl = f"http://ftp.altlinux.org/pub/distributions/ALTLinux/images/{br}/cloud/{file_arch}/{source}"
print(f"source url {sourceurl}")
download_path = f'{tempdir.as_posix()}/{source}'
print(f"downland path {download_path}")
try:
urllib.request.urlretrieve(sourceurl, download_path)
except Exception as e:
print(f"download error: {e}")
print(f"skip building {img.name} for {br} and {a}")
continue
print(f'file is downloaded to {download_path}')
run(["cp",
download_path,
builddir,
])
#delete downlouded from ftp image source
tempdir.joinpath(source).unlink()
#"--build-arg SOURCE=" + source,
#"--build-arg LICENSE=" + LIC,
print(f'render file {img.as_posix()}, set license {LIC} and source {source}')
render_templ(img.as_posix(), LIC, source)
print(f'template {img.name} is rendered to Dockerfile')
#build manifest
run(["podman",
"build",
f"--manifest={manifest}:{tag}",
f"--platform=linux/{a}",
".",
])
print(f'manifest {manifest}:{tag} for linux/{a} is builded')
#delete image source
p = Path(f'{builddir}{source}')
if p.exists():
p.unlink()
#delete dockerfile
p = Path(f'{builddir}{dockerfile}')
if p.exists():
p.unlink()
if args.no_push:
print('no push mode')
continue
print(f'push manifest to registry {args.registry}')
2024-12-11 11:47:49 +03:00
if args.old_structure:
run(["podman",
"manifest",
"push",
"--all",
f"{manifest}:{tag}",
f"docker://{manifest}:{tag}",
])
if args.new_structure:
run(["podman",
"manifest",
"push",
"--all",
f"{manifest}:{tag}",
f"docker://{new_manifest}:{new_tag}",
])
run(["podman",
"manifest",
"push",
"--all",
f"{manifest}:{tag}",
f"docker://{new_manifest}:latest",
])
2024-11-05 14:54:29 +03:00
print(f'manifest {manifest}:{tag} is pushed to registry {args.registry}')
if args.latest == br:
print(f"push latest tag")
2024-12-11 11:47:49 +03:00
if args.old_structure:
run(["podman",
2024-11-05 14:54:29 +03:00
"manifest",
"push",
"--all",
f"{manifest}:{tag}",
f"docker://{manifest}:latest",
])
run(["podman",
"manifest",
"rm",
f"{manifest}:{tag}",
])
if __name__ == "__main__":
main()