diff --git a/.github/workflows/grid-wallet-prod-image.yml b/.github/workflows/grid-wallet-prod-image.yml new file mode 100644 index 000000000..8a1465ab7 --- /dev/null +++ b/.github/workflows/grid-wallet-prod-image.yml @@ -0,0 +1,87 @@ +name: grid-wallet-prod image + +on: + push: + branches: [main] + paths: + - 'components/grid-wallet-prod/**' + - '.github/workflows/grid-wallet-prod-image.yml' + +permissions: + id-token: write + contents: read + +concurrency: + group: grid-wallet-prod-image + cancel-in-progress: false + +jobs: + build: + # The tooling-prod cluster is Graviton (m8g), so the image must be + # linux/arm64. A native ARM runner avoids QEMU emulation, which makes a + # Next.js build painfully slow. NOTE: no other workflow in this repo uses + # an ARM label yet — if this job sits in `queued`, GitHub-hosted ARM + # runners are not enabled for the org and the fallback is + # docker/setup-qemu-action + docker/setup-buildx-action on ubuntu-latest. + runs-on: ubuntu-24.04-arm + # Actions are pinned to full commit SHAs, not major tags. This job holds + # id-token: write and assumes a role that can push to ECR, so an upstream + # tag move would silently change code running with cloud write access. + # NOTE: this repo has no Dependabot config, so these pins do not update + # themselves — re-resolve them when bumping, or add a dependabot.yml with + # a github-actions ecosystem entry so they stay current. + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + + - uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 + with: + role-to-assume: arn:aws:iam::405785876631:role/github-actions-grid-wallet-prod + aws-region: us-west-2 + + - id: ecr + uses: aws-actions/amazon-ecr-login@d539f0932e70871a027e9d5a9d8fc38589180a64 # v2.1.6 + + - name: Build and push + working-directory: components/grid-wallet-prod + env: + REGISTRY: ${{ steps.ecr.outputs.registry }} + run: | + set -euo pipefail + TAG="sha-${GITHUB_SHA}" + IMAGE="${REGISTRY}/grid-wallet-prod:${TAG}" + + # The ECR repo is IMMUTABLE-tagged, so re-pushing an existing tag is + # a hard failure. A re-run (manual retry, cancelled-then-resumed job) + # must be a no-op rather than a red build. + # + # Distinguish "tag absent" from "the call failed". A bare + # `if aws ... >/dev/null 2>&1` is exempt from set -e and treats an + # IAM/network error identically to a missing image, so a transient + # failure would fall through to a build and then die on the immutable + # push. Only ImageNotFoundException means "go build". + set +e + describe_err=$(aws ecr describe-images --repository-name grid-wallet-prod \ + --image-ids imageTag="$TAG" --region us-west-2 2>&1 >/dev/null) + describe_rc=$? + set -e + if [ "$describe_rc" -eq 0 ]; then + echo "$IMAGE already present — nothing to do." + exit 0 + elif ! grep -q 'ImageNotFoundException' <<<"$describe_err"; then + echo "::error::describe-images failed for a reason other than a missing tag:" + echo "$describe_err" + exit 1 + fi + echo "$TAG not present — building." + + # NEXT_PUBLIC_* is inlined by Next at BUILD time, so the sandbox flag + # is a build arg, not a runtime var. It is written literally here and + # has NO default in the Dockerfile: a production image would be a + # separate workflow and a separate ECR repo, so this flag cannot leak + # by inheritance. + docker build \ + --platform linux/arm64 \ + --build-arg NEXT_PUBLIC_GRID_SANDBOX=true \ + -t "$IMAGE" . + docker push "$IMAGE" + echo "Pushed $IMAGE" diff --git a/components/grid-wallet-prod/Dockerfile b/components/grid-wallet-prod/Dockerfile index b03e7c6ed..933e569ab 100644 --- a/components/grid-wallet-prod/Dockerfile +++ b/components/grid-wallet-prod/Dockerfile @@ -19,7 +19,14 @@ WORKDIR /app # postinstall would otherwise pull ~400MB of browsers into the build. ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 COPY package.json package-lock.json ./ -RUN npm ci +# --ignore-scripts matches vercel.json's installCommand for this component. +# Without it the build dies in the deps stage: the seven @central-icons-react +# packages each run a postinstall license-check.js that hard-fails unless +# CENTRAL_LICENSE_KEY is in the environment. Vercel has always skipped these +# scripts, which is why the Vercel deployments work and this Dockerfile (added +# unbuilt in #740) never did. None of this component's dependencies need a +# postinstall to produce a working build. +RUN npm ci --ignore-scripts # ── build ───────────────────────────────────────────────────────────────────── FROM node:22-alpine AS builder diff --git a/docs/superpowers/specs/2026-07-21-grid-wallet-prod-fork-design.md b/docs/superpowers/specs/2026-07-21-grid-wallet-prod-fork-design.md new file mode 100644 index 000000000..2291a6b2c --- /dev/null +++ b/docs/superpowers/specs/2026-07-21-grid-wallet-prod-fork-design.md @@ -0,0 +1,82 @@ +# Grid Wallet Prod — Phase 1: fork the demo, remove the config sidebar + +**Date:** 2026-07-21 +**Status:** Approved (approach A) + +## Goal + +Fork `components/grid-wallet-demo` into `components/grid-wallet-prod`, the starting point for a +**live production demo**: real Grid API requests, real funds, production keys, with the API-call +panel still visible beside the phone. + +This spec covers **Phase 1 only**: the fork, removing the left config sidebar, and hardcoding +passkey sign-in. Wiring the scripted calls in `src/data/actions.ts` to the real Grid production +API is **Phase 2** and gets its own spec — real funds means auth, key handling, error states, and +irreversibility all need a dedicated design pass. + +## Decisions + +- **Location:** `components/grid-wallet-prod` (sibling of the demo; its own Vercel project later). +- **Dev port:** 4001, so the demo (pinned to 4000) and prod fork run side by side. +- **Sidebar removal:** remove cleanly (approach A) — delete usage and component files, don't gate + behind a flag. +- **Sign-in:** hardcoded to passkey; use case pinned to Fintech. +- **Original demo untouched:** no changes to `components/grid-wallet-demo` or the docs iframe + that embeds it. + +## Changes + +### 1. Fork + +Copy `components/grid-wallet-demo` → `components/grid-wallet-prod`, excluding `node_modules`, +`.next`, and build artifacts. In the fork: + +- `package.json`: rename package to `grid-wallet-prod`; dev/start scripts use port 4001. +- `README.md`: retitle for the production demo; note the demo remains the docs-embedded variant. + +### 2. Remove the left nav (config sidebar) + +In the fork's `src/app/page.tsx`: + +- Delete the `configCol` div and the `ConfigurePanel` import/usage. +- Delete the mobile `configure ⇄ playground` view switching that only existed because of the + sidebar: `mobileView` state, `goPlayground`/`goConfigure`, `onConfigureAction`, the back pill, + the "Explore playground" button, the progressive-blur tray, the scroll-hide effect, and the + popstate handler. Mobile renders the playground directly. +- Keep `useWalletDemoLogic` fully intact — the phone already drives every action through it. + +In `src/app/page.module.scss`: drop the config column from the layout so it becomes +`phone (appCol) + resize handle + API panel (apiCol)`; remove styles for the deleted mobile +chrome; keep the stacked ⇄ wide breakpoint behavior for the two remaining columns. + +Delete `src/components/ConfigurePanel/` from the fork. + +### 3. Hardcode passkey + Fintech + +In the fork's `useWalletDemoLogic`: pin `useCase` to Fintech and `methods` to `['passkey']` +(remove or no-op the setters the sidebar used). The phone's sign-in screen then offers only +"Continue with Passkey". + +Google/Apple/Email/Phone auth screens stay in `Phone.tsx` but are unreachable. They get pruned in +Phase 2 when real auth lands, so the phone file isn't churned twice. + +### 4. Unchanged for now + +Header, footer, theme sync/embed contract, all phone visuals, and the API panel (still fed by the +scripted `entries` until Phase 2). + +## Verification + +- `npm install --ignore-scripts && npm run dev` in the fork serves on :4001. +- No config sidebar at any viewport; phone + API panel lay out correctly wide and stacked. +- Sign-in screen shows only passkey; the full flow (create account → add money → send → cash out → + issue card → tap to pay) still runs from the phone, with API calls appearing in the panel. +- `npm run build` passes with no references to the deleted `ConfigurePanel`. +- The original demo still runs unmodified on :4000. + +## Out of scope (Phase 2+) + +- Live Grid production API integration (real requests, real funds, production key handling). +- Real passkey/WebAuthn ceremony. +- Pruning unused auth screens and demo-only code paths from `Phone.tsx`. +- Vercel project + deployment for the prod app.