#!/usr/bin/env bash # Shared helpers for the DealSage custom-app pipeline scripts. Sourced, not run. # # Conventions: # * Every script is `set -euo pipefail` and sources this file first. # * DealSage is reached only through DEALSAGE_FUNCTIONS_URL with the app's # PUBLISH_TOKEN (appId-scoped, minted by DealSage). Nothing here holds a # Supabase service key or an R2 account key. # * Output meant for the job log goes through log(); machine-readable state # is exchanged through $GITHUB_ENV / $GITHUB_OUTPUT like any other action. log() { printf '::group::%s\n' "$*" >/dev/null; printf '\n== %s\n' "$*"; } warn() { printf '::warning::%s\n' "$*"; } die() { printf '::error::%s\n' "$*"; exit 1; } # now_ms — wall clock in milliseconds (GNU date; falls back to seconds*1000). now_ms() { date +%s%3N 2>/dev/null || echo $(( $(date +%s) * 1000 )); } # iso_now — UTC timestamp for API payloads. iso_now() { date -u +%Y-%m-%dT%H:%M:%SZ; } # require_env NAME... — fail loudly when a required variable is missing. require_env() { local missing=0 v for v in "$@"; do if [ -z "${!v:-}" ]; then printf '::error::%s is not set\n' "$v"; missing=1; fi done [ "$missing" = 0 ] || exit 1 } # json_escape STRING — JSON string literal (with quotes) via jq. json_escape() { jq -Rn --arg s "$1" '$s'; } # tail_file FILE [LINES] — last LINES lines of FILE (default 80), for log tails. tail_file() { tail -n "${2:-80}" "$1" 2>/dev/null || true; } # ds_post PATH JSON — POST JSON to a DealSage function with the publish token. # Prints the response body; returns non-zero on HTTP failure (body still printed # to stderr so the job log shows why). ds_post() { local path="$1" body="$2" out code out="$(mktemp)" code="$(curl -sS -o "$out" -w '%{http_code}' -X POST \ "${DEALSAGE_FUNCTIONS_URL%/}/${path}" \ -H "Authorization: Bearer ${PUBLISH_TOKEN}" \ -H "apikey: ${SUPABASE_ANON_KEY}" \ -H "content-type: application/json" \ --data-binary "$body")" || { cat "$out" >&2; rm -f "$out"; return 1; } if [ "${code:0:1}" != "2" ]; then printf 'DealSage %s returned HTTP %s: ' "$path" "$code" >&2; cat "$out" >&2; echo >&2 rm -f "$out"; return 1 fi cat "$out"; rm -f "$out" } # git_context — export the commit/branch/author the pipeline is publishing. # Gitea Actions provides the GITHUB_* variables; the event payload carries the # head commit. Falls back to git itself (the checkout has the commit). git_context() { local ev="${GITHUB_EVENT_PATH:-}" COMMIT_SHA="${GITHUB_SHA:-$(git rev-parse HEAD 2>/dev/null || echo unknown)}" BRANCH="${GITHUB_REF_NAME:-}" if [ -z "$BRANCH" ]; then BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)" fi COMMIT_MESSAGE=""; COMMIT_AUTHOR="" if [ -n "$ev" ] && [ -f "$ev" ]; then COMMIT_MESSAGE="$(jq -r '.head_commit.message // .commits[-1].message // empty' "$ev" 2>/dev/null || true)" COMMIT_AUTHOR="$(jq -r '.head_commit.author.name // .pusher.login // .pusher.username // empty' "$ev" 2>/dev/null || true)" fi [ -n "$COMMIT_MESSAGE" ] || COMMIT_MESSAGE="$(git log -1 --pretty=%B 2>/dev/null | head -c 2000 || true)" [ -n "$COMMIT_AUTHOR" ] || COMMIT_AUTHOR="$(git log -1 --pretty=%an 2>/dev/null || echo "${GITHUB_ACTOR:-}")" # First line only for the summary field; the full message travels separately. COMMIT_SUBJECT="$(printf '%s' "$COMMIT_MESSAGE" | head -n 1 | head -c 200)" export COMMIT_SHA BRANCH COMMIT_MESSAGE COMMIT_AUTHOR COMMIT_SUBJECT } # persist_env NAME VALUE — make a value visible to later composite steps. persist_env() { if [ -n "${GITHUB_ENV:-}" ]; then # Heredoc form handles multi-line values safely. { printf '%s<<__DS_EOF__\n' "$1"; printf '%s\n' "$2"; printf '__DS_EOF__\n'; } >> "$GITHUB_ENV" fi export "$1=$2" } # summary MARKDOWN — append to the run's step summary when the runner supports it. summary() { [ -n "${GITHUB_STEP_SUMMARY:-}" ] && printf '%s\n' "$*" >> "$GITHUB_STEP_SUMMARY" || true; }