Files
kandev/scripts/kandev-logs
T
2026-08-02 20:14:53 +01:00

99 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# kandev-logs — download a bounded diagnostic bundle from a running instance.
#
# Usage:
# scripts/kandev-logs <backend_port> [--source backend|frontend|all] [--output FILE]
#
# Options:
# --source SOURCE Evidence to request (default: backend).
# --output FILE Destination ZIP (default: kandev-diagnostic-<id>.zip).
# -h, --help Show this help.
#
# Authentication:
# Auth-disabled instances need only a port. For authenticated instances set
# KANDEV_API_TOKEN in the environment. Tokens are never accepted on the
# command line or printed.
#
# Examples:
# scripts/kandev-logs 48429
# scripts/kandev-logs 48429 --source frontend
# KANDEV_API_TOKEN=... scripts/kandev-logs 48429 --source all --output /tmp/logs.zip
set -euo pipefail
PORT=""
SOURCE="backend"
OUTPUT=""
usage() { sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-2}"; }
while [[ $# -gt 0 ]]; do
case "$1" in
--source) SOURCE="${2:?--source requires a value}"; shift 2 ;;
--output) OUTPUT="${2:?--output requires a path}"; shift 2 ;;
-h|--help) usage 0 ;;
--*) echo "kandev-logs: unknown flag '$1'" >&2; usage 2 ;;
*)
if [[ -z "$PORT" ]]; then PORT="$1"; else
echo "kandev-logs: unexpected argument '$1'" >&2; usage 2
fi
shift
;;
esac
done
[[ -n "$PORT" ]] || { echo "kandev-logs: a backend port is required." >&2; usage 2; }
[[ "$PORT" =~ ^[0-9]+$ ]] || { echo "kandev-logs: port must be numeric." >&2; exit 2; }
[[ "$SOURCE" == "backend" || "$SOURCE" == "frontend" || "$SOURCE" == "all" ]] || {
echo "kandev-logs: --source must be backend, frontend, or all." >&2
exit 2
}
command -v jq >/dev/null 2>&1 || {
echo "kandev-logs: jq is required to inspect bundle job responses." >&2
exit 1
}
case "$SOURCE" in
backend) sources='["backend"]' ;;
frontend) sources='["frontend"]' ;;
all) sources='["backend","frontend"]' ;;
esac
base_url="http://localhost:$PORT/api/v1/system/logs/bundles"
curl_args=(--silent --show-error --fail-with-body)
if [[ -n "${KANDEV_API_TOKEN:-}" ]]; then
curl_args+=(--header "Authorization: Bearer ${KANDEV_API_TOKEN}")
fi
job="$(curl "${curl_args[@]}" --header "Content-Type: application/json" \
--request POST --data "{\"sources\":$sources}" "$base_url")"
bundle_id="$(printf '%s' "$job" | jq -er '.id')"
status="$(printf '%s' "$job" | jq -er '.status')"
deadline=$((SECONDS + 300))
while [[ "$status" == "collecting" || "$status" == "building" ]]; do
if (( SECONDS >= deadline )); then
echo "kandev-logs: bundle $bundle_id did not finish within five minutes." >&2
exit 1
fi
sleep 1
job="$(curl "${curl_args[@]}" "$base_url/$bundle_id")"
status="$(printf '%s' "$job" | jq -er '.status')"
done
if [[ "$status" != "ready" && "$status" != "partial" ]]; then
echo "kandev-logs: bundle ended in state '$status'." >&2
printf '%s\n' "$job" | jq . >&2
exit 1
fi
[[ -n "$OUTPUT" ]] || OUTPUT="kandev-diagnostic-$bundle_id.zip"
output_dir="$(dirname "$OUTPUT")"
mkdir -p "$output_dir"
temp_file="$(mktemp "$output_dir/.kandev-logs.XXXXXX")"
trap 'rm -f "$temp_file"' EXIT
curl "${curl_args[@]}" --output "$temp_file" "$base_url/$bundle_id/download"
chmod 600 "$temp_file"
mv "$temp_file" "$OUTPUT"
trap - EXIT
echo "$OUTPUT"