Files
omarchy-pkgs/helpers/basecamp-notifier.sh
T
Ryan Hughes 7c3cfc50e9 Fix silent notifications and stop stale checkouts queueing rebuild loops
Two failures from the first live run:

notify_basecamp declared 'local BASECAMP_CHATBOT_URL' and then called
release_chatbot_url, whose fallback reads that same global — bash locals are
visible to called functions, so the fallback saw the empty local and every
notification silently went nowhere for anyone with only the legacy variable
set. The local is now named 'url'.

check-versions compared PKGBUILD and published versions with !=, so a checkout
BEHIND the channel queued a rebuild of an older version every cycle: the
builder produced it and promotion refused it, because that exact filename is
already published with different bytes. It now skips (with a warning naming
the package) when an artifact for the PKGBUILD's version already exists in the
channel, whichever direction the versions differ.
2026-08-27 01:10:33 -04:00

164 lines
4.5 KiB
Bash

# Send notifications to Basecamp Campfire via chatbot
#
# Two destinations, so release traffic does not drown the repository chat:
#
# OMARCHY_RELEASE_CHATBOT_URL release activity (starts, publishes, failures)
# BASECAMP_CHATBOT_URL everything else, and the fallback when the
# release URL is unset
#
# Each is the full chatbot lines URL from Basecamp, e.g.
# https://3.basecamp.com/<account>/integrations/<key>/buckets/<project>/chats/<chat>/lines
#
# With neither set, notifications are silently skipped.
release_chatbot_url() {
echo "${OMARCHY_RELEASE_CHATBOT_URL:-${BASECAMP_CHATBOT_URL:-}}"
}
notify_basecamp() {
local content="$1"
# NOT named BASECAMP_CHATBOT_URL: bash locals are visible to called
# functions, so declaring that name here would shadow the global that
# release_chatbot_url falls back to — and every notification would silently
# go nowhere for anyone who has only the legacy variable set.
local url
url=$(release_chatbot_url)
if [[ -z "$url" ]]; then
return 0
fi
curl -s -o /dev/null \
-H "Content-Type: application/json" \
-d "$(jq -n --arg content "$content" '{content: $content}')" \
"$url" 2>/dev/null || true
}
basecamp_html_escape() {
sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g'
}
basecamp_strip_ansi() {
sed -E $'s/\x1B\\[[0-9;?]*[ -/]*[@-~]//g'
}
basecamp_format_log_tail_html() {
local log_file="$1"
local lines="${2:-25}"
[[ -n "$log_file" && -f "$log_file" ]] || return 0
tail -n "$lines" "$log_file" 2>/dev/null |
tr -d '\r' |
basecamp_strip_ansi |
basecamp_html_escape |
sed ':a;N;$!ba;s/\n/<br>/g'
}
notify_error() {
local title="$1"
local details="${2:-}"
local log_file="${OMARCHY_LOG_FILE:-${LOG_FILE:-}}"
local log_tail_lines="${BASECAMP_LOG_TAIL_LINES:-25}"
local log_tail=""
local content="🔴 <strong>${title}</strong>"
if [[ -n "$details" ]]; then
content+="<br>${details}"
fi
log_tail=$(basecamp_format_log_tail_html "$log_file" "$log_tail_lines")
if [[ -n "$log_tail" ]]; then
content+="<br><br><strong>Last ${log_tail_lines} log lines:</strong><br><code>${log_tail}</code>"
fi
notify_basecamp "$content"
}
# Human-friendly elapsed time: "8m 12s", "45s", "1h 4m".
format_duration() {
local seconds="${1:-0}"
if ((seconds >= 3600)); then
printf '%dh %dm' $((seconds / 3600)) $(((seconds % 3600) / 60))
elif ((seconds >= 60)); then
printf '%dm %ds' $((seconds / 60)) $((seconds % 60))
else
printf '%ds' "$seconds"
fi
}
notify_start() {
local title="$1"
local details="${2:-}"
local content="🔨 <strong>${title}</strong>"
[[ -n "$details" ]] && content+="<br>${details}"
notify_basecamp "$content"
}
notify_success() {
local title="$1"
local details="${2:-}"
local content="✅ <strong>${title}</strong>"
[[ -n "$details" ]] && content+="<br>${details}"
notify_basecamp "$content"
}
notify_info() {
local title="$1"
local details="${2:-}"
local content="📦 <strong>${title}</strong>"
[[ -n "$details" ]] && content+="<br>${details}"
notify_basecamp "$content"
}
# "name-1.2.3-1-x86_64.pkg.tar.zst" -> "name 1.2.3-1". Package file names put
# the architecture last and the pkgrel before it, so peel from the right.
package_file_label() {
local file="$1" stem arch rest rel name_version version name
stem="${file%.pkg.tar.*}"
arch="${stem##*-}"
rest="${stem%-*}"
rel="${rest##*-}"
name_version="${rest%-*}"
version="${name_version##*-}"
name="${name_version%-*}"
printf '%s %s-%s' "$name" "$version" "$rel"
}
# Packages sitting in a build-output directory, newest build first. Excludes
# signatures and the scratch database the builder keeps alongside them.
built_package_files() {
local dir="$1"
[[ -d "$dir" ]] || return 0
find "$dir" -maxdepth 1 -name '*.pkg.tar.*' ! -name '*.sig' -printf '%f\n' 2>/dev/null |
grep -v '^omarchy-build\.' | sort
}
# An HTML list of built packages, capped so a 60-package rebuild does not
# produce an unreadable wall of text in chat.
format_package_list_html() {
local files="$1"
local limit="${2:-25}"
local count shown=0 line out=""
count=$(grep -c '' <<<"$files")
[[ -z "$files" ]] && count=0
((count == 0)) && return 0
while IFS= read -r line; do
[[ -z "$line" ]] && continue
((shown >= limit)) && break
out+="<br>• $(package_file_label "$line" | basecamp_html_escape)"
shown=$((shown + 1))
done <<<"$files"
if ((count > shown)); then
out+="<br>• …and $((count - shown)) more"
fi
echo "$out"
}