Refactor; add sign_commit()

This commit is contained in:
Jeffrey Serio 2024-02-05 21:59:46 -06:00
parent d3066bf6a5
commit ca05f01dd6
6 changed files with 146 additions and 128 deletions

View File

@ -1,19 +1,42 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse """ostree-engine
Usage:
ostree-engine ([--source-branch=BRANCH] [--source-url=URL] | --no-download-sources)
[--ostree-branch=REF] [--treefile=TREEFILE]
[--dest-repo=PATH] [--gpg-passfile=PATH]
[--no-deploy] [--no-clean]
ostree-engine --version
Options:
--no-deploy Do not deploy resulting ostree repo to web server root.
--no-clean Do not clean the working directory, i.e. .cache, .build-repo, .source-repo, .tmp
--no-download-sources Do not download source repo.
--source-url=URL URL for source repo. [default: https://pagure.io/workstation-ostree-config]
--source-branch=BRANCH Branch of the source repo.
--treefile=TREEFILE YAML or JSON treefile to use for rpm-ostree.
--ostree-branch=REF Name of the ref branch.
--dest-repo=PATH Local or remote filesystem destination for rsync-repos. Usually a web server root. [default: /srv/repo]
--gpg-passfile=PATH Path to JSON file containing GPG key-id and passphrase (for auto signing commits).
--version Print version information.
"""
import datetime as dt import datetime as dt
import json
import os import os
import shlex import shlex
import shutil import shutil
import subprocess import subprocess
import sys import sys
from docopt import docopt
from glob import glob from glob import glob
from pathlib import Path from pathlib import Path
if os.geteuid() != 0: if os.geteuid() != 0:
exit("Please run this script with sudo") exit("Please run this script with sudo")
BASE_DIR = Path("/home/jas/vauxite") BASE_DIR = Path("/var/local/vauxite")
OSTREE_FILES_DIR = BASE_DIR.joinpath("src") OSTREE_FILES_DIR = BASE_DIR.joinpath("src")
CACHE_DIR = BASE_DIR.joinpath(".cache") CACHE_DIR = BASE_DIR.joinpath(".cache")
BUILD_REPO = BASE_DIR.joinpath(".build-repo") BUILD_REPO = BASE_DIR.joinpath(".build-repo")
@ -21,56 +44,6 @@ SOURCE_REPO = BASE_DIR.joinpath(".source-repo")
DEPLOY_REPO = BASE_DIR.joinpath(".deploy-repo") DEPLOY_REPO = BASE_DIR.joinpath(".deploy-repo")
WK_DIR = BASE_DIR.joinpath(".tmp") 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): def print_log(msg: str):
if sys.stdout.isatty(): if sys.stdout.isatty():
@ -82,9 +55,9 @@ def print_log(msg: str):
def handle_err(): def handle_err():
print_log("ERROR:") print_log("ERROR:")
print("{}" % sys.exc_info()[0]) print(f"{sys.exc_info()[0]}")
print("{}" % sys.exc_info()[1]) print(f"{sys.exc_info()[1]}")
print("{}" % sys.exc_info()[2]) print(f"{sys.exc_info()[2]}")
exit(1) exit(1)
@ -92,7 +65,8 @@ def clean_wk_dir():
try: try:
print_log("Clean working directory") print_log("Clean working directory")
shutil.rmtree(CACHE_DIR) shutil.rmtree(CACHE_DIR)
shutil.rmtree(SOURCE_REPO) if not args.get("--no-download-sources"):
shutil.rmtree(SOURCE_REPO)
shutil.rmtree(WK_DIR) shutil.rmtree(WK_DIR)
for dir in glob("/tmp/rpmostree*", recursive=True): for dir in glob("/tmp/rpmostree*", recursive=True):
shutil.rmtree(dir) shutil.rmtree(dir)
@ -113,7 +87,8 @@ def run_proc(cmd: str, capture_output=False) -> subprocess.CompletedProcess:
def prepare_build_env(): def prepare_build_env():
clean_wk_dir() if not args.get("--no-clean"):
clean_wk_dir()
print_log("Ensure CACHE_DIR exists") print_log("Ensure CACHE_DIR exists")
CACHE_DIR.mkdir(exist_ok=True) CACHE_DIR.mkdir(exist_ok=True)
@ -133,14 +108,14 @@ def prepare_build_env():
else: else:
print_log("Pull existing deploy repo into local build repo") print_log("Pull existing deploy repo into local build repo")
run_proc( run_proc(
f"ostree --repo={BUILD_REPO} pull-local --depth=2 {DEPLOY_REPO} {args.ostreebranch}" f"ostree --repo={BUILD_REPO} pull-local --depth=2 {DEPLOY_REPO} {args.get("--ostree-branch")}"
) )
if not SOURCE_REPO.exists(): if not SOURCE_REPO.exists():
print_log( print_log(
f"Clone branch {args.sourcebranch} of {args.sourceurl} into SOURCE_REPO" f"Clone branch {args.get("--source-branch")} of {args.get("--source-url")} into SOURCE_REPO"
) )
run_proc(f"git clone -b {args.sourcebranch} {args.sourceurl} {SOURCE_REPO}") run_proc(f"git clone -b {args.get("--source-branch")} {args.get("--source-url")} {SOURCE_REPO}")
print_log("Copy SOURCE_REPO contents into WK_DIR") print_log("Copy SOURCE_REPO contents into WK_DIR")
run_proc(f"rsync -aAX {SOURCE_REPO}/ {WK_DIR}") run_proc(f"rsync -aAX {SOURCE_REPO}/ {WK_DIR}")
@ -165,12 +140,30 @@ def compose_ostree():
f"--cachedir={CACHE_DIR}", f"--cachedir={CACHE_DIR}",
f"--repo={BUILD_REPO}", f"--repo={BUILD_REPO}",
f"--add-metadata-string=Build={time_fmt}", f"--add-metadata-string=Build={time_fmt}",
WK_DIR.joinpath(args.treefile), WK_DIR.joinpath(args.get("--treefile")),
] ]
) )
) )
def sign_commit():
commit_id = subprocess.run(["ostree", f"--repo={DEPLOY_REPO}", "rev-parse", args.get("--ostree-branch")], capture_output=True, text=True)
with open(args.get("--gpg-passfile"), "r") as json_file:
gpg_data = json.loads(json_file.read())
print_log(f"Signing rpm-ostree commit {commit_id.stdout} with GPG key-id {gpg_data.get("gpg-id")}")
gpg_cmd = f"echo {gpg_data.get("passphrase")} | gpg --batch --always-trust --yes --passphrase-fd 0 --pinentry-mode=loopback -s $(mktemp)"
run_gpg_cmd = subprocess.Popen(gpg_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = run_gpg_cmd.communicate()[0]
if output:
print_log(f"Error unlocking GPG keyring: {output}")
exit(99)
run_proc(f"ostree --repo={DEPLOY_REPO} gpg-sign {commit_id.stdout} {gpg_data.get("gpg-id")}")
def prepare_deploy(): def prepare_deploy():
print_log("Prune refs older than 30 days") print_log("Prune refs older than 30 days")
run_proc( run_proc(
@ -179,38 +172,38 @@ def prepare_deploy():
print_log("Pull new ostree commit into DEPLOY_REPO") print_log("Pull new ostree commit into DEPLOY_REPO")
run_proc( run_proc(
f"ostree --repo={DEPLOY_REPO} pull-local --depth=1 {BUILD_REPO} {args.ostreebranch}" f"ostree --repo={DEPLOY_REPO} pull-local --depth=1 {BUILD_REPO} {args.get("--ostree-branch")}"
) )
print_log("Remove local build repo") print_log("Remove local build repo")
shutil.rmtree(BUILD_REPO, ignore_errors=True) shutil.rmtree(BUILD_REPO, ignore_errors=True)
print_log("Check filesystem for errors") print_log("Check filesystem for errors")
run_proc(f"ostree --repo={DEPLOY_REPO} fsck {args.ostreebranch}") run_proc(f"ostree --repo={DEPLOY_REPO} fsck {args.get("--ostree-branch")}")
def generate_deltas(): def generate_deltas():
print_log("Check if main ref has parent") print_log("Check if main ref has parent")
check_parent = run_proc( check_parent = run_proc(
f"ostree --repo={DEPLOY_REPO} show {args.ostreebranch}", capture_output=True f"ostree --repo={DEPLOY_REPO} show {args.get("--ostree-branch")}", capture_output=True
) )
if not check_parent.stderr: if not check_parent.stderr:
print_log("Generate static delta from main ref's parent") print_log("Generate static delta from main ref's parent")
run_proc( run_proc(
f"ostree --repo={DEPLOY_REPO} static-delta generate {args.ostreebranch}" f"ostree --repo={DEPLOY_REPO} static-delta generate {args.get("--ostree-branch")}"
) )
print_log("Check if main ref's parent has parent") print_log("Check if main ref's parent has parent")
check_gparent = run_proc( check_gparent = run_proc(
f"ostree --repo={DEPLOY_REPO} show {args.ostreebranch}^^", f"ostree --repo={DEPLOY_REPO} show {args.get("--ostree-branch")}^^",
capture_output=True, capture_output=True,
) )
if not check_gparent.stderr: if not check_gparent.stderr:
print_log("Generate static delta from parent of main ref's parent") print_log("Generate static delta from parent of main ref's parent")
run_proc( run_proc(
f"ostree --repo={DEPLOY_REPO} static-delta generate --from={args.ostreebranch}^^ --to={args.ostreebranch}" f"ostree --repo={DEPLOY_REPO} static-delta generate --from={args.get("--ostree-branch")}^^ --to={args.get("--ostree-branch")}"
) )
else: else:
print_log("Main ref's parent has no parent. No deltas generated.") print_log("Main ref's parent has no parent. No deltas generated.")
@ -226,24 +219,19 @@ def update_summary():
def deploy_repo(): def deploy_repo():
print_log("Deploying repo to web server root") print_log("Deploying repo to web server root")
if DEPLOY_REPO.exists(): if DEPLOY_REPO.exists():
run_proc(f"{BASE_DIR}/rsync-repos --src {DEPLOY_REPO} --dest {args.destrepo}") run_proc(f"{BASE_DIR}/rsync-repos --src {DEPLOY_REPO} --dest {args.get("--dest-repo")}")
else: else:
print_log("DEPLOY_REPO not found. Not deploying to web server") print_log("DEPLOY_REPO not found. Not deploying to web server")
if __name__ == "__main__": if __name__ == "__main__":
if args.nodeploy: args = docopt(__doc__, help=True, version="ostree-engine 0.1.0")
prepare_build_env() prepare_build_env()
compose_ostree() compose_ostree()
prepare_deploy() prepare_deploy()
generate_deltas() generate_deltas()
update_summary() if args.get("--gpg-passfile"):
elif args.onlydeploy: sign_commit()
deploy_repo() update_summary()
else: if not args.get("--no-deploy"):
prepare_build_env()
compose_ostree()
prepare_deploy()
generate_deltas()
update_summary()
deploy_repo() deploy_repo()

24
src/borgmatic-config.yaml Normal file
View File

@ -0,0 +1,24 @@
source_directories:
- /var/home
- /etc
- /usr/local
repositories:
- path: /srv/backup/localhost
label: localhost-backup
exclude_caches: true
compression: zstd
relocated_repo_access_is_ok: true
unknown_unencrypted_repo_access_is_ok: true
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
keep_yearly: 1
checks:
- name: repository
frequency: 2 weeks
- name: archives
frequency: 2 weeks
check_repositories:
- /srv/backup/localhost
check_last: 3
encryption_passcommand: secret-tool lookup borg-repo localhost

12
src/user-dirs.defaults Normal file
View File

@ -0,0 +1,12 @@
# Default settings for user directories
#
# The values are relative pathnames from the home directory and
# will be translated on a per-path-element basis into the users locale
DESKTOP=desktop
DOWNLOAD=downloads
TEMPLATES=
PUBLICSHARE=sync
DOCUMENTS=sync/documents
MUSIC=
PICTURES=sync/pictures
VIDEOS=

View File

@ -1,10 +0,0 @@
[copr:copr.fedorainfracloud.org:varlad:zellij]
name=Copr repo for zellij owned by varlad
baseurl=https://download.copr.fedorainfracloud.org/results/varlad/zellij/fedora-39-x86_64/
type=rpm-md
skip_if_unavailable=True
gpgcheck=1
gpgkey=https://download.copr.fedorainfracloud.org/results/varlad/zellij/pubkey.gpg
repo_gpgcheck=0
enabled=1
enabled_metadata=1

40
src/vauxite.json Normal file
View File

@ -0,0 +1,40 @@
{
"ref": "vauxite/f39/x86_64/main",
"include": "xfce-desktop-pkgs.yaml",
"packages": [
"borgbackup",
"borgmatic",
"cronie",
"distrobox",
"fedora-release-ostree-desktop",
"fontconfig-enhanced-defaults",
"fontconfig-font-replacements",
"rofi",
"snapper",
"vim-default-editor",
"xfce4-cpugraph-plugin",
"xfce4-genmon-plugin",
"xfce4-weather-plugin"
],
"repos": [
"fedora",
"updates",
"copr:copr.fedorainfracloud.org:hyperreal:better_fonts"
],
"add-files": [
[
"borgmatic-config.yaml",
"/etc/borgmatic/config.yaml"
],
[
"user-dirs.defaults",
"/etc/xdg/user-dirs.defaults"
]
],
"units": [
"snapper-cleanup.timer",
"snapper-timeline.timer"
],
"cliwrap": true,
"readonly-executables": true
}

View File

@ -1,36 +0,0 @@
---
ref: vauxite/f39/x86_64/main
include: xfce-desktop-pkgs.yaml
rojig:
name: vauxite
summary: "Vauxite base image"
license: MIT
description: "OSTree-based Fedora Xfce Desktop"
packages:
- distrobox
- fedora-release-ostree-desktop
- rofi
- snapper
- vim-default-editor
- xfce4-cpugraph-plugin
- xfce4-genmon-plugin
- xfce4-weather-plugin
repo-packages:
- packages:
- fontconfig-enhanced-defaults
- fontconfig-font-replacements
repo: copr:copr.fedorainfracloud.org:hyperreal:better_fonts
- packages:
- zellij
repo: copr:copr.fedorainfracloud.org:varlad:zellij
repos:
- fedora
- updates
- copr:copr.fedorainfracloud.org:hyperreal:better_fonts
- copr:copr.fedorainfracloud.org:varlad:zellij