aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-19 20:53:05 +0200
committerDanilo M. <danix@danix.xyz>2026-10-19 20:53:05 +0200
commite6ac42e158ed5ceee2c58eaf2f23a0ad3bb82f41 (patch)
treeaf12ae932e9cbbf774f5a03cceeba0dc20c4b1c0
parent30c6bc22a445105e7e7f37fb00ca054de1ecf3ad (diff)
downloadslackrepo_setup-e6ac42e158ed5ceee2c58eaf2f23a0ad3bb82f41.tar.gz
slackrepo_setup-e6ac42e158ed5ceee2c58eaf2f23a0ad3bb82f41.zip
Track the slackrepo config; add sb-publish and publish gates
slackrepo_SBo-danix.conf is now kept here. Site values (rsync target, Gotify URL and key) moved to the root-only SBo-danix.local it sources; make-local.py migrates them out of an old inline config. The rsync push moved into sb-publish; rsync_push_hook calls it and holds back when SBB_PUBLISH=n, so sb-builder can build without publishing and publish later. --preview does not set OPT_DRY_RUN, so the hooks now also check OPT_PREVIEW: previews no longer notify Gotify or run manage-packages.sh for real. gpg-agent.conf adds allow-preset-passphrase so sb-builder can unlock the signing key for jobs without a tty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--AGENTS.md16
-rw-r--r--README.md18
-rw-r--r--SBo-danix.local.example8
-rw-r--r--gpg-agent.conf10
-rw-r--r--make-local.py31
-rwxr-xr-xsb-publish35
-rw-r--r--slackrepo_SBo-danix.conf310
7 files changed, 427 insertions, 1 deletions
diff --git a/AGENTS.md b/AGENTS.md
index dcc195e..874558d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -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
diff --git a/README.md b/README.md
index 075c5b6..e462c7e 100644
--- a/README.md
+++ b/README.md
@@ -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"