From 4ef2e3811c71af7b2c39da98d82e6ce5b03af3af Mon Sep 17 00:00:00 2001 From: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:35:57 +0000 Subject: [PATCH] ci(preview): give PR previews the Stirling account config they need to link (#7728) Add CI steps to enable PR deploy servers to link to prod saas. This will allow pr testing of payment flows, usage of real credits etc --- .github/workflows/PR-Auto-Deploy-V2.yml | 46 +++++++++++++++++++++++++ docker/embedded/Dockerfile | 14 ++++++++ 2 files changed, 60 insertions(+) diff --git a/.github/workflows/PR-Auto-Deploy-V2.yml b/.github/workflows/PR-Auto-Deploy-V2.yml index c55d70fd4e..b4bbd111e1 100644 --- a/.github/workflows/PR-Auto-Deploy-V2.yml +++ b/.github/workflows/PR-Auto-Deploy-V2.yml @@ -220,6 +220,42 @@ jobs: echo "app_short=${APP_HASH:0:8}" >> $GITHUB_OUTPUT fi + # The Stirling account previews connect to. Derived from the ref rather than stored as a URL + # so it cannot drift from the key: a mismatched pair is accepted by the browser and rejected + # by Supabase, surfacing much later as "session expired" on Usage rather than at sign-in. + # Secret only to match Saas-Dev-Deploy.yml, which owns the same value; a project ref is not + # itself sensitive, which is why SAAS_API_BASE_URL next to it is a plain variable. + - name: Resolve Stirling account config + id: saas + env: + PROJECT_REF: ${{ secrets.SAAS_DB_PROJECT_REF }} + API_BASE_OVERRIDE: ${{ vars.SAAS_API_BASE_URL }} + run: | + # Set, this is the one value both halves use: the browser's portal reads and the backend's + # register/entitlement calls have to land on the same SaaS, and nothing checks that they + # do. Unset, only the backend gets a base, from its own compiled-in default. + API_BASE="${API_BASE_OVERRIDE:-https://stirling.com/app}" + echo "backend_base=${API_BASE}" >> "$GITHUB_OUTPUT" + + if [ -z "${PROJECT_REF}" ]; then + echo "Not configured for this environment: the preview will build without a Stirling" + echo "account, and the connect dialog will say so. To wire one up, set on the" + echo "pr-preview environment the secrets SAAS_DB_PROJECT_REF and" + echo "SAAS_SUPABASE_PUBLISHABLE_KEY, both from the same Supabase project." + echo "supabase_url=" >> "$GITHUB_OUTPUT" + echo "frontend_base=" >> "$GITHUB_OUTPUT" + else + # Only whether, not which: the ref is a secret here, so Actions masks it out of any + # line it appears in, derived URL included. + echo "Stirling account configured, at ${API_BASE}." + echo "supabase_url=https://${PROJECT_REF}.supabase.co" >> "$GITHUB_OUTPUT" + # Deliberately the override and not API_BASE: the backend's default is a subpath URL + # nobody has confirmed answers /api/v1, and prod CORS does not list preview hostnames, + # so portal reads stay off until someone sets a base they have checked. Empty leaves the + # committed .env default alone, which is the clean "not configured" state. + echo "frontend_base=${API_BASE_OVERRIDE}" >> "$GITHUB_OUTPUT" + fi + - name: Check if image exists id: check-image run: | @@ -246,6 +282,9 @@ jobs: build-args: | VERSION_TAG=v2-alpha BUILD_PORTAL=${{ env.BUILD_PORTAL }} + VITE_SUPABASE_URL=${{ steps.saas.outputs.supabase_url }} + VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY=${{ secrets.SAAS_SUPABASE_PUBLISHABLE_KEY }} + VITE_SAAS_API_URL=${{ steps.saas.outputs.frontend_base }} platforms: linux/amd64 - name: Set up SSH @@ -279,6 +318,13 @@ jobs: environment: DISABLE_ADDITIONAL_FEATURES: "false" STIRLING_BILLING_ACCOUNT_LINK_ENABLED: "true" + STIRLING_BILLING_ACCOUNT_LINK_SAAS_BASE_URL: "${{ steps.saas.outputs.backend_base }}" + # Off so preview traffic never accrues against a real wallet or trips its cap. The + # 402 gate is separate and stays on, so gating is still testable here. + STIRLING_BILLING_ACCOUNT_LINK_METERING_ENABLED: "false" + # Stated rather than inferred from the request: the callback has to come back to the + # preview hostname, not to the container's own :8080 behind this proxy. + SYSTEM_FRONTENDURL: "https://${V2_PORT}.ssl.stirlingpdf.cloud" SECURITY_ENABLELOGIN: "true" SECURITY_INITIALLOGIN_USERNAME: "${TEST_LOGIN_USERNAME}" SECURITY_INITIALLOGIN_PASSWORD: "${TEST_LOGIN_PASSWORD}" diff --git a/docker/embedded/Dockerfile b/docker/embedded/Dockerfile index 80e163dd9a..6f525b2732 100644 --- a/docker/embedded/Dockerfile +++ b/docker/embedded/Dockerfile @@ -48,9 +48,23 @@ ENV STIRLING_FLAVOR=${STIRLING_FLAVOR} # portal or AI layers change; defaults false so normal builds skip the extra app. ARG BUILD_PORTAL=false +# Which Stirling account the portal connects to. Build-time because Vite inlines VITE_* into the +# bundle; there is no runtime override. Empty leaves the committed .env.proprietary defaults, which +# is what an ordinary image wants: no Stirling account and no connect flow. The publishable key is +# client-side by design, not a secret. Pass the URL and the key from the same Supabase project or +# the browser accepts the pair and Supabase rejects it, which surfaces later as "session expired". +ARG VITE_SUPABASE_URL="" +ARG VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY="" +ARG VITE_SAAS_API_URL="" + # Bundle only the JPDFium native for this image's target arch. ARG TARGETARCH +# Exported only when non-empty: Vite reads process.env ahead of the .env files, so exporting an +# empty value would blank the committed default rather than fall back to it. RUN JPDFIUM_PLATFORM="$([ "$TARGETARCH" = arm64 ] && echo linux-arm64 || echo linux-x64)" && \ + if [ -n "${VITE_SUPABASE_URL}" ]; then export VITE_SUPABASE_URL="${VITE_SUPABASE_URL}"; fi; \ + if [ -n "${VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY}" ]; then export VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY="${VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY}"; fi; \ + if [ -n "${VITE_SAAS_API_URL}" ]; then export VITE_SAAS_API_URL="${VITE_SAAS_API_URL}"; fi; \ STIRLING_FLAVOR=${STIRLING_FLAVOR} \ gradle clean build \ -PbuildWithFrontend=true \