Move to infisical. Create local dev environment. Add ci gates. Modernize repo
Build and Push Next App / quality (push) Successful in 1m8s
Build and Push Next App / build-next (push) Successful in 2m59s

This commit is contained in:
Gabriel Brown
2026-06-21 14:04:02 -05:00
parent 86e2fdc82e
commit a12bf6071b
79 changed files with 1612 additions and 42168 deletions
+9 -16
View File
@@ -1,19 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
ENV_FILE="$ROOT_DIR/.env"
COMPOSE_FILE="$ROOT_DIR/docker/compose.yml"
if [ ! -f "$ENV_FILE" ]; then
echo "Error: env file not found at $ENV_FILE" >&2
exit 1
fi
set -a
source "$ENV_FILE"
set +a
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" build "$NEXT_CONTAINER_NAME"
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
ENVIRONMENT="${1:-staging}"
[[ "$ENVIRONMENT" == dev || "$ENVIRONMENT" == staging ]] || { echo "usage: build-next-app [dev|staging]" >&2; exit 2; }
ENV_FILE="${CI_ENV_FILE:-}"
cleanup() { [[ -n "$ENV_FILE" && "$ENV_FILE" != "${CI_ENV_FILE:-}" ]] && rm -f "$ENV_FILE" || true; }
trap cleanup EXIT
if [[ -z "$ENV_FILE" && -z "${CI:-}" ]]; then ENV_FILE="$(mktemp)"; sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"; fi
args=(); [[ -z "$ENV_FILE" ]] || args+=(--env-file "$ENV_FILE")
docker compose "${args[@]}" -f "$ROOT_DIR/docker/compose.yml" build convexmonorepo-next
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/../.." && pwd)"
COMPOSE_FILE="$ROOT_DIR/docker/compose.local.yml"
STATE_FILE="$ROOT_DIR/.local/dev.generated.env"
PAYLOAD_SEED_MARKER="$ROOT_DIR/.local/payload-seed-state.env"
WIPE=false
[ "${1:-}" = "--wipe" ] && WIPE=true
if command -v docker >/dev/null 2>&1; then RUNTIME=docker
elif command -v podman >/dev/null 2>&1; then RUNTIME=podman
else echo "Docker or Podman not found; nothing to stop." >&2; exit 0; fi
ENV_FILE="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-local.XXXXXX.env")"
trap 'rm -f "$ENV_FILE"' EXIT
sh "$ROOT_DIR/scripts/export-env" dev > "$ENV_FILE"
if [ "$WIPE" = true ]; then
"$RUNTIME" compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" down -v
if [ -f "$STATE_FILE" ]; then
tmp="${STATE_FILE}.tmp"
grep -v '^CONVEX_SELF_HOSTED_ADMIN_KEY=' "$STATE_FILE" > "$tmp" || true
mv "$tmp" "$STATE_FILE"
fi
rm -f "$PAYLOAD_SEED_MARKER"
echo "Local stack and both data volumes removed; generated admin key and Payload seed marker cleared."
else
"$RUNTIME" compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" down
echo "Local stack stopped; Payload and Convex data preserved."
fi
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# Restore the local Payload database from the local snapshot only when the
# database has not already been seeded. Does not contact staging/production.
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/../.." && pwd)"
COMPOSE_FILE="$ROOT_DIR/docker/compose.local.yml"
SNAPSHOT="$ROOT_DIR/.local/payload-staging.dump"
MARKER="$ROOT_DIR/.local/payload-seed-state.env"
DEV_ENV=""
FORCE=false
ASSUME_YES=false
for arg in "$@"; do
case "$arg" in
--force) FORCE=true ;;
--yes) ASSUME_YES=true ;;
*) printf 'usage: seed-payload [--force] [--yes]\n' >&2; exit 2 ;;
esac
done
info() { printf '▶ %s\n' "$*"; }
die() { printf 'Error: %s\n' "$*" >&2; exit 1; }
if command -v docker >/dev/null 2>&1; then RUNTIME=docker
elif command -v podman >/dev/null 2>&1; then RUNTIME=podman
else die "Docker or Podman is required."; fi
"$RUNTIME" info >/dev/null 2>&1 || die "$RUNTIME is not usable."
cleanup() { [ -z "$DEV_ENV" ] || rm -f "$DEV_ENV"; }
trap cleanup EXIT INT TERM HUP
if [ ! -s "$SNAPSHOT" ]; then
echo "No local Payload snapshot found at .local/payload-staging.dump; skipping Payload seed."
exit 0
fi
mkdir -p "$ROOT_DIR/.local"
chmod 700 "$ROOT_DIR/.local"
next_is_running=false
if command -v ss >/dev/null 2>&1; then
ss -ltnH 'sport = :3000' | grep -q . && next_is_running=true
elif command -v lsof >/dev/null 2>&1; then
lsof -nP -iTCP:3000 -sTCP:LISTEN >/dev/null 2>&1 && next_is_running=true
elif curl -sS --max-time 1 http://localhost:3000 >/dev/null 2>&1; then
next_is_running=true
fi
if [ "$next_is_running" = true ]; then
die "Next is running on port 3000. Stop bun dev:next before replacing Payload data."
fi
DEV_ENV="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-dev.XXXXXX.env")"
sh "$ROOT_DIR/scripts/export-env" dev > "$DEV_ENV"
bunx dotenv -e "$DEV_ENV" -- node -e '
const raw = process.env.PAYLOAD_DB_URL;
if (!raw) process.exit(2);
const url = new URL(raw);
const local = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
if (!local) process.exit(3);
' || die "Refusing to seed: dev PAYLOAD_DB_URL is not localhost."
dc() { "$RUNTIME" compose --env-file "$DEV_ENV" -f "$COMPOSE_FILE" "$@"; }
POSTGRES_USER="$(bunx dotenv -e "$DEV_ENV" -- sh -c 'printf %s "$POSTGRES_USER"')"
POSTGRES_DB="$(bunx dotenv -e "$DEV_ENV" -- sh -c 'printf %s "$POSTGRES_DB"')"
[ -n "$POSTGRES_USER" ] && [ -n "$POSTGRES_DB" ] || die "Local Postgres configuration is incomplete."
info "Ensuring local Payload Postgres is running"
dc up -d postgres >/dev/null
for i in $(seq 1 30); do
if dc exec -T postgres pg_isready -U "$POSTGRES_USER" >/dev/null 2>&1; then break; fi
[ "$i" -lt 30 ] || die "Local Payload Postgres did not become ready."
sleep 1
done
SNAPSHOT_HASH="$(sha256sum "$SNAPSHOT" | awk '{print $1}')"
marker_exists=false
# Earlier versions stored this marker in the Payload database. Payload/Drizzle
# treats unmanaged tables as drift and prompts to delete them during dev startup,
# so keep seed state in .local/ and remove the legacy table from local DBs.
dc exec -T postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1 \
-c 'DROP TABLE IF EXISTS _local_seed_state' >/dev/null
if [ -s "$MARKER" ] && grep -qx "PAYLOAD_SNAPSHOT_SHA256=$SNAPSHOT_HASH" "$MARKER"; then
marker_exists=true
fi
if [ "$marker_exists" = true ] && [ "$FORCE" != true ]; then
echo "Payload snapshot seed already applied; skipping. Use --force to restore again."
exit 0
fi
if [ "$FORCE" = true ] && [ "$ASSUME_YES" != true ]; then
printf 'This will replace the LOCAL Payload database from .local/payload-staging.dump.\n'
read -r -p 'Continue? [y/N] ' answer
case "$answer" in y|Y|yes|YES) ;; *) echo "Cancelled."; exit 0 ;; esac
fi
info "Restoring local Payload database from .local/payload-staging.dump"
dc exec -T postgres dropdb --force --if-exists -U "$POSTGRES_USER" "$POSTGRES_DB"
dc exec -T postgres createdb -U "$POSTGRES_USER" -O "$POSTGRES_USER" "$POSTGRES_DB"
dc exec -T postgres pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" \
--no-owner --no-acl --exit-on-error < "$SNAPSHOT"
{
printf 'PAYLOAD_SNAPSHOT_SHA256=%s\n' "$SNAPSHOT_HASH"
printf 'PAYLOAD_SNAPSHOT_SEEDED_AT=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
} > "$MARKER"
chmod 600 "$MARKER"
echo "Payload snapshot seed applied."
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env bash
# Refresh the local Payload seed snapshot from staging, then force-apply it to
# the local development Payload database. Normal db:up never calls staging.
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/../.." && pwd)"
COMPOSE_FILE="$ROOT_DIR/docker/compose.local.yml"
SNAPSHOT="$ROOT_DIR/.local/payload-staging.dump"
STAGING_ENV=""
DEV_ENV=""
SOURCE_PG_ENV=""
ASSUME_YES=false
[ "${1:-}" = "--yes" ] && ASSUME_YES=true
info() { printf '▶ %s\n' "$*"; }
die() { printf 'Error: %s\n' "$*" >&2; exit 1; }
if command -v docker >/dev/null 2>&1; then RUNTIME=docker
elif command -v podman >/dev/null 2>&1; then RUNTIME=podman
else die "Docker or Podman is required."; fi
"$RUNTIME" info >/dev/null 2>&1 || die "$RUNTIME is not usable."
mkdir -p "$ROOT_DIR/.local"
chmod 700 "$ROOT_DIR/.local"
cleanup() {
[ -z "$STAGING_ENV" ] || rm -f "$STAGING_ENV"
[ -z "$DEV_ENV" ] || rm -f "$DEV_ENV"
[ -z "$SOURCE_PG_ENV" ] || rm -f "$SOURCE_PG_ENV"
rm -f "${SNAPSHOT}.tmp"
}
trap cleanup EXIT INT TERM HUP
STAGING_ENV="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-staging.XXXXXX.env")"
DEV_ENV="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-dev.XXXXXX.env")"
sh "$ROOT_DIR/scripts/export-env" staging > "$STAGING_ENV"
sh "$ROOT_DIR/scripts/export-env" dev > "$DEV_ENV"
validate_url() {
local env_file="$1" expected="$2"
bunx dotenv -e "$env_file" -- node -e '
const expected = process.argv[1];
const raw = process.env.PAYLOAD_DB_URL;
if (!raw) process.exit(2);
const url = new URL(raw);
const local = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
if ((expected === "local") !== local) process.exit(3);
' "$expected" || die "PAYLOAD_DB_URL safety check failed for the $expected environment."
}
validate_url "$STAGING_ENV" remote
validate_url "$DEV_ENV" local
next_is_running=false
if command -v ss >/dev/null 2>&1; then
ss -ltnH 'sport = :3000' | grep -q . && next_is_running=true
elif command -v lsof >/dev/null 2>&1; then
lsof -nP -iTCP:3000 -sTCP:LISTEN >/dev/null 2>&1 && next_is_running=true
elif curl -sS --max-time 1 http://localhost:3000 >/dev/null 2>&1; then
next_is_running=true
fi
if [ "$next_is_running" = true ]; then
die "Next is running on port 3000. Stop bun dev:next before replacing Payload data."
fi
SOURCE_PG_ENV="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-pg.XXXXXX.env")"
PG_ENV_FILE="$SOURCE_PG_ENV" bunx dotenv -e "$STAGING_ENV" -- sh -c '
umask 077
printf "PGDATABASE=%s\n" "$PAYLOAD_DB_URL" > "$PG_ENV_FILE"
'
if [ "$ASSUME_YES" != true ]; then
printf 'This will download staging Payload data and replace the LOCAL Payload database.\n'
printf 'The snapshot may contain user records and password hashes; it stays under .local/.\n'
read -r -p 'Continue? [y/N] ' answer
case "$answer" in y|Y|yes|YES) ;; *) echo "Cancelled."; exit 0 ;; esac
fi
info "Exporting a read-only snapshot from staging Payload Postgres"
"$RUNTIME" run --rm --network host --env-file "$SOURCE_PG_ENV" \
docker.io/library/postgres:17 \
sh -c 'exec pg_dump --dbname="$PGDATABASE" --format=custom --no-owner --no-acl' \
> "${SNAPSHOT}.tmp"
mv "${SNAPSHOT}.tmp" "$SNAPSHOT"
chmod 600 "$SNAPSHOT"
info "Ensuring local Payload Postgres is running"
bash "$ROOT_DIR/scripts/db/seed-payload" --force --yes
printf '\nLocal Payload seed snapshot refreshed and applied.\n'
printf 'Snapshot: .local/payload-staging.dump\n'
printf 'Normal db:up will reuse this snapshot and skip after .local/payload-seed-state.env exists.\n'
Executable
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/../.." && pwd)"
COMPOSE_FILE="$ROOT_DIR/docker/compose.local.yml"
STATE_FILE="$ROOT_DIR/.local/dev.generated.env"
ENV_FILE=""
info() { printf '▶ %s\n' "$*"; }
die() { printf 'Error: %s\n' "$*" >&2; exit 1; }
if command -v docker >/dev/null 2>&1; then RUNTIME=docker
elif command -v podman >/dev/null 2>&1; then RUNTIME=podman
else die "Docker or Podman is required."; fi
"$RUNTIME" info >/dev/null 2>&1 || die "$RUNTIME is not usable."
mkdir -p "$ROOT_DIR/.local"
cleanup() { [ -z "$ENV_FILE" ] || rm -f "$ENV_FILE"; }
trap cleanup EXIT
refresh_env() {
local next
next="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-local.XXXXXX.env")"
sh "$ROOT_DIR/scripts/export-env" dev > "$next" || { rm -f "$next"; die "Unable to export Infisical dev."; }
[ -z "$ENV_FILE" ] || rm -f "$ENV_FILE"
ENV_FILE="$next"
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
}
dc() { "$RUNTIME" compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" "$@"; }
upsert_state() {
local key="$1" value="$2" tmp escaped
tmp="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-state.XXXXXX.env")"
[ ! -f "$STATE_FILE" ] || grep -v "^${key}=" "$STATE_FILE" > "$tmp" || true
escaped="$(printf '%s' "$value" | sed "s/'/'\\\\''/g")"
printf "%s='%s'\n" "$key" "$escaped" >> "$tmp"
mv "$tmp" "$STATE_FILE"
}
refresh_env
info "Starting local Payload Postgres, Convex, and dashboard"
dc up -d
info "Waiting for Payload Postgres"
for i in $(seq 1 30); do
dc exec -T postgres pg_isready -U "${POSTGRES_USER:-convexmonorepo}" >/dev/null 2>&1 && break
[ "$i" -lt 30 ] || die "Postgres did not become ready."
sleep 1
done
info "Waiting for Convex at http://localhost:${BACKEND_PORT:-3210}"
for i in $(seq 1 60); do
curl -fs "http://localhost:${BACKEND_PORT:-3210}/version" >/dev/null 2>&1 && break
[ "$i" -lt 60 ] || die "Convex did not become ready."
sleep 2
done
if [ -z "${CONVEX_SELF_HOSTED_ADMIN_KEY:-}" ]; then
admin_key="$(dc exec -T convex-backend ./generate_admin_key.sh 2>/dev/null | grep -E '.+\|.+' | tail -n1 | tr -d '\r')"
[ -n "$admin_key" ] || die "Unable to generate the Convex admin key."
upsert_state CONVEX_SELF_HOSTED_ADMIN_KEY "$admin_key"
refresh_env
info "Generated the machine-local Convex admin key"
fi
info "Deploying Convex schema and functions"
(cd "$ROOT_DIR/packages/backend" && bun run setup)
convex_env_names="$(
sh "$ROOT_DIR/scripts/with-env" dev -- bash -c \
'cd packages/backend && bunx convex env list' 2>/dev/null \
| sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=.*/\1/p'
)"
if ! printf '%s\n' "$convex_env_names" | grep -qx 'JWT_PRIVATE_KEY' \
|| ! printf '%s\n' "$convex_env_names" | grep -qx 'JWKS' \
|| ! printf '%s\n' "$convex_env_names" | grep -qx 'SITE_URL'; then
info "Configuring local Convex Auth keys"
auth_keys="$(node "$ROOT_DIR/scripts/generate-convex-auth-keys.mjs")"
jwt="$(printf '%s\n' "$auth_keys" | sed -n 's/^JWT_PRIVATE_KEY="\(.*\)"$/\1/p')"
jwks="$(printf '%s\n' "$auth_keys" | sed -n 's/^JWKS=//p')"
JWT_VAL="$jwt" JWKS_VAL="$jwks" sh "$ROOT_DIR/scripts/with-env" dev -- bash -c '
cd packages/backend
bunx convex env set "JWT_PRIVATE_KEY=$JWT_VAL" >/dev/null
bunx convex env set "JWKS=$JWKS_VAL" >/dev/null
bunx convex env set "SITE_URL=http://localhost:3000" >/dev/null
'
fi
info "Seeding local Payload from snapshot if needed"
bash "$ROOT_DIR/scripts/db/seed-payload" --yes
printf '\nLocal stack ready:\n App: http://localhost:3000\n Convex: http://localhost:%s\n Dashboard: http://localhost:%s\n Payload Postgres: localhost:%s\n' "${BACKEND_PORT:-3210}" "${DASHBOARD_PORT:-6791}" "${POSTGRES_PORT:-5432}"
+18 -40
View File
@@ -1,49 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
ENV_FILE="$ROOT_DIR/.env"
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
COMPOSE_FILE="$ROOT_DIR/docker/compose.yml"
ENVIRONMENT="${1:-staging}"
if [[ "$ENVIRONMENT" == dev || "$ENVIRONMENT" == staging ]]; then shift || true; else ENVIRONMENT=staging; fi
if [ ! -f "$ENV_FILE" ]; then
echo "Error: env file not found at $ENV_FILE" >&2
exit 1
ENV_FILE="${CI_ENV_FILE:-}"
cleanup() { [[ -n "$ENV_FILE" && "$ENV_FILE" != "${CI_ENV_FILE:-}" ]] && rm -f "$ENV_FILE" || true; }
trap cleanup EXIT
if [[ -z "$ENV_FILE" && -z "${CI:-}" ]]; then
ENV_FILE="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-compose.XXXXXX.env")"
sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"
fi
if [ ! -f "$COMPOSE_FILE" ]; then
echo "Error: compose file not found at $COMPOSE_FILE" >&2
exit 1
fi
set -a
source "$ENV_FILE"
set +a
translated_args=()
args=()
[[ -z "$ENV_FILE" ]] || args+=(--env-file "$ENV_FILE")
translated=()
for arg in "$@"; do
case "$arg" in
backend)
: "${BACKEND_CONTAINER_NAME:?BACKEND_CONTAINER_NAME is not set in .env}"
translated_args+=("$BACKEND_CONTAINER_NAME")
;;
dashboard)
: "${DASHBOARD_CONTAINER_NAME:?DASHBOARD_CONTAINER_NAME is not set in .env}"
translated_args+=("$DASHBOARD_CONTAINER_NAME")
;;
next)
: "${NEXT_CONTAINER_NAME:?NEXT_CONTAINER_NAME is not set in .env}"
translated_args+=("$NEXT_CONTAINER_NAME")
;;
*)
translated_args+=("$arg")
;;
esac
case "$arg" in backend) translated+=(convexmonorepo-backend) ;; dashboard) translated+=(convexmonorepo-dashboard) ;; next) translated+=(convexmonorepo-next) ;; *) translated+=("$arg") ;; esac
done
exec docker compose \
--env-file "$ENV_FILE" \
-f "$COMPOSE_FILE" \
"${translated_args[@]}"
set +e
docker compose "${args[@]}" -f "$COMPOSE_FILE" "${translated[@]}"
status=$?
set -e
exit "$status"
Executable
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
if [ -n "${CI:-}" ]; then echo "CI detected; skipping local e2e."; exit 0; fi
if [ -n "${SKIP_E2E:-}" ]; then echo "SKIP_E2E set; skipping local e2e."; exit 0; fi
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
bash "$ROOT_DIR/scripts/db/up"
echo "Local-stack smoke checks passed; no seeded browser flow is configured."
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env sh
set -eu
[ "$#" -eq 1 ] || { echo "usage: export-env <dev|staging>" >&2; exit 2; }
ENVIRONMENT="$1"
case "$ENVIRONMENT" in dev|staging) ;; *) echo "export-env: expected dev or staging" >&2; exit 2 ;; esac
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
STATE_FILE="$ROOT_DIR/.local/$ENVIRONMENT.generated.env"
if [ -n "${CI:-}" ]; then
echo "export-env: refusing to export secrets in CI; use injected variables or CI_ENV_FILE." >&2
exit 1
fi
[ -f "$ROOT_DIR/.infisical.json" ] || { echo "export-env: run 'infisical init' in this repository." >&2; exit 1; }
command -v infisical >/dev/null 2>&1 || { echo "export-env: Infisical CLI is required." >&2; exit 1; }
(cd "$ROOT_DIR" && infisical export --env="$ENVIRONMENT" --format=dotenv --silent) || {
echo "export-env: failed to export '$ENVIRONMENT'; check login and project access." >&2
exit 1
}
if [ -f "$STATE_FILE" ]; then
printf '\n'
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in ''|'#'*) printf '%s\n' "$line"; continue ;; esac
key=${line%%=*}
value=${line#*=}
case "$value" in \'*\') value=${value#\'}; value=${value%\'} ;; \"*\") value=${value#\"}; value=${value%\"} ;; esac
escaped=$(printf '%s' "$value" | sed "s/'/'\\\\''/g")
printf "%s='%s'\n" "$key" "$escaped"
done < "$STATE_FILE"
fi
+6 -18
View File
@@ -1,20 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
ENV_FILE="$ROOT_DIR/.env"
COMPOSE_FILE="$ROOT_DIR/docker/compose.yml"
if [ ! -f "$ENV_FILE" ]; then
echo "Error: env file not found at $ENV_FILE" >&2
exit 1
fi
set -a
source "$ENV_FILE"
set +a
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" exec \
"$BACKEND_CONTAINER_NAME" ./generate_admin_key.sh
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
ENVIRONMENT="${1:-staging}"
[[ "$ENVIRONMENT" == dev || "$ENVIRONMENT" == staging ]] || { echo "usage: generate-convex-admin-key [dev|staging]" >&2; exit 2; }
ENV_FILE="$(mktemp)"; trap 'rm -f "$ENV_FILE"' EXIT
sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"
docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker/compose.yml" exec convexmonorepo-backend ./generate_admin_key.sh
+7 -20
View File
@@ -1,22 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
ENV_FILE="$ROOT_DIR/.env"
COMPOSE_FILE="$ROOT_DIR/docker/compose.yml"
if [ ! -f "$ENV_FILE" ]; then
echo "Error: env file not found at $ENV_FILE" >&2
exit 1
fi
set -a
source "$ENV_FILE"
set +a
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" pull \
"$BACKEND_CONTAINER_NAME" "$DASHBOARD_CONTAINER_NAME"
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d \
"$BACKEND_CONTAINER_NAME" "$DASHBOARD_CONTAINER_NAME"
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
ENVIRONMENT="${1:-staging}"
[[ "$ENVIRONMENT" == dev || "$ENVIRONMENT" == staging ]] || { echo "usage: update-convex [dev|staging]" >&2; exit 2; }
ENV_FILE="$(mktemp)"; trap 'rm -f "$ENV_FILE"' EXIT
sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"
docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker/compose.yml" pull convexmonorepo-backend convexmonorepo-dashboard
docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker/compose.yml" up -d convexmonorepo-backend convexmonorepo-dashboard
+7 -18
View File
@@ -1,20 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
ENV_FILE="$ROOT_DIR/.env"
COMPOSE_FILE="$ROOT_DIR/docker/compose.yml"
if [ ! -f "$ENV_FILE" ]; then
echo "Error: env file not found at $ENV_FILE" >&2
exit 1
fi
set -a
source "$ENV_FILE"
set +a
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" build "$NEXT_CONTAINER_NAME"
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d "$NEXT_CONTAINER_NAME"
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
ENVIRONMENT="${1:-staging}"
[[ "$ENVIRONMENT" == dev || "$ENVIRONMENT" == staging ]] || { echo "usage: update-next-app [dev|staging]" >&2; exit 2; }
ENV_FILE="$(mktemp)"; trap 'rm -f "$ENV_FILE"' EXIT
sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"
docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker/compose.yml" build convexmonorepo-next
docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker/compose.yml" up -d convexmonorepo-next
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env sh
set -eu
if [ "$#" -lt 1 ]; then
echo "usage: with-env <dev|staging> -- <command> [args...]" >&2
exit 2
fi
ENVIRONMENT="$1"
shift
[ "${1:-}" = "--" ] && shift
[ "$#" -gt 0 ] || { echo "with-env: no command given" >&2; exit 2; }
case "$ENVIRONMENT" in dev|staging) ;; *) echo "with-env: expected dev or staging" >&2; exit 2 ;; esac
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
STATE_FILE="$ROOT_DIR/.local/$ENVIRONMENT.generated.env"
if [ -n "${CI:-}" ]; then
export WITH_ENV_SOURCE=ci WITH_ENV_ENVIRONMENT="$ENVIRONMENT" WITH_ENV_STATE_FILE="$STATE_FILE"
exec "$@"
fi
command -v infisical >/dev/null 2>&1 || {
echo "with-env: install Infisical, run 'infisical login', and link this repo with 'infisical init'." >&2
exit 1
}
[ -f "$ROOT_DIR/.infisical.json" ] || { echo "with-env: .infisical.json is missing." >&2; exit 1; }
TMP_ENV="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-$ENVIRONMENT.XXXXXX.env")"
trap 'rm -f "$TMP_ENV"' EXIT INT TERM HUP
sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$TMP_ENV"
export WITH_ENV_SOURCE=infisical WITH_ENV_ENVIRONMENT="$ENVIRONMENT" WITH_ENV_STATE_FILE="$STATE_FILE"
set +e
bunx dotenv -e "$TMP_ENV" -- "$@"
status=$?
set -e
exit "$status"