aven 0.1.13

Local-first task manager CLI and sync server
Documentation
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
default_session="${CUA_SANDBOX_SESSION:-}"
default_db="${AVEN_SANDBOX_DB:-$HOME/.local/state/aven/marketing-screenshot.sqlite}"
global_sandbox="${CUA_SANDBOX_COMMAND:-$(command -v cua-sandbox || true)}"

usage() {
  cat <<'USAGE'
Usage: scripts/cua-sandbox <command> [arguments]

Aven command:
  db-bootstrap <session> [source] [--force]
                           Snapshot a host aven database into a session

Global commands:
  setup                    Install the CLI and build the persistent image
  start <session>          Start an isolated CuaBot server in the foreground
  stop <session>           Stop a named CuaBot server
  status <session>         Show a named CuaBot server's status
  list                     List discoverable CuaBot sessions
  cua <session> [args...]  Pass arguments to a named CuaBot server
  container <session>      Print the Docker container name

Environment:
  CUA_SANDBOX_COMMAND    Global command override
  CUA_SANDBOX_SESSION    Session fallback when an argument is omitted
  AVEN_SANDBOX_DB        Database source for db-bootstrap
USAGE
}

require_command() {
  if ! command -v "$1" >/dev/null 2>&1; then
    printf 'missing required command: %s\n' "$1" >&2
    exit 1
  fi
}

require_global_sandbox() {
  if [[ -z "$global_sandbox" || ! -x "$global_sandbox" ]]; then
    echo "Global cua-sandbox command is missing from PATH." >&2
    exit 1
  fi
}

resolve_session() {
  local requested="${1:-$default_session}"
  if [[ -z "$requested" ]]; then
    echo "a session name is required" >&2
    echo "Pass one explicitly or set CUA_SANDBOX_SESSION." >&2
    exit 1
  fi
  if [[ ! "$requested" =~ ^[a-zA-Z0-9][a-zA-Z0-9._-]*$ ]]; then
    echo "invalid session name: $requested" >&2
    exit 1
  fi
  printf '%s\n' "$requested"
}

bootstrap_database() (
  local session="$1"
  local source="$2"
  local force="$3"
  local container="cuabot-xpra-$session"
  local destination="/home/user/aven-run/aven.db"
  local binary="/home/user/aven-run/aven"

  require_command docker
  require_command sqlite3
  if [[ ! -f "$source" ]]; then
    echo "database source does not exist: $source" >&2
    exit 1
  fi
  if [[ "$(docker inspect --format '{{.State.Status}}' "$container" 2>/dev/null || true)" != "running" ]]; then
    echo "sandbox container is not running: $container" >&2
    exit 1
  fi
  if docker exec "$container" pgrep -f 'aven tui' >/dev/null 2>&1; then
    echo "aven is running in session '$session'; stop it before replacing its database" >&2
    exit 1
  fi
  if ! docker exec -u user "$container" test -x "$binary"; then
    echo "sandbox aven binary is missing: $binary" >&2
    echo "Deploy the branch build before bootstrapping its database." >&2
    exit 1
  fi
  if [[ "$force" != "true" ]] \
    && docker exec -u user "$container" test -e "$destination"
  then
    echo "sandbox database already exists: $destination"
    echo "Pass --force to replace it."
    exit 0
  fi

  local temp_dir snapshot remote
  temp_dir="$(mktemp -d)"
  trap 'rm -rf "$temp_dir"' EXIT
  snapshot="$temp_dir/aven.sqlite"
  remote="/home/user/aven-run/.aven-bootstrap-$session-$$.sqlite"

  sqlite3 "$source" ".backup '$snapshot'"

  local version migration
  local -a normalized=()
  while IFS= read -r version; do
    if [[ ! "$version" =~ ^[0-9]+$ ]]; then
      echo "database contains an invalid migration version: $version" >&2
      exit 1
    fi
    migration="$(compgen -G "$repo_root/crates/aven-core/migrations/${version}_*.sql" || true)"
    if [[ -z "$migration" ]]; then
      sqlite3 "$snapshot" "DELETE FROM _sqlx_migrations WHERE version = $version;"
      normalized+=("$version")
    fi
  done < <(sqlite3 "$snapshot" 'SELECT version FROM _sqlx_migrations ORDER BY version;')

  if [[ "$(sqlite3 "$snapshot" 'PRAGMA quick_check;')" != "ok" ]]; then
    echo "database snapshot failed its integrity check" >&2
    exit 1
  fi
  chmod 0644 "$snapshot"
  docker exec -u user "$container" mkdir -p /home/user/aven-run
  docker cp "$snapshot" "$container:$remote"
  docker exec -u user "$container" sh -lc "
    set -eu
    cp '$remote' '$destination.bootstrap'
  "
  docker exec "$container" rm -f "$remote"

  if ! docker exec -u user -e AVEN_DB="$destination.bootstrap" \
    "$container" "$binary" list --all --limit 1 >/dev/null
  then
    docker exec -u user "$container" rm -f "$destination.bootstrap"
    echo "sandbox aven could not open the database snapshot" >&2
    exit 1
  fi
  docker exec -u user "$container" \
    rm -f "$destination-wal" "$destination-shm"
  docker exec -u user "$container" mv "$destination.bootstrap" "$destination"

  echo "Bootstrapped $destination"
  echo "Source: $source"
  echo "Session: $session"
  if [[ "${#normalized[@]}" -gt 0 ]]; then
    printf 'Ignored source-only migrations:'
    printf ' %s' "${normalized[@]}"
    printf '\n'
  fi
)

command="${1:-}"
case "$command" in
  db-bootstrap)
    session="$(resolve_session "${2:-}")"
    source="${3:-$default_db}"
    force="false"
    if [[ "$source" == "--force" ]]; then
      source="$default_db"
      force="true"
    elif [[ "${4:-}" == "--force" ]]; then
      force="true"
    elif [[ -n "${4:-}" ]]; then
      echo "unexpected argument: ${4}" >&2
      exit 1
    fi
    bootstrap_database "$session" "$source" "$force"
    ;;
  help|-h|--help|'')
    usage
    ;;
  *)
    require_global_sandbox
    exec "$global_sandbox" "$@"
    ;;
esac