vauxite-build/ostree-engine
2024-02-01 19:25:46 -06:00

250 lines
6.9 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import datetime as dt
import os
import shlex
import shutil
import subprocess
import sys
from glob import glob
from pathlib import Path
if os.geteuid() != 0:
exit("Please run this script with sudo")
BASE_DIR = Path("/home/jas/vauxite")
OSTREE_FILES_DIR = BASE_DIR.joinpath("src")
CACHE_DIR = BASE_DIR.joinpath(".cache")
BUILD_REPO = BASE_DIR.joinpath(".build-repo")
SOURCE_REPO = BASE_DIR.joinpath(".source-repo")
DEPLOY_REPO = BASE_DIR.joinpath(".deploy-repo")
WK_DIR = BASE_DIR.joinpath(".tmp")
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument(
"--nodeploy",
required=False,
action="store_true",
help="Do not deploy repo to web server root",
)
group.add_argument(
"--onlydeploy",
required=False,
action="store_true",
help="Skip the build steps and only deploy repo to web server root",
)
parser.add_argument(
"--sourceurl",
required=False,
action="store",
default="https://pagure.io/workstation-ostree-config",
help="URL for source repo",
)
parser.add_argument(
"--sourcebranch",
required=True,
action="store",
help="Branch of source repo",
)
parser.add_argument(
"--treefile",
required=False,
action="store",
default="vauxite.yaml",
help="Treefile to use for rpm-ostree",
)
parser.add_argument(
"--ostreebranch",
required=True,
action="store",
help="Name of ref branch",
)
parser.add_argument(
"--destrepo",
required=False,
action="store",
default="/srv/repo",
help="Destination for rsync-repos",
)
args = parser.parse_args()
def print_log(msg: str):
if sys.stdout.isatty():
log_date = dt.datetime.now().isoformat(" ", "seconds")
print("%s: %s" % (log_date, msg))
else:
print(msg)
def handle_err():
print_log("ERROR:")
print("{}" % sys.exc_info()[0])
print("{}" % sys.exc_info()[1])
print("{}" % sys.exc_info()[2])
exit(1)
def clean_wk_dir():
try:
print_log("Clean working directory")
shutil.rmtree(CACHE_DIR)
shutil.rmtree(SOURCE_REPO)
shutil.rmtree(WK_DIR)
for dir in glob("/tmp/rpmostree*", recursive=True):
shutil.rmtree(dir)
except FileNotFoundError as ferr:
pass
except Exception:
handle_err()
def run_proc(cmd: str, capture_output=False) -> subprocess.CompletedProcess:
try:
if capture_output:
return subprocess.run(shlex.split(cmd), capture_output=True, text=True)
return subprocess.run(shlex.split(cmd), check=True, text=True)
except subprocess.CalledProcessError:
handle_err()
def prepare_build_env():
clean_wk_dir()
print_log("Ensure CACHE_DIR exists")
CACHE_DIR.mkdir(exist_ok=True)
print_log("Ensure WK_DIR exists")
WK_DIR.mkdir(exist_ok=True)
print_log("Remove previous BUILD_REPO if it exists")
shutil.rmtree(BUILD_REPO, ignore_errors=True)
print_log("Initialize ostree repo in bare-user mode")
run_proc(f"ostree --repo={BUILD_REPO} init --mode=bare-user")
if not DEPLOY_REPO.exists():
print_log("Deploy repo not found; initialize new deploy repo in archive mode")
run_proc(f"ostree --repo={BUILD_REPO} init --mode=archive")
else:
print_log("Pull existing deploy repo into local build repo")
run_proc(
f"ostree --repo={BUILD_REPO} pull-local --depth=2 {DEPLOY_REPO} {args.ostreebranch}"
)
if not SOURCE_REPO.exists():
print_log(
f"Clone branch {args.sourcebranch} of {args.sourceurl} into SOURCE_REPO"
)
run_proc(f"git clone -b {args.sourcebranch} {args.sourceurl} {SOURCE_REPO}")
print_log("Copy SOURCE_REPO contents into WK_DIR")
run_proc(f"rsync -aAX {SOURCE_REPO}/ {WK_DIR}")
print_log("Remove upstream xfce-desktop-pkgs.yaml from WK_DIR")
WK_DIR.joinpath("xfce-desktop-pkgs.yaml").unlink(missing_ok=True)
print_log("Copy OSTREE_FILES_DIR contents into WK_DIR")
run_proc(f"rsync -aAX {OSTREE_FILES_DIR}/ {WK_DIR}")
def compose_ostree():
print_log("Compose ostree")
time_fmt = dt.datetime.now(dt.timezone.utc).strftime("%Y-%m-%dT%H%M%S")
run_proc(
subprocess.list2cmdline(
[
"rpm-ostree",
"compose",
"tree",
"--unified-core",
f"--cachedir={CACHE_DIR}",
f"--repo={BUILD_REPO}",
f"--add-metadata-string=Build={time_fmt}",
WK_DIR.joinpath(args.treefile),
]
)
)
def prepare_deploy():
print_log("Prune refs older than 30 days")
run_proc(
f"ostree --repo={BUILD_REPO} prune --refs-only --keep-younger-than='30 days ago'"
)
print_log("Pull new ostree commit into DEPLOY_REPO")
run_proc(
f"ostree --repo={DEPLOY_REPO} pull-local --depth=1 {BUILD_REPO} {args.ostreebranch}"
)
print_log("Remove local build repo")
shutil.rmtree(BUILD_REPO, ignore_errors=True)
print_log("Check filesystem for errors")
run_proc(f"ostree --repo={DEPLOY_REPO} fsck {args.ostreebranch}")
def generate_deltas():
print_log("Check if main ref has parent")
check_parent = run_proc(
f"ostree --repo={DEPLOY_REPO} show {args.ostreebranch}", capture_output=True
)
if not check_parent.stderr:
print_log("Generate static delta from main ref's parent")
run_proc(
f"ostree --repo={DEPLOY_REPO} static-delta generate {args.ostreebranch}"
)
print_log("Check if main ref's parent has parent")
check_gparent = run_proc(
f"ostree --repo={DEPLOY_REPO} show {args.ostreebranch}^^",
capture_output=True,
)
if not check_gparent.stderr:
print_log("Generate static delta from parent of main ref's parent")
run_proc(
f"ostree --repo={DEPLOY_REPO} static-delta generate --from={args.ostreebranch}^^ --to={args.ostreebranch}"
)
else:
print_log("Main ref's parent has no parent. No deltas generated.")
else:
print_log("Main ref has no parent. No deltas generated.")
def update_summary():
print_log("Update summary file of DEPLOY_REPO")
run_proc(f"ostree --repo={DEPLOY_REPO} summary -u")
def deploy_repo():
print_log("Deploying repo to web server root")
if DEPLOY_REPO.exists():
run_proc(f"{BASE_DIR}/rsync-repos --src {DEPLOY_REPO} --dest {args.destrepo}")
else:
print_log("DEPLOY_REPO not found. Not deploying to web server")
if __name__ == "__main__":
if args.nodeploy:
prepare_build_env()
compose_ostree()
prepare_deploy()
generate_deltas()
update_summary()
elif args.onlydeploy:
deploy_repo()
else:
prepare_build_env()
compose_ostree()
prepare_deploy()
generate_deltas()
update_summary()
deploy_repo()