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 "
2024-11-08 16:35:47 +03:00
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 "
2024-11-08 16:35:47 +03:00
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 " ]
2024-11-08 16:35:47 +03:00
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 ( )