# Shared publisher-side helpers for the artifact index (sourced by # make_hotfix.sh / make_app.sh — release tooling only, never the CLI tree). # # The index is the team-signed catalog every install fetches + verifies # (scripts/source/artifacts.sh, docs/roadmap/updates-and-distribution.md §8). # These helpers own the parts every publisher tool shares: signing when a key # is configured, reading the publisher pubkey for the publishers map, and the # upsert-entry + bump-serial + refresh-freshness index rewrite. # # Env (shared by all publisher tools): # LP_MINISIGN_SECKEY offline secret key — set to SIGN (required for a real # publish; unset = unsigned, local-testing only). # LP_MINISIGN_PUBKEY public key file for the publishers map (default: repo # libreportal.pub). Installs verify against the # ROOT-OWNED footprint key, so this must be that key. # LP_INDEX_VALID_DAYS freshness window written to valid_until (default 30; # LP_HOTFIX_VALID_DAYS honoured as a legacy alias). # releaseSignIfKeyed — minisign-sign in place when a # key is configured; a no-op otherwise (unsigned local-testing mode). releaseSignIfKeyed() { [[ -n "${LP_MINISIGN_SECKEY:-}" ]] || return 0 command -v minisign >/dev/null 2>&1 || { echo "release: LP_MINISIGN_SECKEY set but 'minisign' isn't installed" >&2; exit 1; } minisign -Sm "$1" -s "$LP_MINISIGN_SECKEY" -t "$2" >/dev/null } # releaseSignedNote — the human-readable signing status for the summary line. releaseSignedNote() { if [[ -n "${LP_MINISIGN_SECKEY:-}" ]]; then echo "signed"; else echo "unsigned (set LP_MINISIGN_SECKEY to sign — required for a real publish)" fi } # releaseReadPubkey — echo the bare key line for the publishers # map ("" when the file is missing; callers warn, signing still proceeds). releaseReadPubkey() { [[ -f "${1:-}" ]] || { echo ""; return 0; } grep -v -i '^untrusted comment' "$1" | head -1 | tr -d ' \t\r\n' } # releaseIndexUpsert # Load-or-init the index, replace any same-id entry with the envelope, bump # index_serial, refresh valid_until/generated_at, and upsert the publisher # into the publishers map (role: official for "libreportal", else community). # Echoes the new serial (callers sign the index with it in the comment). releaseIndexUpsert() { local envelope="$1" index="$2" publisher="$3" pubkey="$4" local cur serial days valid_until now default_role cur='{"schema":1,"index_serial":0,"publishers":{},"artifacts":[]}' [[ -f "$index" ]] && cur="$(cat "$index")" serial=$(( $(jq -r '.index_serial // 0' <<<"$cur") + 1 )) days="${LP_INDEX_VALID_DAYS:-${LP_HOTFIX_VALID_DAYS:-30}}" valid_until=$(( $(date +%s) + days * 86400 )) now="$(date -Iseconds 2>/dev/null || date)" default_role="community"; [[ "$publisher" == "libreportal" ]] && default_role="official" jq \ --argjson env "$envelope" \ --arg pub "$publisher" --arg pubkey "$pubkey" --arg role "$default_role" \ --argjson serial "$serial" --argjson vu "$valid_until" --arg now "$now" ' .schema = 1 | .index_serial = $serial | .valid_until = $vu | .generated_at = $now | .publishers = (.publishers // {}) | .publishers[$pub] = ((.publishers[$pub] // {display: $pub, role: $role}) + (if $pubkey != "" then {key: $pubkey} else {} end)) | .artifacts = (((.artifacts // []) | map(select(.id != $env.id))) + [$env]) ' <<<"$cur" > "$index" echo "$serial" }