diff options
| -rw-r--r-- | AGENTS.md | 16 | ||||
| -rw-r--r-- | README.md | 18 | ||||
| -rw-r--r-- | SBo-danix.local.example | 8 | ||||
| -rw-r--r-- | gpg-agent.conf | 10 | ||||
| -rw-r--r-- | make-local.py | 31 | ||||
| -rwxr-xr-x | sb-publish | 35 | ||||
| -rw-r--r-- | slackrepo_SBo-danix.conf | 310 |
7 files changed, 427 insertions, 1 deletions
@@ -43,6 +43,22 @@ Weekly flow: regenerate repo with this script; `sbshadow` runs `mkhint -F` at th Do **not** add a hintfile generator to this script — it would create a second owner of HINT_DIR and fight mkhint's `.bak` handling. +## slackrepo config and publishing + +`slackrepo_SBo-danix.conf` is the source of truth for `/etc/slackrepo/slackrepo_SBo-danix.conf`. +Never commit site values (rsync target, passwords, Gotify URL or key, hostnames): they live in the +root-only `/etc/slackrepo/SBo-danix.local`, which the config sources; `SBo-danix.local.example` +documents the keys with placeholders. slackrepo unsets only its own known config names after +sourcing (`varnames`, `genrepnames`, `initnames`, `OPTIONLIST` in `slackrepo`), so the `SB_*` +variables survive into the hooks, which run as functions in slackrepo's shell. + +Gates in the hooks: `SBB_PUBLISH=n` holds back `rsync_push_hook` (sb-builder sets it for +unpublished runs; `sb-publish` pushes later). `--preview` does not set `OPT_DRY_RUN` +(`cmdfunctions.sh`), so hooks check `OPT_PREVIEW` too. To check the gates without a VM, source +the config with `/usr/local/bin/` rewritten to a stub dir (stub `sb-publish`, +`manage-packages.sh`, `curl`) and call the hooks under each `OPT_DRY_RUN`/`OPT_PREVIEW`/ +`SBB_PUBLISH` combination. + ## Run ```bash @@ -25,6 +25,19 @@ upstream tree), so the overlay version wins. `--yes` applies without asking, `--no` only shows the plan (for non-interactive callers such as sb-builder). `SBO_REPO` overrides the repo path. +- `slackrepo_SBo-danix.conf`: the slackrepo config for this repo + (`/etc/slackrepo/`). Site values (rsync target, Gotify URL and key) are not + in it: it sources the root-only `/etc/slackrepo/SBo-danix.local`, see + `SBo-danix.local.example`. Hooks honour `SBB_PUBLISH=n` (no rsync push, set by + sb-builder for unpublished runs), and a `--preview` neither notifies Gotify + nor runs `manage-packages.sh` for real. +- `sb-publish`: pushes the package repository to the mirror. Called by the + config's `rsync_push_hook`; run it by hand to publish builds made with + `SBB_PUBLISH=n`. +- `gpg-agent.conf`: root's gpg-agent config, with `allow-preset-passphrase` so + sb-builder can unlock the package signing key for jobs that have no tty. +- `make-local.py`: one-off migration, builds `SBo-danix.local` from the values + in an older config that still had them inline. - `sbpull.bash`: `sbpull {personal|pentesting} [--yes|--no]` shell function. Pulls one overlay subtree and runs `sbshadow` (passing the flag on), to carry overlay changes without a full rebuild. Source it from root's @@ -33,8 +46,11 @@ upstream tree), so the overlay version wins. ## Install ```bash -install -m 755 slackrepo_setup_SBo-danix.sh sbshadow /usr/local/bin/ +install -m 755 slackrepo_setup_SBo-danix.sh sbshadow sb-publish /usr/local/bin/ echo ". /path/to/sbpull.bash" >> ~/.bashrc +install -m 644 slackrepo_SBo-danix.conf /etc/slackrepo/ +install -m 600 SBo-danix.local.example /etc/slackrepo/SBo-danix.local # then edit +install -m 600 gpg-agent.conf /root/.gnupg/ && gpgconf --reload gpg-agent ``` Optional: `mkhint` (mkhintfile repo) for phantom-dep hintfile diff --git a/SBo-danix.local.example b/SBo-danix.local.example new file mode 100644 index 0000000..fc1664b --- /dev/null +++ b/SBo-danix.local.example @@ -0,0 +1,8 @@ +# /etc/slackrepo/SBo-danix.local: site settings for slackrepo_SBo-danix.conf +# and sb-publish. Not in git: it holds the Gotify key. Keep it root-only: +# install -m 600 SBo-danix.local.example /etc/slackrepo/SBo-danix.local +SB_RSYNC_TARGET='rsync://user@mirror.example.org/packages' +SB_RSYNC_PASSWORD_FILE='/root/.rsync-password' +SB_RSYNC_EXCLUDES='/root/.repo-excludes.txt' +SB_GOTIFY_URL='http://gotify.example.org/message' +SB_GOTIFY_KEY='replace-me' diff --git a/gpg-agent.conf b/gpg-agent.conf new file mode 100644 index 0000000..510efcc --- /dev/null +++ b/gpg-agent.conf @@ -0,0 +1,10 @@ +pinentry-program /usr/bin/pinentry-curses +enable-ssh-support +# cache passwords for both gpg and ssh for 4h +max-cache-ttl-ssh 14400 +default-cache-ttl 14400 +# expire passwords after 6h +max-cache-ttl 21600 +max-cache-ttl-ssh 21600 +# let sb-builder unlock the signing key: jobs have no tty for pinentry +allow-preset-passphrase diff --git a/make-local.py b/make-local.py new file mode 100644 index 0000000..366935c --- /dev/null +++ b/make-local.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. See the LICENSE file for details. + +# Run on the VM, as root, BEFORE installing the new slackrepo_SBo-danix.conf: +# copies the site values out of the old config into SBo-danix.local (mode 600). +# Prints only which keys it found, never their values. +import os, re, sys +old, dest = sys.argv[1], sys.argv[2] +if os.path.exists(dest): + sys.exit(f"{dest} exists, left alone") +s = open(old).read() +found = { + "SB_RSYNC_TARGET": re.search(r"local rsync_target='([^']*)'", s), + "SB_RSYNC_EXCLUDES": re.search(r"local rsync_excludes='([^']*)'", s), + "SB_RSYNC_PASSWORD_FILE": re.search(r"local rsync_password_file='([^']*)'", s), + "SB_GOTIFY_KEY": re.search(r'X-Gotify-Key: ([^"]*)"', s), + "SB_GOTIFY_URL": re.search(r'\n\s*"(https?://[^"]*)" >/dev/null', s), +} +missing = [k for k, m in found.items() if not m] +if missing: + sys.exit(f"not found in {old}: {', '.join(missing)}") +fd = os.open(dest, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) +with os.fdopen(fd, "w") as f: + f.write("# site settings for slackrepo_SBo-danix.conf and sb-publish (root-only)\n") + for k, m in found.items(): + f.write(f"{k}='{m.group(1)}'\n") +print(f"wrote {dest}: {', '.join(found)}") diff --git a/sb-publish b/sb-publish new file mode 100755 index 0000000..9f53c5f --- /dev/null +++ b/sb-publish @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published +# by the Free Software Foundation. See the LICENSE file for details. + +# sb-publish: push the local package repository to the public mirror. +# +# Usage: sb-publish [PKGREPO] (default /repo) +# +# Called by rsync_push_hook in slackrepo_SBo-danix.conf after a build, unless +# the build ran with SBB_PUBLISH=n; run it by hand (or from sb-builder's +# Publish action) to push such builds later. Settings come from the root-only +# SB_LOCAL file (default /etc/slackrepo/SBo-danix.local): +# SB_RSYNC_TARGET rsync destination (required) +# SB_RSYNC_PASSWORD_FILE --password-file, if the file exists +# SB_RSYNC_EXCLUDES --exclude-from, if the file exists + +set -euo pipefail + +SB_LOCAL=${SB_LOCAL:-/etc/slackrepo/SBo-danix.local} +[ -r "$SB_LOCAL" ] && . "$SB_LOCAL" +: "${SB_RSYNC_TARGET:?SB_RSYNC_TARGET is not set in $SB_LOCAL}" + +pkgrepo=${1:-/repo} +args=( -havz --delete-after --stats ) +[ -n "${SB_RSYNC_EXCLUDES:-}" ] && [ -f "$SB_RSYNC_EXCLUDES" ] && \ + args+=( --exclude-from="$SB_RSYNC_EXCLUDES" ) +[ -n "${SB_RSYNC_PASSWORD_FILE:-}" ] && [ -f "$SB_RSYNC_PASSWORD_FILE" ] && \ + args+=( --password-file="$SB_RSYNC_PASSWORD_FILE" ) + +echo "sb-publish: pushing $pkgrepo to $SB_RSYNC_TARGET ..." +rsync "${args[@]}" "$pkgrepo"/ "$SB_RSYNC_TARGET" +echo "sb-publish: rsync push complete." diff --git a/slackrepo_SBo-danix.conf b/slackrepo_SBo-danix.conf new file mode 100644 index 0000000..b789c0b --- /dev/null +++ b/slackrepo_SBo-danix.conf @@ -0,0 +1,310 @@ +# slackrepo configuration file for the SBo-danix personal repository. +# +# Priority order: +# (1) Environment variables +# (2) ~/.slackreporc +# (3) /etc/slackrepo/slackrepo_SBo-danix.conf +# +# %REPO%, %SLACKVER% and %ARCH% are replaced at runtime. + +#------------------------------------------------------------------------------- +# Filestore locations + +# Personal SlackBuilds repo (managed as a git fork of SBo): +# Site settings (rsync target, Gotify URL and key) live outside git in a +# root-only file, see SBo-danix.local.example in slackrepo_setup. +SB_LOCAL='/etc/slackrepo/SBo-danix.local' +[ -r "$SB_LOCAL" ] && . "$SB_LOCAL" + +SBREPO="/var/lib/sbopkg/SBo-danix" + +# Cached sources: +SRCREPO="/var/lib/slackrepo/%REPO%/source" + +# Built packages: +#PKGREPO="/var/lib/slackrepo/%REPO%/packages/%SLACKVER%/%ARCH%" +PKGREPO="/repo" + +# Backups (comment out to disable): +#PKGBACKUP="/var/lib/slackrepo/%REPO%/backups/%SLACKVER%/%ARCH%" + +# Local hintfiles (for overriding SBo packages without touching the SlackBuild): +HINTDIR="/etc/slackrepo/%REPO%/hintfiles" + +# Default hintfiles from slackrepo-hints package (optional): +DEFAULT_HINTDIR="/etc/slackrepo/%REPO%/default_hintfiles/%SLACKVER%" + +# Logs: +LOGDIR="/var/log/slackrepo/%REPO%" + +# Database: +DATABASE="/var/lib/slackrepo/%REPO%/database_%REPO%_%ARCH%.sqlite3" + +# Temporary build space (needs plenty of room): +TMP="/tmp/%REPO%" + +#------------------------------------------------------------------------------- +# Package tag +# Using _danix instead of _SBo to identify your personal builds. +TAG='_danix' + +# Package compression: +PKGTYPE='txz' + +#------------------------------------------------------------------------------- +# Build control + +SUBSTITUTE="" +NUMJOBS='' +ARCH='' + +#------------------------------------------------------------------------------- +# Control argument defaults + +VERBOSE='n' +VERY_VERBOSE='n' +DRY_RUN='n' +INSTALL='n' +CHROOT='/' +LINT='n' +KEEP_TMP='n' +COLOR='auto' +NICE='5' +NOWARNING='' + +#------------------------------------------------------------------------------- +# Git branch setup +# +# INIT_GITCLONE is only used if SBREPO does not exist yet. +# INIT_GITBRANCH must match the branch you have checked out in SBREPO. +# Since we use a personal branch, gitfetch_hook is replaced below with a +# custom hook that fetches upstream and rebases your branch on top of it. + +INIT_GITCLONE='https://github.com/Ponce/slackbuilds.git' +INIT_GITBRANCH='danix-current' + +#------------------------------------------------------------------------------- +# Hooks + +function danix_gitfetch_hook +{ + local upstream_branch='current' + local personal_branch='danix-current' + + cd "$SR_SBREPO" || return 1 + + local currbranch + currbranch=$(git rev-parse --abbrev-ref HEAD) + if [ "$currbranch" != "$personal_branch" ]; then + echo "slackrepo: WARNING: SBREPO is on branch '$currbranch', expected '$personal_branch'. Skipping git update." + return 0 + fi + + local muck + muck=$(git status -s .) + if [ -n "$muck" ]; then + echo "slackrepo: WARNING: SBREPO has uncommitted changes. Skipping git update." + return 0 + fi + + echo "slackrepo: Fetching upstream SBo..." + git fetch origin || { echo "slackrepo: WARNING: git fetch failed."; return 1; } + + echo "slackrepo: Rebasing $personal_branch onto origin/$upstream_branch..." + git rebase --rebase-merges -X theirs origin/"$upstream_branch" || { + echo "slackrepo: ERROR: rebase failed. Resolve conflicts in $SR_SBREPO and run 'git rebase --continue'." + return 1 + } + + return 0 +} + +HOOK_START=( danix_gitfetch_hook ) + +function manage_packages_hook +{ + local args=() + # --preview does not set OPT_DRY_RUN (slackrepo cmdfunctions.sh), check both + if [ "$OPT_DRY_RUN" = 'y' ] || [ "$OPT_PREVIEW" = 'y' ]; then + args+=( --dry-run ) + fi + REPO="$SR_PKGREPO" /usr/local/bin/manage-packages.sh "${args[@]}" +} + +#------------------------------------------------------------------------------- +# generate web files + +function gen_web_hook +{ + if [ "$OPT_DRY_RUN" = 'y' ] || [ ! -s "$CHANGELOG" ]; then + return 0 + fi + SR_PKGREPO="$SR_PKGREPO" /usr/local/bin/gen_web_hook.sh + local stat=$? + [ $stat -ne 0 ] && log_warning "gen_web_hook failed, status $stat" + return $stat +} + +#------------------------------------------------------------------------------- +# dep template generator +# Writes <TEMPLATE_DIR>/<prgnam>.template for each successfully built package +# that has one or more recursive dependencies. + +TEMPLATE_DIR='/repo/templates' + +function dep_template_hook +{ + local template_dir="${TEMPLATE_DIR:-/repo/templates}" + + [ "$OPT_DRY_RUN" = 'y' ] && return 0 + [ ${#OKLIST[@]} -eq 0 ] && return 0 + + mkdir -p "$template_dir" || return 1 + + local itemid prgnam dep depname outfile + for itemid in "${OKLIST[@]}"; do + [ -z "${FULLDEPS[$itemid]}" ] && continue + + prgnam="${ITEMPRGNAM[$itemid]}" + outfile="$template_dir/${prgnam}.template" + + { + for dep in ${FULLDEPS[$itemid]}; do + depname="${ITEMPRGNAM[$dep]}" + printf '%s\n' "${depname:-$dep}" + done + printf '%s\n' "$prgnam" + } > "$outfile" + + log_normal "dep_template_hook: wrote $outfile" + done + + return 0 +} + +#------------------------------------------------------------------------------- +# rsync push config + +function rsync_push_hook +{ + # SBB_PUBLISH=n (set by sb-builder for an unpublished run) holds the push + # back; sb-publish pushes later. Unset means publish, as from a terminal. + if [ "$OPT_DRY_RUN" = 'y' ] || [ "$OPT_PREVIEW" = 'y' ] || [ ! -s "$CHANGELOG" ] \ + || [ "${SBB_PUBLISH:-y}" = 'n' ]; then + return 0 + fi + /usr/local/bin/sb-publish "$SR_PKGREPO" + local rsyncstat=$? + if [ "$rsyncstat" != 0 ]; then + log_warning "rsync_push_hook failed, status $rsyncstat" + return 1 + fi + return 0 +} + +function recap_hook +{ + # TODOLIST is per-item scratch (overwritten on each build_command call), so it + # only ever holds the last item. Build the recap from the cumulative status + # lists that slackrepo dedupes before running the finish hooks. + local recaplist=( + "${OKLIST[@]}" "${FAILEDLIST[@]}" "${ABORTEDLIST[@]}" + "${SKIPPEDLIST[@]}" "${UNSUPPORTEDLIST[@]}" + ) + recaplist=( $(printf '%s\n' "${recaplist[@]}" | sort -u) ) + [ ${#recaplist[@]} -eq 0 ] && return 0 + + local col_name=4 col_ver=7 col_status=6 + local itemid prgnam ver stat + for itemid in "${recaplist[@]}"; do + prgnam="${ITEMPRGNAM[$itemid]}" + ver="${HINT_VERSION[$itemid]:-${INFOVERSION[$itemid]:-?}}" + stat="${STATUS[$itemid]:-?}" + [ ${#prgnam} -gt $col_name ] && col_name=${#prgnam} + [ ${#ver} -gt $col_ver ] && col_ver=${#ver} + [ ${#stat} -gt $col_status ] && col_status=${#stat} + done + + local sep + sep="+-$(printf '%*s' "$col_name" '' | tr ' ' '-')-+-$(printf '%*s' "$col_ver" '' | tr ' ' '-')-+-$(printf '%*s' "$col_status" '' | tr ' ' '-')-+" + + echo "" + echo "Build recap:" + echo "$sep" + printf "| %-*s | %-*s | %-*s |\n" "$col_name" "Name" "$col_ver" "Version" "$col_status" "Status" + echo "$sep" + for itemid in "${recaplist[@]}"; do + prgnam="${ITEMPRGNAM[$itemid]}" + ver="${HINT_VERSION[$itemid]:-${INFOVERSION[$itemid]:-?}}" + stat="${STATUS[$itemid]:-?}" + printf "| %-*s | %-*s | %-*s |\n" "$col_name" "$prgnam" "$col_ver" "$ver" "$col_status" "$stat" + done + echo "$sep" + echo "" + + return 0 +} + +function gotify_hook +{ + # --preview does not set OPT_DRY_RUN (slackrepo cmdfunctions.sh), check both + if [ "$OPT_DRY_RUN" = 'y' ] || [ "$OPT_PREVIEW" = 'y' ]; then + return 0 + fi + if [ -z "${SB_GOTIFY_URL:-}" ] || [ -z "${SB_GOTIFY_KEY:-}" ]; then + log_warning "SB_GOTIFY_URL/SB_GOTIFY_KEY not set in $SB_LOCAL, skipping gotify_hook" + return 0 + fi + + local curl_bin + curl_bin=$(which curl 2>/dev/null) + if [ ! -x "$curl_bin" ]; then + log_warning "curl not found, skipping gotify_hook" + return 1 + fi + + local ok="${#OKLIST[@]}" + local failed="${#FAILEDLIST[@]}" + local aborted="${#ABORTEDLIST[@]}" + local skipped="${#SKIPPEDLIST[@]}" + + local title msg priority + if [ $(( failed + aborted )) -gt 0 ]; then + title="slackrepo [$CMD]: FAILED" + priority=8 + else + title="slackrepo [$CMD]: OK" + priority=5 + fi + + msg="OK: $ok Failed: $failed Aborted: $aborted Skipped: $skipped" + [ ${#OKLIST[@]} -gt 0 ] && msg+=$'\nOK: '"$(printf '%s ' "${OKLIST[@]}")" + [ ${#FAILEDLIST[@]} -gt 0 ] && msg+=$'\nFailed: '"$(printf '%s ' "${FAILEDLIST[@]}")" + [ ${#ABORTEDLIST[@]} -gt 0 ] && msg+=$'\nAborted: '"$(printf '%s ' "${ABORTEDLIST[@]}")" + + "$curl_bin" -s \ + -H "X-Gotify-Key: $SB_GOTIFY_KEY" \ + -F "title=$title" \ + -F "message=$msg" \ + -F "priority=$priority" \ + "$SB_GOTIFY_URL" >/dev/null + local stat=$? + [ $stat -ne 0 ] && log_warning "gotify_hook failed, status $stat" + return $stat +} + +function bell_hook +{ + printf '\a' + return 0 +} + +HOOK_FINISH=( manage_packages_hook bell_hook genrepos_hook gen_web_hook dep_template_hook rsync_push_hook recap_hook gotify_hook ) + +#------------------------------------------------------------------------------- +# gen_repos_files.sh (disabled by default) + +USE_GENREPOS='1' + +#------------------------------------------------------------------------------- +export XZ_OPT="--threads=0" |
