#!/usr/bin/env bash # Talk to a running Silo deployment — local or over SSH — using the settings in # `.silo-dev.env`. Run `scripts/silo-dev help` for the subcommands. set -euo pipefail repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) env_file=${SILO_DEV_ENV_FILE:-"$repo_root/.silo-dev.env"} if [[ -f "$env_file" ]]; then set -a # shellcheck disable=SC1090 . "$env_file" set +a fi SILO_URL=${SILO_URL:-http://localhost:8090} SILO_SSH=${SILO_SSH:-} SILO_DIR=${SILO_DIR:-/opt/silo} SILO_COMPOSE_FILE=${SILO_COMPOSE_FILE:-docker-compose.yml} SILO_SERVICE=${SILO_SERVICE:-silo} SILO_POSTGRES_SERVICE=${SILO_POSTGRES_SERVICE:-postgres} SILO_DB_USER=${SILO_DB_USER:-silo} SILO_DB_NAME=${SILO_DB_NAME:-silo} die() { printf 'silo-dev: %s\n' "$1" >&2 exit 1 } need_env_file() { [[ -f "$env_file" ]] || die "no ${env_file##*/} — copy .silo-dev.env.example to .silo-dev.env and fill it in" } # Run a shell command on whichever host Silo runs on. on_host() { if [[ -n "$SILO_SSH" ]]; then ssh "$SILO_SSH" "$@" else bash -lc "$@" fi } compose() { on_host "cd $(printf '%q' "$SILO_DIR") && COMPOSE_FILE=$(printf '%q' "$SILO_COMPOSE_FILE") docker compose $*" } # POST /auth/login and print the access token. token() { need_env_file [[ -n "${SILO_ADMIN_USER:-}" && -n "${SILO_ADMIN_PASSWORD:-}" ]] || die "set SILO_ADMIN_USER and SILO_ADMIN_PASSWORD in ${env_file##*/}" local body body=$(SILO_ADMIN_USER="$SILO_ADMIN_USER" SILO_ADMIN_PASSWORD="$SILO_ADMIN_PASSWORD" \ python3 -c 'import json,os;print(json.dumps({"username":os.environ["SILO_ADMIN_USER"],"password":os.environ["SILO_ADMIN_PASSWORD"]}))') curl -fsS -X POST "$SILO_URL/api/v1/auth/login" \ -H 'Content-Type: application/json' \ --data-binary "$body" | python3 -c 'import json,sys;print(json.load(sys.stdin)["access_token"])' } usage() { cat <<'EOF' usage: scripts/silo-dev [args] doctor check config and reachability, then report what works env print the resolved settings (secrets masked) sh run a shell command on the host running Silo compose docker compose against the deployment's stack logs [args...] compose logs for the Silo service (default: --tail 200) psql run one SQL statement against the Silo database token print an access token for SILO_ADMIN_USER api [curl...] authenticated request against the API, e.g. api /api/v1/health Settings come from .silo-dev.env (see .silo-dev.env.example). With SILO_SSH empty, host commands run locally. EOF } cmd=${1:-help} if [[ $# -gt 0 ]]; then shift; fi case "$cmd" in help | -h | --help) usage ;; env) mask() { [[ -n "${1:-}" ]] && printf 'set' || printf 'empty'; } cat <} SILO_DIR $SILO_DIR SILO_COMPOSE_FILE $SILO_COMPOSE_FILE SILO_SERVICE $SILO_SERVICE database $SILO_DB_USER@$SILO_POSTGRES_SERVICE/$SILO_DB_NAME SILO_ADMIN_USER ${SILO_ADMIN_USER:-} SILO_ADMIN_PASSWORD $(mask "${SILO_ADMIN_PASSWORD:-}") SILO_MEDIA_ROOT ${SILO_MEDIA_ROOT:-} EOF ;; doctor) need_env_file status=0 printf 'api ' if curl -fsS --max-time 10 "$SILO_URL/api/v1/health" >/dev/null 2>&1; then printf 'ok (%s)\n' "$SILO_URL" else printf 'FAIL — %s/api/v1/health did not respond\n' "$SILO_URL" status=1 fi printf 'host ' if on_host 'true' >/dev/null 2>&1; then printf 'ok (%s)\n' "${SILO_SSH:-local}" else printf 'FAIL — cannot run commands on %s\n' "${SILO_SSH:-local shell}" status=1 fi printf 'compose ' if compose ps --services >/dev/null 2>&1; then printf 'ok (%s in %s)\n' "$SILO_COMPOSE_FILE" "$SILO_DIR" else printf 'FAIL — no compose stack at %s\n' "$SILO_DIR" status=1 fi printf 'database ' if compose "exec -T $SILO_POSTGRES_SERVICE psql -U $SILO_DB_USER -d $SILO_DB_NAME -tAc 'select 1'" >/dev/null 2>&1; then printf 'ok (%s/%s)\n' "$SILO_DB_USER" "$SILO_DB_NAME" else printf 'FAIL — psql as %s on %s failed; check POSTGRES_USER/POSTGRES_DB in the deployment .env\n' \ "$SILO_DB_USER" "$SILO_DB_NAME" status=1 fi printf 'auth ' if [[ -z "${SILO_ADMIN_USER:-}" ]]; then printf 'skipped — SILO_ADMIN_USER not set\n' elif token >/dev/null 2>&1; then printf 'ok (%s)\n' "$SILO_ADMIN_USER" else printf 'FAIL — login rejected for %s\n' "$SILO_ADMIN_USER" status=1 fi exit "$status" ;; sh) [[ $# -gt 0 ]] || die 'sh needs a command' on_host "$*" ;; compose) compose "$*" ;; logs) compose "logs $SILO_SERVICE ${*:---tail 200}" ;; psql) [[ $# -gt 0 ]] || die 'psql needs a SQL statement' sql=$* compose "exec -T $SILO_POSTGRES_SERVICE psql -U $SILO_DB_USER -d $SILO_DB_NAME -P pager=off -c $(printf '%q' "$sql")" ;; token) token ;; api) [[ $# -gt 0 ]] || die 'api needs a path, e.g. /api/v1/health' path=$1 shift curl -fsS -H "Authorization: Bearer $(token)" "$SILO_URL$path" "$@" ;; *) usage >&2 die "unknown command: $cmd" ;; esac