301 lines
9.7 KiB
Python
Executable File
301 lines
9.7 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import argparse
|
|
from os import walk
|
|
from pathlib import Path
|
|
import urllib.request
|
|
import shutil
|
|
import subprocess
|
|
from datetime import datetime
|
|
|
|
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" }
|
|
|
|
def parse_args():
|
|
registry = "gitea.basealt.ru/alt"
|
|
arches = ["amd64", "386", "arm64", "loong64", "riscv64"]
|
|
names=["alt"]
|
|
latest_tag="p10"
|
|
#org="alt"
|
|
branches=["p11", "p10", "Sisyphus"]
|
|
exclusions='{"p10": ["loong64", "riscv64"], "p11": ["loong64", "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",
|
|
action=argparse.BooleanOptionalAction,
|
|
default=False,
|
|
)
|
|
parser.add_argument(
|
|
"--use-local-source",
|
|
action=argparse.BooleanOptionalAction,
|
|
default=False,
|
|
)
|
|
parser.add_argument(
|
|
"-l",
|
|
"--latest",
|
|
default=latest_tag,
|
|
help="tag images in this branch as latest",
|
|
)
|
|
parser.add_argument(
|
|
"--old-structure",
|
|
action=argparse.BooleanOptionalAction,
|
|
default=False,
|
|
)
|
|
parser.add_argument(
|
|
"--new-structure",
|
|
action=argparse.BooleanOptionalAction,
|
|
default=True,
|
|
)
|
|
|
|
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}/{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}')
|
|
|
|
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",
|
|
])
|
|
|
|
print(f'manifest {manifest}:{tag} is pushed to registry {args.registry}')
|
|
|
|
if args.latest == br:
|
|
print(f"push latest tag")
|
|
if args.old_structure:
|
|
run(["podman",
|
|
"manifest",
|
|
"push",
|
|
"--all",
|
|
f"{manifest}:{tag}",
|
|
f"docker://{manifest}:latest",
|
|
])
|
|
|
|
run(["podman",
|
|
"manifest",
|
|
"rm",
|
|
f"{manifest}:{tag}",
|
|
])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|