Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@

Automated deploy reads credentials from **environment variables**, never from committed files.

## #vacation-deploy agent flow (merge + deploy)

The `#vacation-deploy` Slack channel triggers the **Paradise Planner – Deploy Agent**
automation. The agent's job is now a single two-step flow: **merge the feature branch
into `main`, then deploy `main` to the Firebase prod project.**

Run it with one command (it merges, pushes `main`, and deploys):

```bash
scripts/merge-and-deploy.sh <branch-name> # e.g. cursor/my-feature-c593
scripts/merge-and-deploy.sh <pr-number> # e.g. 9 — resolves the PR head branch via gh
```

What the script does:

1. `git fetch` and check out `main`, fast-forward to the latest remote `main`.
2. Merge the feature branch (or PR head) into `main` with a merge commit.
3. Push `main` to the remote.
4. Deploy `main` to Firebase (`hosting` by default).

Options (environment variables):

| Variable | Default | Purpose |
|----------|---------|---------|
| `DEPLOY_TARGETS` | `hosting` | Comma list of targets, e.g. `hosting,functions`. |
| `FIREBASE_PROJECT` | `vacation-app-21706` | Target Firebase project. |
| `MAIN_BRANCH` | `main` | Branch to merge into and deploy. |
| `DRY_RUN` | `0` | `1` = merge locally only; skip push + deploy (safe rehearsal). |

Requires `FIREBASE_TOKEN` (or `GOOGLE_APPLICATION_CREDENTIALS`) — already provided as a
Cloud Agent secret. If the merge hits a conflict the script stops before pushing or
deploying so the conflict can be resolved manually.

## Credentials (do not commit)

| Variable | Where to set | How to get |
Expand Down
95 changes: 95 additions & 0 deletions scripts/merge-and-deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env bash
# Merge a feature branch into main, then deploy main to the Firebase prod project.
#
# This is the canonical entrypoint for the #vacation-deploy agent flow:
# 1. Merge the given feature branch (or PR head) into the main branch.
# 2. Push the updated main branch to the remote.
# 3. Deploy main to Firebase (Hosting by default, optionally Functions).
#
# Requires FIREBASE_TOKEN (CI token) or GOOGLE_APPLICATION_CREDENTIALS (service account).
#
# Usage:
# scripts/merge-and-deploy.sh <branch-name> # merge a branch
# scripts/merge-and-deploy.sh 9 # merge PR #9 (resolves head branch via gh)
# DEPLOY_BRANCH=my-branch scripts/merge-and-deploy.sh
# DEPLOY_TARGETS="hosting,functions" scripts/merge-and-deploy.sh my-branch
# DRY_RUN=1 scripts/merge-and-deploy.sh my-branch # do everything locally, skip push + deploy
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

# Local dev convenience: load .env for FIREBASE_TOKEN. Cloud agents inject it as env.
if [[ -f "$ROOT/.env" ]]; then
set -a
# shellcheck disable=SC1091
source "$ROOT/.env"
set +a
fi

REF="${1:-${DEPLOY_BRANCH:-${DEPLOY_PR:-}}}"
MAIN_BRANCH="${MAIN_BRANCH:-main}"
REMOTE="${GIT_REMOTE:-origin}"
PROJECT="${FIREBASE_PROJECT:-vacation-app-21706}"
DEPLOY_TARGETS="${DEPLOY_TARGETS:-hosting}"
DRY_RUN="${DRY_RUN:-0}"

if [[ -z "$REF" ]]; then
echo "Usage: scripts/merge-and-deploy.sh <branch-or-PR> (or set DEPLOY_BRANCH / DEPLOY_PR)" >&2
exit 1
fi

if [[ -z "${FIREBASE_TOKEN:-}" && -z "${GOOGLE_APPLICATION_CREDENTIALS:-}" ]]; then
echo "Error: set FIREBASE_TOKEN or GOOGLE_APPLICATION_CREDENTIALS before deploying." >&2
echo " Cloud: Cursor dashboard -> Cloud Agents -> Secrets -> FIREBASE_TOKEN" >&2
exit 1
fi

# Resolve a PR number to its head branch (requires gh). A purely numeric REF is a PR.
BRANCH="$REF"
if [[ "$REF" =~ ^[0-9]+$ ]]; then
echo "==> Resolving PR #$REF head branch via gh..."
BRANCH="$(gh pr view "$REF" --json headRefName -q .headRefName)"
echo " PR #$REF -> $BRANCH"
fi

echo "==> Fetching $REMOTE..."
git fetch "$REMOTE" --prune

if ! git rev-parse --verify "$REMOTE/$BRANCH" >/dev/null 2>&1; then
echo "Error: branch '$BRANCH' not found on remote '$REMOTE'." >&2
exit 1
fi

echo "==> Checking out $MAIN_BRANCH..."
git checkout "$MAIN_BRANCH"
git pull --ff-only "$REMOTE" "$MAIN_BRANCH"

echo "==> Merging $BRANCH into $MAIN_BRANCH..."
git merge --no-ff --no-edit "$REMOTE/$BRANCH" \
-m "Merge branch '$BRANCH' into $MAIN_BRANCH (automated #vacation-deploy)"

if [[ "$DRY_RUN" == "1" ]]; then
echo "==> DRY_RUN=1: merge completed locally; skipping push and deploy."
echo " HEAD is now $(git rev-parse --short HEAD) on $MAIN_BRANCH."
exit 0
fi

echo "==> Pushing $MAIN_BRANCH to $REMOTE..."
git push "$REMOTE" "$MAIN_BRANCH"

# Deploy Hosting (build + publish) when requested.
if [[ ",$DEPLOY_TARGETS," == *",hosting,"* ]]; then
echo "==> Deploying Hosting to $PROJECT..."
FIREBASE_PROJECT="$PROJECT" "$ROOT/scripts/deploy-hosting.sh"
fi

# Deploy Functions when requested.
if [[ ",$DEPLOY_TARGETS," == *",functions,"* ]]; then
echo "==> Deploying Functions to $PROJECT..."
npm --prefix "$ROOT/functions" install
npx firebase-tools@latest deploy --only functions --project "$PROJECT" --non-interactive
fi

echo "==> Done. Deployed $MAIN_BRANCH ($DEPLOY_TARGETS) to $PROJECT."
echo " Smoke test: https://$PROJECT.web.app"