From 460f6893e6f94cc679cc7b9b1d5c9e7836f40358 Mon Sep 17 00:00:00 2001 From: Nadezhda Fedorova Date: Tue, 5 Nov 2024 14:54:29 +0300 Subject: [PATCH] init commit --- README.md | 1 + base-img/alt.yaml | 15 +++ build.py | 279 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 README.md create mode 100644 base-img/alt.yaml create mode 100755 build.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..5fbc748 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# base-image-forge diff --git a/base-img/alt.yaml b/base-img/alt.yaml new file mode 100644 index 0000000..34d6116 --- /dev/null +++ b/base-img/alt.yaml @@ -0,0 +1,15 @@ +FROM scratch + +MAINTAINER \ +[Alexey Shabalin ] \ +[Nadezhda Fedorova /etc/security/limits.d/50-defaults.conf + +# overwrite this with 'CMD []' in a dependent Dockerfile +CMD ["/bin/bash"] diff --git a/build.py b/build.py new file mode 100755 index 0000000..e01cfb9 --- /dev/null +++ b/build.py @@ -0,0 +1,279 @@ +#!/usr/bin/python3 + +import argparse +from pathlib import Path +import urllib.request +import shutil +import subprocess + +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", "loongarch64": "loongarch64" } + +def parse_args(): + registry = "gitea.basealt.ru" + arches = ["amd64", "386", "arm64", "loongarch64", "riscv64"] + names=["alt"] + latest_tag="p10" + org="alt" + branches=["p11", "p10", "Sisyphus"] + exclusions='{"p10": ["loongarch64", "riscv64"], "p11": ["loongarch64", "riscv64"]}' + + 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, + ) + parser.add_argument( + "--org", + default=org, + ) + parser.add_argument( + "--no-push", + ) + parser.add_argument( + "--use-local-source", + ) + parser.add_argument( + "-l", + "--latest", + default=latest_tag, + help="tag images in this branch as latest", + ) + parser.add_argument( + "--old-structure", + ) + parser.add_argument( + "--new-structure", + ) + + 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() + #new_tag = datetime.now().strftime("%Y%m%d") + manifest = f'{args.registry}/{args.org}/{imgname}' + #new_manifest = f"args.registry/{br}/{imgname}" + + 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}') + + run(["podman", + "manifest", + "push", + "--all", + f"{manifest}:{tag}", + f"docker://{manifest}:{tag}", + ]) + + print(f'manifest {manifest}:{tag} is pushed to registry {args.registry}') + + if args.latest == br: + print(f"push latest tag") + run(["podman", + "manifest", + "push", + "--all", + f"{manifest}:{tag}", + f"docker://{manifest}:latest", + ]) + + run(["podman", + "manifest", + "rm", + f"{manifest}:latest", + ]) + + run(["podman", + "manifest", + "rm", + f"{manifest}:{tag}", + ]) + + +if __name__ == "__main__": + main() + + + + + + +