Initial import from garrytan/gstack@026751e (main snapshot via local relay)
Some checks failed
Workflow Lint / actionlint (push) Has been cancelled
Build CI Image / build (push) Has been cancelled
Skill Docs Freshness / check-freshness (push) Has been cancelled
Periodic Evals / build-image (push) Has been cancelled
Periodic Evals / evals (map[file:test/codex-e2e.test.ts name:e2e-codex]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/gemini-e2e.test.ts name:e2e-gemini]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-design.test.ts name:e2e-design]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-plan.test.ts name:e2e-plan]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-bugs.test.ts name:e2e-qa-bugs]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-workflow.test.ts name:e2e-qa-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-review.test.ts name:e2e-review]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-workflow.test.ts name:e2e-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-routing-e2e.test.ts name:e2e-routing]) (push) Has been cancelled
Some checks failed
Workflow Lint / actionlint (push) Has been cancelled
Build CI Image / build (push) Has been cancelled
Skill Docs Freshness / check-freshness (push) Has been cancelled
Periodic Evals / build-image (push) Has been cancelled
Periodic Evals / evals (map[file:test/codex-e2e.test.ts name:e2e-codex]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/gemini-e2e.test.ts name:e2e-gemini]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-design.test.ts name:e2e-design]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-plan.test.ts name:e2e-plan]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-bugs.test.ts name:e2e-qa-bugs]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-workflow.test.ts name:e2e-qa-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-review.test.ts name:e2e-review]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-workflow.test.ts name:e2e-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-routing-e2e.test.ts name:e2e-routing]) (push) Has been cancelled
Source: https://github.com/garrytan/gstack/commit/026751e
This commit is contained in:
126
bin/gstack-gbrain-supabase-verify
Executable file
126
bin/gstack-gbrain-supabase-verify
Executable file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
# gstack-gbrain-supabase-verify — structural check on a Supabase Session
|
||||
# Pooler URL before handing it to `gbrain init`.
|
||||
#
|
||||
# Usage:
|
||||
# gstack-gbrain-supabase-verify <url>
|
||||
# echo "<url>" | gstack-gbrain-supabase-verify -
|
||||
#
|
||||
# Accepts ONLY Session Pooler URLs (port 6543, host *.pooler.supabase.com).
|
||||
# Rejects direct-connection URLs (db.*.supabase.co:5432) since those are
|
||||
# IPv6-only and fail in many environments — gbrain's init wizard warns
|
||||
# about this at init.ts:150-158.
|
||||
#
|
||||
# Canonical shape (per gbrain init.ts:266):
|
||||
# postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 — URL passes structural check
|
||||
# 2 — invalid format (bad scheme, port, host, userinfo, or empty password)
|
||||
# 3 — direct-connection URL rejected (common mistake, special-cased for UX)
|
||||
#
|
||||
# The verifier never makes a network call; purely a regex match. Whether
|
||||
# the URL actually works (database up, password correct, host reachable)
|
||||
# is gbrain's problem at init time.
|
||||
#
|
||||
# Reads URL from:
|
||||
# 1. argv[1] if provided and not "-"
|
||||
# 2. stdin if argv[1] is "-" or missing
|
||||
#
|
||||
# Never echoes the URL to stderr (it contains a password). Error messages
|
||||
# refer to "the URL" generically.
|
||||
set -euo pipefail
|
||||
|
||||
die() { echo "gstack-gbrain-supabase-verify: $*" >&2; exit 2; }
|
||||
reject_direct() {
|
||||
cat >&2 <<EOF
|
||||
gstack-gbrain-supabase-verify: rejected direct-connection URL
|
||||
|
||||
You pasted a Supabase direct-connection URL (db.*.supabase.co on port
|
||||
5432). Direct connections are IPv6-only and fail in many environments.
|
||||
|
||||
Use the Session Pooler instead:
|
||||
Supabase Dashboard → Settings → Database → Connection Pooler →
|
||||
Transaction/Session → copy URI (port 6543)
|
||||
|
||||
Expected shape:
|
||||
postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres
|
||||
EOF
|
||||
exit 3
|
||||
}
|
||||
|
||||
URL=""
|
||||
case "${1:-}" in
|
||||
-) URL=$(cat) ;;
|
||||
"") URL=$(cat) ;;
|
||||
*) URL="$1" ;;
|
||||
esac
|
||||
|
||||
URL=$(printf '%s' "$URL" | tr -d '[:space:]')
|
||||
[ -z "$URL" ] && die "empty URL"
|
||||
|
||||
# Scheme: must be postgresql:// or postgres://. Explicitly reject other
|
||||
# schemes rather than guess.
|
||||
case "$URL" in
|
||||
postgresql://*|postgres://*) ;;
|
||||
*) die "bad scheme (must start with postgresql:// or postgres://)" ;;
|
||||
esac
|
||||
|
||||
# Strip scheme to expose userinfo + host + port + path.
|
||||
rest="${URL#*://}"
|
||||
|
||||
# Userinfo portion: everything before the first @. Must contain a : (user:pass).
|
||||
case "$rest" in
|
||||
*@*) ;;
|
||||
*) die "missing userinfo (expected postgres.<ref>:<password>@host)" ;;
|
||||
esac
|
||||
userinfo="${rest%%@*}"
|
||||
after_at="${rest#*@}"
|
||||
|
||||
# Userinfo must be user:password with neither part empty.
|
||||
case "$userinfo" in
|
||||
*:*) ;;
|
||||
*) die "userinfo missing password separator (expected user:password@)" ;;
|
||||
esac
|
||||
user_part="${userinfo%%:*}"
|
||||
pass_part="${userinfo#*:}"
|
||||
[ -z "$user_part" ] && die "empty user portion in userinfo"
|
||||
[ -z "$pass_part" ] && die "empty password in userinfo"
|
||||
|
||||
# Host + port + path.
|
||||
# Direct-connection detection FIRST (specific error beats generic).
|
||||
case "$after_at" in
|
||||
db.*.supabase.co:5432*|db.*.supabase.co/*|db.*.supabase.co) reject_direct ;;
|
||||
esac
|
||||
|
||||
# Extract host:port (before first / if present).
|
||||
hostport="${after_at%%/*}"
|
||||
case "$hostport" in
|
||||
*:*) ;;
|
||||
*) die "missing port (Session Pooler requires :6543)" ;;
|
||||
esac
|
||||
host="${hostport%:*}"
|
||||
port="${hostport##*:}"
|
||||
|
||||
# Host must be *.pooler.supabase.com (case-insensitive).
|
||||
host_lower=$(printf '%s' "$host" | tr '[:upper:]' '[:lower:]')
|
||||
case "$host_lower" in
|
||||
*.pooler.supabase.com) ;;
|
||||
*) die "host '$host' is not a Supabase Session Pooler (expected *.pooler.supabase.com)" ;;
|
||||
esac
|
||||
|
||||
# Port must be 6543 (Session Pooler default).
|
||||
if [ "$port" != "6543" ]; then
|
||||
die "port must be 6543 for Session Pooler (got $port)"
|
||||
fi
|
||||
|
||||
# User portion should look like postgres.<ref> (20-char lowercase ref,
|
||||
# per the Supabase Management API contract). Not strictly required by
|
||||
# gbrain, but rejecting a plain "postgres" user catches a common paste
|
||||
# error where someone grabs the Direct URL userinfo by mistake.
|
||||
case "$user_part" in
|
||||
postgres.*) ;;
|
||||
*) die "user portion '$user_part' should be 'postgres.<project-ref>' (20-char ref)" ;;
|
||||
esac
|
||||
|
||||
echo "ok"
|
||||
Reference in New Issue
Block a user