Files
Htmx/GCR/scripts/04-deploy.sh
T

181 lines
7.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# 04-deploy.sh (Linux)
# Builds the Docker image, pushes it to Artifact Registry, and deploys it
# to Cloud Run — all in one command.
#
# Usage:
# ./GCR/scripts/04-deploy.sh # deploy with tag = git short SHA
# ./GCR/scripts/04-deploy.sh my-tag # deploy with a custom tag
#
# Prerequisites:
# 1. GCR/.env exists and is filled in (copy from GCR/.env.example)
# 2. 01-login.sh has been run (gcloud auth + Docker configured)
# 3. 02-setup-project.sh has been run (APIs enabled, repo created)
# 4. 03-create-secrets.sh has been run (MongoDB secret created)
# 5. Docker daemon is running locally
#
# Windows users: run GCR/scripts/04-deploy.ps1 in PowerShell instead.
# =============================================================================
set -euo pipefail
if [[ "$(uname -s)" != "Linux" ]]; then
echo "ERROR: This script is for Linux only."
echo "Windows users: run GCR/scripts/04-deploy.ps1"
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# ── Load .env ─────────────────────────────────────────────────────────────────
ENV_FILE="$SCRIPT_DIR/../.env"
if [[ ! -f "$ENV_FILE" ]]; then
echo "ERROR: $ENV_FILE not found."
echo "Copy GCR/.env.example to GCR/.env and fill in your values first."
exit 1
fi
# shellcheck disable=SC1090
source "$ENV_FILE"
: "${GCP_PROJECT_ID:?GCP_PROJECT_ID is not set in .env}"
: "${GCP_REGION:?GCP_REGION is not set in .env}"
: "${GCP_REPOSITORY:?GCP_REPOSITORY is not set in .env}"
: "${SERVICE_NAME:?SERVICE_NAME is not set in .env}"
: "${MONGODB_DATABASE_NAME:?MONGODB_DATABASE_NAME is not set in .env}"
# Note: MONGODB_CONNECTION_STRING is stored in Secret Manager (mongodb-connection-string)
# See GCR/README.md for Secret Manager setup
secret_setup_ready() {
local service_account
service_account="serviceAccount:${GCP_PROJECT_ID}@appspot.gserviceaccount.com"
gcloud secrets describe "mongodb-connection-string" --project="$GCP_PROJECT_ID" >/dev/null 2>&1 || return 1
gcloud secrets get-iam-policy "mongodb-connection-string" \
--project="$GCP_PROJECT_ID" \
--flatten="bindings[].members" \
--filter="bindings.role=roles/secretmanager.secretAccessor AND bindings.members=${service_account}" \
--format="value(bindings.members)" 2>/dev/null \
| grep -Fxq "$service_account"
}
if ! secret_setup_ready; then
echo ""
echo ">>> Required secrets are not fully configured yet."
read -rp " Run GCR/scripts/03-create-secrets.sh now? [y/N]: " RUN_SECRET_SETUP
if [[ "$RUN_SECRET_SETUP" =~ ^[Yy]$ ]]; then
bash "$SCRIPT_DIR/03-create-secrets.sh"
else
echo ""
echo "ERROR: Deployment requires secret setup first."
echo "Run: bash GCR/scripts/03-create-secrets.sh"
exit 1
fi
if ! secret_setup_ready; then
echo ""
echo "ERROR: Secret setup check still failing after running 03-create-secrets.sh."
exit 1
fi
fi
# ── Determine image tag ────────────────────────────────────────────────────────
TAG="${1:-}"
if [[ -z "$TAG" ]]; then
# Default to git short SHA if inside a git repo; otherwise use timestamp
if git -C "$REPO_ROOT" rev-parse --short HEAD &>/dev/null; then
TAG="$(git -C "$REPO_ROOT" rev-parse --short HEAD)"
else
TAG="$(date +%Y%m%d%H%M%S)"
fi
fi
REGISTRY="${GCP_REGION}-docker.pkg.dev"
IMAGE_URI="${REGISTRY}/${GCP_PROJECT_ID}/${GCP_REPOSITORY}/${SERVICE_NAME}:${TAG}"
echo "================================================================"
echo " Htmx → Cloud Run deployment"
echo "================================================================"
echo " Project: $GCP_PROJECT_ID"
echo " Region: $GCP_REGION"
echo " Service: $SERVICE_NAME"
echo " Image: $IMAGE_URI"
echo "================================================================"
echo ""
# ── Step 1: Ensure package-lock.json exists (required for `npm ci`) ───────────
LOCKFILE="$REPO_ROOT/Htmx.ApiDemo/package-lock.json"
if [[ ! -f "$LOCKFILE" ]]; then
echo ">>> package-lock.json not found. Generating it now..."
echo " (This requires node + npm to be installed locally)"
(cd "$REPO_ROOT/Htmx.ApiDemo" && npm install --package-lock-only)
echo " package-lock.json generated. Commit it to the repository."
echo ""
fi
# ── Step 2: Build the Docker image ────────────────────────────────────────────
echo ">>> Building Docker image..."
echo " Context: $REPO_ROOT"
echo " Dockerfile: GCR/Dockerfile"
echo ""
# Build from repo root so the COPY instructions can reach both
# Htmx.ApiDemo/ and Htmx.SourceGenerator/ directories.
docker build \
--file "$REPO_ROOT/GCR/Dockerfile" \
--tag "$IMAGE_URI" \
"$REPO_ROOT"
echo ""
echo ">>> Image built: $IMAGE_URI"
# ── Step 3: Push image to Artifact Registry ───────────────────────────────────
echo ""
echo ">>> Pushing image to Artifact Registry..."
docker push "$IMAGE_URI"
echo ">>> Push complete."
# ── Step 4: Deploy to Cloud Run via docker-compose.yml ───────────────────────
echo ""
echo ">>> Deploying to Cloud Run..."
# Export variables consumed by docker-compose.yml substitution
export IMAGE_URI
export MONGODB_DATABASE_NAME
gcloud run services replace "$REPO_ROOT/GCR/docker-compose.yml" \
--region="$GCP_REGION" \
--project="$GCP_PROJECT_ID"
# ── Step 4b: Inject MongoDB connection string from Secret Manager ────────────
echo ""
echo ">>> Injecting MongoDB connection string from Secret Manager..."
gcloud run services update "$SERVICE_NAME" \
--region="$GCP_REGION" \
--project="$GCP_PROJECT_ID" \
--set-secrets="ConnectionStrings__DefaultConnection=mongodb-connection-string:latest"
# ── Step 5: Make the service publicly accessible ──────────────────────────────
# Remove this block if you want the service to require authentication.
echo ""
echo ">>> Allowing public (unauthenticated) access to the service..."
gcloud run services add-iam-policy-binding "$SERVICE_NAME" \
--region="$GCP_REGION" \
--project="$GCP_PROJECT_ID" \
--member="allUsers" \
--role="roles/run.invoker"
# ── Print service URL ─────────────────────────────────────────────────────────
echo ""
SERVICE_URL=$(gcloud run services describe "$SERVICE_NAME" \
--region="$GCP_REGION" \
--project="$GCP_PROJECT_ID" \
--format="value(status.url)")
echo "================================================================"
echo " Deployment complete!"
echo " Service URL: $SERVICE_URL"
echo "================================================================"