Template
Read CI env from Infisical and give the local stack its own ports
CI reads Infisical staging as the read-only convexmonorepo-base-ci machine identity, runs the quality gate, and builds and pushes the image. The template is not deployed, so there is no Convex or VPS deploy step. The local stack moves to its own Compose name and ports so it runs beside the Payload template's, and it syncs the Convex runtime env. The scripts pick up Infisical account switching, dev-kill and a cache-safe CI install.
This commit is contained in:
1 parent
55b253257b
commit
8f09a423d4
18 files changed
+452
-55
No files matched your search
@@ -2,55 +2,86 @@ name: Build and Push Next App
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches:
|
||||||
|
- main
|
||||||
paths:
|
paths:
|
||||||
- 'apps/**'
|
- 'apps/next/**'
|
||||||
- 'packages/**'
|
- 'packages/**'
|
||||||
- 'tools/**'
|
- 'tools/**'
|
||||||
- 'scripts/**'
|
- 'scripts/**'
|
||||||
- 'docker/**'
|
- 'docker/**'
|
||||||
- '.gitea/workflows/build-next.yml'
|
- '.gitea/workflows/build-next.yml'
|
||||||
- '.infisical.json'
|
|
||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'bun.lock'
|
- 'bun.lock'
|
||||||
- 'turbo.json'
|
- 'turbo.json'
|
||||||
|
|
||||||
|
# One image build at a time; a newer push waits rather than racing.
|
||||||
|
concurrency:
|
||||||
|
group: build-next
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
# This template is not deployed. CI runs the quality gate and builds and pushes
|
||||||
|
# the image so the template stays known-good. Env comes from the Infisical
|
||||||
|
# `staging` environment: each job logs in as the read-only
|
||||||
|
# `convexmonorepo-base-ci` machine identity and writes it to a temp file
|
||||||
|
# (scripts/ci-export-env.mjs), so no values live in Gitea.
|
||||||
jobs:
|
jobs:
|
||||||
|
# Quality gate: workspace lint (sherif), eslint, typecheck and the unit,
|
||||||
|
# integration and component suites must pass before anything touches
|
||||||
|
# production. The local-stack e2e smoke check is skipped in CI.
|
||||||
quality:
|
quality:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repository
|
||||||
- uses: oven-sh/setup-bun@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
with:
|
with:
|
||||||
bun-version: 1.3.10
|
bun-version: 1.3.14
|
||||||
- run: bun install --frozen-lockfile
|
|
||||||
- name: Lint, typecheck, and test
|
- name: Install dependencies
|
||||||
|
run: ./scripts/ci-install
|
||||||
|
|
||||||
|
- name: Lint and typecheck
|
||||||
env:
|
env:
|
||||||
DOTENV_PROD: ${{ secrets.DOTENV_PROD }}
|
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
|
||||||
|
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
|
||||||
run: |
|
run: |
|
||||||
env_file="$(mktemp)"
|
env_file="$(mktemp)"
|
||||||
trap 'rm -f "$env_file"' EXIT
|
trap 'rm -f "$env_file"' EXIT
|
||||||
printf '%s\n' "$DOTENV_PROD" > "$env_file"
|
node scripts/ci-export-env.mjs staging > "$env_file"
|
||||||
bunx dotenv -e "$env_file" -- env NODE_ENV=test SKIP_E2E=1 bun run ci:check
|
bunx dotenv -e "$env_file" -- env NODE_ENV=test SKIP_E2E=1 bun run ci:check
|
||||||
|
|
||||||
build-next:
|
build-next:
|
||||||
needs: [quality]
|
needs: [quality]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Log in to container registry
|
- name: Log in to container registry
|
||||||
run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.gbrown.org -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
run: |
|
||||||
- name: Build image
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.gbrown.org -u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||||
|
|
||||||
|
- name: Build app using project script
|
||||||
env:
|
env:
|
||||||
DOTENV_PROD: ${{ secrets.DOTENV_PROD }}
|
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
|
||||||
|
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
|
||||||
run: |
|
run: |
|
||||||
env_file="$(mktemp)"
|
env_file="$(mktemp)"
|
||||||
trap 'rm -f "$env_file"' EXIT
|
trap 'rm -f "$env_file"' EXIT
|
||||||
printf '%s\n' "$DOTENV_PROD" > "$env_file"
|
node scripts/ci-export-env.mjs staging > "$env_file"
|
||||||
CI_ENV_FILE="$env_file" ./scripts/build-next-app staging
|
CI_ENV_FILE="$env_file" ./scripts/build-next-app staging
|
||||||
- name: Tag and push image
|
|
||||||
|
- name: Tag built image
|
||||||
run: |
|
run: |
|
||||||
docker tag convexmonorepo-no-payload-next:latest git.gbrown.org/gib/convexmonorepo-no-payload-next:${{ gitea.sha }}
|
docker tag convexmonorepo-no-payload-next:latest git.gbrown.org/gib/convexmonorepo-no-payload-next:${{ gitea.sha }}
|
||||||
docker tag convexmonorepo-no-payload-next:latest git.gbrown.org/gib/convexmonorepo-no-payload-next:latest
|
docker tag convexmonorepo-no-payload-next:latest git.gbrown.org/gib/convexmonorepo-no-payload-next:latest
|
||||||
|
|
||||||
|
- name: Push image tags
|
||||||
|
run: |
|
||||||
docker push git.gbrown.org/gib/convexmonorepo-no-payload-next:${{ gitea.sha }}
|
docker push git.gbrown.org/gib/convexmonorepo-no-payload-next:${{ gitea.sha }}
|
||||||
docker push git.gbrown.org/gib/convexmonorepo-no-payload-next:latest
|
docker push git.gbrown.org/gib/convexmonorepo-no-payload-next:latest
|
||||||
@@ -66,3 +66,6 @@ apps/next/playwright-report/
|
|||||||
|
|
||||||
# tanstack
|
# tanstack
|
||||||
.tanstack
|
.tanstack
|
||||||
|
|
||||||
|
# local command flags (machine-specific, e.g. which Infisical account to use)
|
||||||
|
scripts/flags/
|
||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"workspaceId": "b2760150-c581-4e0d-acb5-b4da51cee58d",
|
"workspaceId": "93d78472-e1a0-4faf-8874-e57aa030a06f",
|
||||||
"defaultEnvironment": "",
|
"defaultEnvironment": "",
|
||||||
"gitBranchToEnvironmentMapping": null
|
"gitBranchToEnvironmentMapping": null,
|
||||||
|
"domain": "https://infisical.gbrown.org/api"
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
- `packages/backend/convex`: self-hosted Convex functions, schema, and auth.
|
- `packages/backend/convex`: self-hosted Convex functions, schema, and auth.
|
||||||
- `packages/ui`: shared shadcn-based UI components.
|
- `packages/ui`: shared shadcn-based UI components.
|
||||||
- `tools`: shared ESLint, Prettier, Tailwind, TypeScript, and Vitest config.
|
- `tools`: shared ESLint, Prettier, Tailwind, TypeScript, and Vitest config.
|
||||||
- Local development uses host-run apps, local Convex on ports 3210/3211, and
|
- Local development uses host-run apps, local Convex on ports 3250/3251, and
|
||||||
its own data volume. Convex does not use Postgres unless a cloned project
|
its own data volume. Convex does not use Postgres unless a cloned project
|
||||||
explicitly enables the commented optional `POSTGRES_URL` example.
|
explicitly enables the commented optional `POSTGRES_URL` example.
|
||||||
|
|
||||||
@@ -27,8 +27,18 @@
|
|||||||
- Run `infisical login` and `infisical init` before local development.
|
- Run `infisical login` and `infisical init` before local development.
|
||||||
- Machine-generated values belong in `.local/<env>.generated.env`; never put
|
- Machine-generated values belong in `.local/<env>.generated.env`; never put
|
||||||
the generated Convex admin key in shared Infisical.
|
the generated Convex admin key in shared Infisical.
|
||||||
- CI uses Gitea-injected secrets or `CI_ENV_FILE` and must not call Infisical.
|
- CI reads `staging` through `scripts/ci-export-env.mjs`, logged in as the
|
||||||
- App code imports validated variables from `@/env`, never `process.env`.
|
read-only `convexmonorepo-base-ci` machine identity (Gitea secrets
|
||||||
|
`INFISICAL_CLIENT_ID` and `INFISICAL_CLIENT_SECRET`). `scripts/export-env`
|
||||||
|
refuses to run in CI.
|
||||||
|
- This template's `staging` has no Convex admin key, so nothing here can deploy
|
||||||
|
to a real backend. A cloned app gets its own Infisical project.
|
||||||
|
- If you are logged in to more than one Infisical account, put
|
||||||
|
`INFISICAL_EMAIL=...` in `scripts/flags/infisical.env` (gitignored) and the
|
||||||
|
scripts switch the CLI to that account before exporting.
|
||||||
|
- Server code imports validated variables from `@/env`. Client components and
|
||||||
|
`instrumentation-client.ts` import from `@/publicEnv`, which keeps zod out of
|
||||||
|
the browser bundle. Never read `process.env` elsewhere.
|
||||||
- Add cache-relevant variables to `turbo.json` `globalEnv`.
|
- Add cache-relevant variables to `turbo.json` `globalEnv`.
|
||||||
|
|
||||||
## Code and dependency rules
|
## Code and dependency rules
|
||||||
@@ -50,13 +60,28 @@ bun db:up # start Convex and dashboard
|
|||||||
bun dev:next # host Next + deploy/watch local Convex functions
|
bun dev:next # host Next + deploy/watch local Convex functions
|
||||||
bun db:down # stop and preserve Convex data
|
bun db:down # stop and preserve Convex data
|
||||||
bun db:down:wipe # remove Convex volume and generated admin key
|
bun db:down:wipe # remove Convex volume and generated admin key
|
||||||
|
bun db:sync-env # re-push Infisical dev values into the local Convex env
|
||||||
|
bun dev:kill # stop dev servers that survived a Ctrl-C
|
||||||
```
|
```
|
||||||
|
|
||||||
Local URLs: app `http://localhost:3000`, Convex `http://localhost:3210`,
|
Local URLs: app `http://localhost:3000`, Convex `http://localhost:3250`,
|
||||||
dashboard `http://localhost:6791`.
|
dashboard `http://localhost:6795`. The Compose project and containers are named
|
||||||
|
`convexmonorepo-base-local*` so this stack can run beside the Payload template's.
|
||||||
Use `INFISICAL_ENV=staging bun dev:next` for staging services. Normal `db:up`
|
Use `INFISICAL_ENV=staging bun dev:next` for staging services. Normal `db:up`
|
||||||
never connects to staging and does not start Postgres.
|
never connects to staging and does not start Postgres.
|
||||||
|
|
||||||
|
## Analytics and bundle size
|
||||||
|
|
||||||
|
- Plausible (`NEXT_PUBLIC_PLAUSIBLE_SRC`, the per-site script URL) loads through
|
||||||
|
a same-origin proxy under `/pa`. PostHog (`NEXT_PUBLIC_POSTHOG_KEY`,
|
||||||
|
`NEXT_PUBLIC_POSTHOG_HOST`) loads after the page is idle through `/ingest`.
|
||||||
|
Both stay off when their env is unset.
|
||||||
|
- Session replay comes from PostHog. Sentry carries errors and traces only.
|
||||||
|
- `packages/ui/src/index.tsx` holds re-exports only (`cn` and `ccn` live in
|
||||||
|
`utils.ts`). With `optimizePackageImports` in `next.config.js`, each page
|
||||||
|
loads only the UI modules it imports. Declaring anything in the index breaks
|
||||||
|
that.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
Use `bun typecheck`, never a production build for routine validation. The full
|
Use `bun typecheck`, never a production build for routine validation. The full
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ shared UI/config packages, Vitest, and Docker deployment.
|
|||||||
|
|
||||||
## Local setup
|
## Local setup
|
||||||
|
|
||||||
Requirements: Bun 1.3.10, Docker or Podman, Node 22, and the Infisical CLI.
|
Requirements: Bun 1.3.14, Docker or Podman, Node 22, the Infisical CLI, `jq`, and
|
||||||
|
`flock`.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun install --frozen-lockfile
|
bun install --frozen-lockfile
|
||||||
@@ -22,8 +23,8 @@ files. Select staging with `INFISICAL_ENV=staging bun dev:next`.
|
|||||||
Local services:
|
Local services:
|
||||||
|
|
||||||
- Next.js: `http://localhost:3000`
|
- Next.js: `http://localhost:3000`
|
||||||
- Convex API: `http://localhost:3210`
|
- Convex API: `http://localhost:3250`
|
||||||
- Convex dashboard: `http://localhost:6791`
|
- Convex dashboard: `http://localhost:6795`
|
||||||
|
|
||||||
Next and Expo run on the host. Convex uses a self-hosted data volume and does
|
Next and Expo run on the host. Convex uses a self-hosted data volume and does
|
||||||
not use Postgres by default. The commented `POSTGRES_URL` in Compose is an
|
not use Postgres by default. The commented `POSTGRES_URL` in Compose is an
|
||||||
@@ -46,7 +47,8 @@ Convex URL with the development host's LAN address when testing Expo on-device.
|
|||||||
|
|
||||||
- Local `dev` and `staging`: Infisical.
|
- Local `dev` and `staging`: Infisical.
|
||||||
- Generated local state: `.local/<environment>.generated.env`.
|
- Generated local state: `.local/<environment>.generated.env`.
|
||||||
- CI/CD: Gitea `DOTENV_PROD`, materialized only as a temporary runner file.
|
- CI/CD: Infisical `staging`, read by the read-only `convexmonorepo-base-ci`
|
||||||
|
machine identity and materialized only as a temporary runner file.
|
||||||
- Docker compilation: explicit Compose build args; `.env*` stays outside the
|
- Docker compilation: explicit Compose build args; `.env*` stays outside the
|
||||||
image context.
|
image context.
|
||||||
|
|
||||||
@@ -81,5 +83,11 @@ Shared dependency versions belong in root catalogs. Edit the root catalog, run
|
|||||||
Production Compose retains the self-hosted Convex backend/dashboard and has no
|
Production Compose retains the self-hosted Convex backend/dashboard and has no
|
||||||
Postgres service by default. A commented `POSTGRES_URL` remains only as an
|
Postgres service by default. A commented `POSTGRES_URL` remains only as an
|
||||||
optional Convex-on-Postgres example for cloned projects. Gitea runs the quality
|
optional Convex-on-Postgres example for cloned projects. Gitea runs the quality
|
||||||
gate first, builds the Next image from a temporary Gitea-secret env file, then
|
gate first, then builds the Next image and pushes SHA and `latest` tags. Each
|
||||||
pushes SHA and `latest` tags. CI never installs or invokes Infisical.
|
job reads Infisical `staging` through `scripts/ci-export-env.mjs` as the
|
||||||
|
`convexmonorepo-base-ci` machine identity (Gitea secrets `INFISICAL_CLIENT_ID`
|
||||||
|
and `INFISICAL_CLIENT_SECRET`).
|
||||||
|
|
||||||
|
The template itself is not deployed. A cloned app that is adds a `convex-deploy`
|
||||||
|
job and the VPS deploy step from an app that already has them (StPeteIT,
|
||||||
|
LashAddict, bill-tracker), plus its own Infisical project and machine identity.
|
||||||
+9
-5
@@ -1,16 +1,18 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM docker.io/oven/bun:1.3.10-alpine AS base
|
FROM docker.io/oven/bun:1.3.14-alpine AS base
|
||||||
|
|
||||||
# Builder stage
|
# Builder stage
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
RUN apk add --no-cache libc6-compat
|
RUN apk add --no-cache curl libc6-compat
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ARG SENTRY_AUTH_TOKEN
|
ARG SENTRY_AUTH_TOKEN
|
||||||
ARG SENTRY_DISABLE_AUTO_UPLOAD=false
|
ARG SENTRY_DISABLE_AUTO_UPLOAD=false
|
||||||
ARG NEXT_PUBLIC_SITE_URL
|
ARG NEXT_PUBLIC_SITE_URL
|
||||||
ARG NEXT_PUBLIC_CONVEX_URL
|
ARG NEXT_PUBLIC_CONVEX_URL
|
||||||
ARG NEXT_PUBLIC_PLAUSIBLE_URL
|
ARG NEXT_PUBLIC_PLAUSIBLE_SRC
|
||||||
|
ARG NEXT_PUBLIC_POSTHOG_KEY
|
||||||
|
ARG NEXT_PUBLIC_POSTHOG_HOST
|
||||||
ARG NEXT_PUBLIC_SENTRY_DSN
|
ARG NEXT_PUBLIC_SENTRY_DSN
|
||||||
ARG NEXT_PUBLIC_SENTRY_URL
|
ARG NEXT_PUBLIC_SENTRY_URL
|
||||||
ARG NEXT_PUBLIC_SENTRY_ORG
|
ARG NEXT_PUBLIC_SENTRY_ORG
|
||||||
@@ -20,7 +22,9 @@ ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN
|
|||||||
ENV SENTRY_DISABLE_AUTO_UPLOAD=$SENTRY_DISABLE_AUTO_UPLOAD
|
ENV SENTRY_DISABLE_AUTO_UPLOAD=$SENTRY_DISABLE_AUTO_UPLOAD
|
||||||
ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL
|
ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL
|
||||||
ENV NEXT_PUBLIC_CONVEX_URL=$NEXT_PUBLIC_CONVEX_URL
|
ENV NEXT_PUBLIC_CONVEX_URL=$NEXT_PUBLIC_CONVEX_URL
|
||||||
ENV NEXT_PUBLIC_PLAUSIBLE_URL=$NEXT_PUBLIC_PLAUSIBLE_URL
|
ENV NEXT_PUBLIC_PLAUSIBLE_SRC=$NEXT_PUBLIC_PLAUSIBLE_SRC
|
||||||
|
ENV NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY
|
||||||
|
ENV NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST
|
||||||
ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN
|
ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN
|
||||||
ENV NEXT_PUBLIC_SENTRY_URL=$NEXT_PUBLIC_SENTRY_URL
|
ENV NEXT_PUBLIC_SENTRY_URL=$NEXT_PUBLIC_SENTRY_URL
|
||||||
ENV NEXT_PUBLIC_SENTRY_ORG=$NEXT_PUBLIC_SENTRY_ORG
|
ENV NEXT_PUBLIC_SENTRY_ORG=$NEXT_PUBLIC_SENTRY_ORG
|
||||||
@@ -36,7 +40,7 @@ RUN bun install --frozen-lockfile
|
|||||||
# Build with proper environment
|
# Build with proper environment
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
RUN cd apps/next && bun run build:docker
|
RUN sh scripts/build-next-bundle
|
||||||
|
|
||||||
# Runner stage
|
# Runner stage
|
||||||
FROM docker.io/library/node:22-alpine AS runner
|
FROM docker.io/library/node:22-alpine AS runner
|
||||||
|
|||||||
+10
-10
@@ -1,17 +1,17 @@
|
|||||||
name: convexmonorepo-local
|
name: convexmonorepo-base-local
|
||||||
|
|
||||||
services:
|
services:
|
||||||
convex-backend:
|
convex-backend:
|
||||||
image: ghcr.io/get-convex/convex-backend:${BACKEND_TAG:-latest}
|
image: ghcr.io/get-convex/convex-backend:${BACKEND_TAG:-latest}
|
||||||
container_name: convexmonorepo-local-convex
|
container_name: convexmonorepo-base-local-convex
|
||||||
ports:
|
ports:
|
||||||
- '${BACKEND_PORT:-3210}:3210'
|
- '${BACKEND_PORT:-3250}:3210'
|
||||||
- '${SITE_PROXY_PORT:-3211}:3211'
|
- '${SITE_PROXY_PORT:-3251}:3211'
|
||||||
environment:
|
environment:
|
||||||
- INSTANCE_NAME=${LOCAL_INSTANCE_NAME:-convexmonorepo_local}
|
- INSTANCE_NAME=${LOCAL_INSTANCE_NAME:-convexmonorepo_base_local}
|
||||||
- INSTANCE_SECRET=${LOCAL_INSTANCE_SECRET:-0000000000000000000000000000000000000000000000000000000000000000}
|
- INSTANCE_SECRET=${LOCAL_INSTANCE_SECRET:-0000000000000000000000000000000000000000000000000000000000000000}
|
||||||
- CONVEX_CLOUD_ORIGIN=http://localhost:${BACKEND_PORT:-3210}
|
- CONVEX_CLOUD_ORIGIN=http://localhost:${BACKEND_PORT:-3250}
|
||||||
- CONVEX_SITE_ORIGIN=http://localhost:${SITE_PROXY_PORT:-3211}
|
- CONVEX_SITE_ORIGIN=http://localhost:${SITE_PROXY_PORT:-3251}
|
||||||
- DISABLE_BEACON=true
|
- DISABLE_BEACON=true
|
||||||
- REDACT_LOGS_TO_CLIENT=false
|
- REDACT_LOGS_TO_CLIENT=false
|
||||||
- DO_NOT_REQUIRE_SSL=true
|
- DO_NOT_REQUIRE_SSL=true
|
||||||
@@ -31,10 +31,10 @@ services:
|
|||||||
|
|
||||||
convex-dashboard:
|
convex-dashboard:
|
||||||
image: ghcr.io/get-convex/convex-dashboard:${DASHBOARD_TAG:-latest}
|
image: ghcr.io/get-convex/convex-dashboard:${DASHBOARD_TAG:-latest}
|
||||||
container_name: convexmonorepo-local-convex-dashboard
|
container_name: convexmonorepo-base-local-convex-dashboard
|
||||||
ports: ['${DASHBOARD_PORT:-6791}:6791']
|
ports: ['${DASHBOARD_PORT:-6795}:6791']
|
||||||
environment:
|
environment:
|
||||||
- NEXT_PUBLIC_DEPLOYMENT_URL=http://localhost:${BACKEND_PORT:-3210}
|
- NEXT_PUBLIC_DEPLOYMENT_URL=http://localhost:${BACKEND_PORT:-3250}
|
||||||
depends_on:
|
depends_on:
|
||||||
convex-backend:
|
convex-backend:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
+6
-2
@@ -12,7 +12,9 @@ services:
|
|||||||
SENTRY_DISABLE_AUTO_UPLOAD: ${SENTRY_DISABLE_AUTO_UPLOAD:-false}
|
SENTRY_DISABLE_AUTO_UPLOAD: ${SENTRY_DISABLE_AUTO_UPLOAD:-false}
|
||||||
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL}
|
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL}
|
||||||
NEXT_PUBLIC_CONVEX_URL: ${NEXT_PUBLIC_CONVEX_URL}
|
NEXT_PUBLIC_CONVEX_URL: ${NEXT_PUBLIC_CONVEX_URL}
|
||||||
NEXT_PUBLIC_PLAUSIBLE_URL: ${NEXT_PUBLIC_PLAUSIBLE_URL}
|
NEXT_PUBLIC_PLAUSIBLE_SRC: ${NEXT_PUBLIC_PLAUSIBLE_SRC:-}
|
||||||
|
NEXT_PUBLIC_POSTHOG_KEY: ${NEXT_PUBLIC_POSTHOG_KEY:-}
|
||||||
|
NEXT_PUBLIC_POSTHOG_HOST: ${NEXT_PUBLIC_POSTHOG_HOST:-}
|
||||||
NEXT_PUBLIC_SENTRY_DSN: ${NEXT_PUBLIC_SENTRY_DSN}
|
NEXT_PUBLIC_SENTRY_DSN: ${NEXT_PUBLIC_SENTRY_DSN}
|
||||||
NEXT_PUBLIC_SENTRY_URL: ${NEXT_PUBLIC_SENTRY_URL}
|
NEXT_PUBLIC_SENTRY_URL: ${NEXT_PUBLIC_SENTRY_URL}
|
||||||
NEXT_PUBLIC_SENTRY_ORG: ${NEXT_PUBLIC_SENTRY_ORG}
|
NEXT_PUBLIC_SENTRY_ORG: ${NEXT_PUBLIC_SENTRY_ORG}
|
||||||
@@ -25,7 +27,9 @@ services:
|
|||||||
- SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN}
|
- SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN}
|
||||||
- NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL:-http://localhost:${NEXT_PORT:-3000}}
|
- NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL:-http://localhost:${NEXT_PORT:-3000}}
|
||||||
- NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL:-http://${BACKEND_CONTAINER_NAME:-convex-backend}:${BACKEND_PORT:-3210}}
|
- NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL:-http://${BACKEND_CONTAINER_NAME:-convex-backend}:${BACKEND_PORT:-3210}}
|
||||||
- NEXT_PUBLIC_PLAUSIBLE_URL=${NEXT_PUBLIC_PLAUSIBLE_URL:-https://plausible.gbrown.org}
|
- NEXT_PUBLIC_PLAUSIBLE_SRC=${NEXT_PUBLIC_PLAUSIBLE_SRC:-}
|
||||||
|
- NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY:-}
|
||||||
|
- NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST:-}
|
||||||
- NEXT_PUBLIC_SENTRY_DSN=${NEXT_PUBLIC_SENTRY_DSN}
|
- NEXT_PUBLIC_SENTRY_DSN=${NEXT_PUBLIC_SENTRY_DSN}
|
||||||
- NEXT_PUBLIC_SENTRY_URL=${NEXT_PUBLIC_SENTRY_URL}
|
- NEXT_PUBLIC_SENTRY_URL=${NEXT_PUBLIC_SENTRY_URL}
|
||||||
- NEXT_PUBLIC_SENTRY_ORG=${NEXT_PUBLIC_SENTRY_ORG:-sentry}
|
- NEXT_PUBLIC_SENTRY_ORG=${NEXT_PUBLIC_SENTRY_ORG:-sentry}
|
||||||
|
|||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd -- "$(dirname -- "$0")/.." && pwd)"
|
||||||
|
|
||||||
|
if [ -n "${SENTRY_AUTH_TOKEN:-}" ] &&
|
||||||
|
[ -n "${NEXT_PUBLIC_SENTRY_URL:-}" ] &&
|
||||||
|
! curl \
|
||||||
|
--connect-timeout 3 \
|
||||||
|
--max-time 10 \
|
||||||
|
--silent \
|
||||||
|
--show-error \
|
||||||
|
--output /dev/null \
|
||||||
|
"${NEXT_PUBLIC_SENTRY_URL%/}/api/0/"; then
|
||||||
|
echo "Sentry is unreachable; continuing without source-map upload." >&2
|
||||||
|
SENTRY_AUTH_TOKEN=""
|
||||||
|
SKIP_ENV_VALIDATION=true
|
||||||
|
NEXT_PUBLIC_SENTRY_URL="http://127.0.0.1:9"
|
||||||
|
export NEXT_PUBLIC_SENTRY_URL SENTRY_AUTH_TOKEN SKIP_ENV_VALIDATION
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$ROOT_DIR/apps/next"
|
||||||
|
exec bun run build:docker
|
||||||
Executable
+62
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// ci-export-env.mjs [environment]
|
||||||
|
//
|
||||||
|
// CI's counterpart to scripts/export-env: prints an Infisical environment
|
||||||
|
// (default `staging`) as dotenv on stdout, in the same quoting export-env uses.
|
||||||
|
// It logs in as the `convexmonorepo-base-ci` machine identity through Universal Auth, using
|
||||||
|
// INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET from the job's Gitea secrets,
|
||||||
|
// and reads the project and host from .infisical.json. Plain Node, no CLI to
|
||||||
|
// install, so it runs in every job whether or not Bun is set up.
|
||||||
|
import { readFile } from 'node:fs/promises';
|
||||||
|
|
||||||
|
const environment = process.argv[2] ?? 'staging';
|
||||||
|
const { INFISICAL_CLIENT_ID: clientId, INFISICAL_CLIENT_SECRET: clientSecret } =
|
||||||
|
process.env;
|
||||||
|
|
||||||
|
const fail = (message) => {
|
||||||
|
console.error(`ci-export-env: ${message}`);
|
||||||
|
process.exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!clientId || !clientSecret)
|
||||||
|
fail('INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET must be set.');
|
||||||
|
|
||||||
|
const { workspaceId, domain } = JSON.parse(
|
||||||
|
await readFile(new URL('../.infisical.json', import.meta.url), 'utf8'),
|
||||||
|
);
|
||||||
|
|
||||||
|
const request = async (path, init) => {
|
||||||
|
const response = await fetch(`${domain}${path}`, init);
|
||||||
|
if (!response.ok)
|
||||||
|
fail(`${init?.method ?? 'GET'} ${path} returned ${response.status}.`);
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
const { accessToken } = await request('/v1/auth/universal-auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ clientId, clientSecret }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const query = new URLSearchParams({
|
||||||
|
workspaceId,
|
||||||
|
environment,
|
||||||
|
secretPath: '/',
|
||||||
|
expandSecretReferences: 'true',
|
||||||
|
include_imports: 'true',
|
||||||
|
});
|
||||||
|
const { secrets, imports = [] } = await request(`/v3/secrets/raw?${query}`, {
|
||||||
|
headers: { Authorization: `Bearer ${accessToken}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Imported secrets first so the environment's own values win.
|
||||||
|
const values = new Map();
|
||||||
|
for (const { secretKey, secretValue } of [
|
||||||
|
...imports.flatMap((folder) => folder.secrets),
|
||||||
|
...secrets,
|
||||||
|
])
|
||||||
|
values.set(secretKey, secretValue);
|
||||||
|
|
||||||
|
const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
|
||||||
|
for (const [key, value] of [...values].sort(([a], [b]) => a.localeCompare(b)))
|
||||||
|
process.stdout.write(`${key}=${quote(value)}\n`);
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Install on a runner, and survive a half-written cache entry.
|
||||||
|
#
|
||||||
|
# Runners have failed twice with "Fail extracting tarball" on a different
|
||||||
|
# package each time, on different machines, and passed on a re-run. The cache
|
||||||
|
# is shared between jobs on a host, so a job that dies mid-write leaves an
|
||||||
|
# entry the next job cannot read. Clearing it and installing again costs a
|
||||||
|
# couple of minutes; a human re-running the job costs however long it takes
|
||||||
|
# someone to notice.
|
||||||
|
set -euo pipefail
|
||||||
|
if bun install --frozen-lockfile; then exit 0; fi
|
||||||
|
echo "Install failed. Clearing the bun cache and trying once more." >&2
|
||||||
|
rm -rf "${BUN_INSTALL_CACHE_DIR:-$HOME/.bun/install/cache}"
|
||||||
|
bun install --frozen-lockfile
|
||||||
+8
-3
@@ -44,9 +44,9 @@ refresh_env
|
|||||||
info "Starting local Convex and dashboard"
|
info "Starting local Convex and dashboard"
|
||||||
dc up -d
|
dc up -d
|
||||||
|
|
||||||
info "Waiting for Convex at http://localhost:${BACKEND_PORT:-3210}"
|
info "Waiting for Convex at http://localhost:${BACKEND_PORT:-3250}"
|
||||||
for i in $(seq 1 60); do
|
for i in $(seq 1 60); do
|
||||||
curl -fs "http://localhost:${BACKEND_PORT:-3210}/version" >/dev/null 2>&1 && break
|
curl -fs "http://localhost:${BACKEND_PORT:-3250}/version" >/dev/null 2>&1 && break
|
||||||
[ "$i" -lt 60 ] || die "Convex did not become ready."
|
[ "$i" -lt 60 ] || die "Convex did not become ready."
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
@@ -62,6 +62,11 @@ fi
|
|||||||
info "Deploying Convex schema and functions"
|
info "Deploying Convex schema and functions"
|
||||||
(cd "$ROOT_DIR/packages/backend" && bun run setup)
|
(cd "$ROOT_DIR/packages/backend" && bun run setup)
|
||||||
|
|
||||||
|
# Env sync runs after the deploy: changing deployment env re-analyzes the
|
||||||
|
# deployed modules, which fails on a fresh volume before the first deploy.
|
||||||
|
info "Synchronizing the Convex function runtime environment"
|
||||||
|
sh "$ROOT_DIR/scripts/with-env" dev -- bash "$ROOT_DIR/scripts/sync-local-convex-env"
|
||||||
|
|
||||||
convex_env_names="$(
|
convex_env_names="$(
|
||||||
sh "$ROOT_DIR/scripts/with-env" dev -- bash -c \
|
sh "$ROOT_DIR/scripts/with-env" dev -- bash -c \
|
||||||
'cd packages/backend && bunx convex env list' 2>/dev/null \
|
'cd packages/backend && bunx convex env list' 2>/dev/null \
|
||||||
@@ -82,4 +87,4 @@ if ! printf '%s\n' "$convex_env_names" | grep -qx 'JWT_PRIVATE_KEY' \
|
|||||||
'
|
'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '\nLocal stack ready:\n App: http://localhost:3000\n Convex: http://localhost:%s\n Dashboard: http://localhost:%s\n' "${BACKEND_PORT:-3210}" "${DASHBOARD_PORT:-6791}"
|
printf '\nLocal stack ready:\n App: http://localhost:3000\n Convex: http://localhost:%s\n Dashboard: http://localhost:%s\n' "${BACKEND_PORT:-3250}" "${DASHBOARD_PORT:-6795}"
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Kill any lingering host-level dev servers (Next.js, Expo Metro, Convex CLI
|
||||||
|
# watchers, turbo orchestrators) that survived a Ctrl-C. Does not touch the
|
||||||
|
# dockerized Convex/Postgres stack — use `bun db:down` for that.
|
||||||
|
#
|
||||||
|
# Usage: scripts/dev-kill (wired as `bun dev:kill`)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
info() { printf '\033[36m▶ %s\033[0m\n' "$*"; }
|
||||||
|
ok() { printf '\033[32m✓ %s\033[0m\n' "$*"; }
|
||||||
|
|
||||||
|
PORTS=(3000 3001 8081 19000 19001 19002)
|
||||||
|
PATTERNS=('next-server' 'next dev' 'expo start' 'turbo run dev' 'convex dev')
|
||||||
|
|
||||||
|
killed=false
|
||||||
|
|
||||||
|
for port in "${PORTS[@]}"; do
|
||||||
|
pids="$(lsof -ti "tcp:$port" -sTCP:LISTEN 2>/dev/null || true)"
|
||||||
|
[ -z "$pids" ] && continue
|
||||||
|
for pid in $pids; do
|
||||||
|
cmd="$(ps -p "$pid" -o comm= 2>/dev/null || true)"
|
||||||
|
info "Killing $cmd (pid $pid) on port $port"
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
killed=true
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
for pattern in "${PATTERNS[@]}"; do
|
||||||
|
pids="$(pgrep -f "$pattern" 2>/dev/null || true)"
|
||||||
|
[ -z "$pids" ] && continue
|
||||||
|
for pid in $pids; do
|
||||||
|
info "Killing '$pattern' (pid $pid)"
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
killed=true
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$killed" = true ]; then
|
||||||
|
sleep 1
|
||||||
|
ok "Dev servers stopped."
|
||||||
|
else
|
||||||
|
ok "No dev servers were running."
|
||||||
|
fi
|
||||||
+4
-4
@@ -15,8 +15,8 @@ container_running() {
|
|||||||
"$RUNTIME" ps --format '{{.Names}}' | grep -qx "$1"
|
"$RUNTIME" ps --format '{{.Names}}' | grep -qx "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
if container_running convexmonorepo-local-convex \
|
if container_running convexmonorepo-base-local-convex \
|
||||||
&& container_running convexmonorepo-local-convex-dashboard; then
|
&& container_running convexmonorepo-base-local-convex-dashboard; then
|
||||||
SHOULD_STOP_STACK=false
|
SHOULD_STOP_STACK=false
|
||||||
else
|
else
|
||||||
SHOULD_STOP_STACK=true
|
SHOULD_STOP_STACK=true
|
||||||
@@ -25,8 +25,8 @@ fi
|
|||||||
cleanup() {
|
cleanup() {
|
||||||
if [ "$SHOULD_STOP_STACK" = true ]; then
|
if [ "$SHOULD_STOP_STACK" = true ]; then
|
||||||
"$RUNTIME" rm -f \
|
"$RUNTIME" rm -f \
|
||||||
convexmonorepo-local-convex-dashboard \
|
convexmonorepo-base-local-convex-dashboard \
|
||||||
convexmonorepo-local-convex \
|
convexmonorepo-base-local-convex \
|
||||||
>/dev/null 2>&1 || true
|
>/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ fi
|
|||||||
[ -f "$ROOT_DIR/.infisical.json" ] || { echo "export-env: run 'infisical init' in this repository." >&2; exit 1; }
|
[ -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; }
|
command -v infisical >/dev/null 2>&1 || { echo "export-env: Infisical CLI is required." >&2; exit 1; }
|
||||||
|
|
||||||
|
if [ -f "$ROOT_DIR/scripts/infisical-profile" ]; then
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
. "$ROOT_DIR/scripts/infisical-profile"
|
||||||
|
ensure_infisical_profile "$ROOT_DIR/scripts/flags/infisical.env"
|
||||||
|
fi
|
||||||
|
|
||||||
(cd "$ROOT_DIR" && infisical export --env="$ENVIRONMENT" --format=dotenv --silent) || {
|
(cd "$ROOT_DIR" && infisical export --env="$ENVIRONMENT" --format=dotenv --silent) || {
|
||||||
echo "export-env: failed to export '$ENVIRONMENT'; check login and project access." >&2
|
echo "export-env: failed to export '$ENVIRONMENT'; check login and project access." >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Executable
+117
@@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
#
|
||||||
|
# ensure_infisical_profile <profile_env_file>
|
||||||
|
#
|
||||||
|
# Infisical's CLI keeps a single active login (~/.infisical/infisical-config.json)
|
||||||
|
# shared across every project on the machine. If you juggle multiple Infisical
|
||||||
|
# accounts (e.g. a client org vs. a personal org), running with-env in one repo
|
||||||
|
# can silently pull secrets while logged in as the wrong user.
|
||||||
|
#
|
||||||
|
# This switches the active `loggedInUserEmail` to the email configured in
|
||||||
|
# scripts/flags/infisical.env (INFISICAL_EMAIL=...) before infisical runs,
|
||||||
|
# provided that email is already logged in locally (`infisical login`).
|
||||||
|
# It is a no-op if the flag file is absent, only one user is logged in, or the
|
||||||
|
# expected user isn't logged in yet.
|
||||||
|
#
|
||||||
|
# POSIX sh, no bashisms: matches the rest of scripts/ which must stay
|
||||||
|
# busybox-compatible.
|
||||||
|
|
||||||
|
warn_infisical_profile() {
|
||||||
|
echo "Warning: infisical-profile: $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_infisical_profile() {
|
||||||
|
profile_env_file="${1:-}"
|
||||||
|
|
||||||
|
if [ -z "$profile_env_file" ] || [ ! -f "$profile_env_file" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -a
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
if ! . "$profile_env_file"; then
|
||||||
|
set +a
|
||||||
|
warn_infisical_profile "could not read $profile_env_file. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
set +a
|
||||||
|
|
||||||
|
expected_email="${INFISICAL_EMAIL:-}"
|
||||||
|
if [ -z "$expected_email" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
config="${INFISICAL_CONFIG_PATH:-$HOME/.infisical/infisical-config.json}"
|
||||||
|
lock="${INFISICAL_LOCK_PATH:-$HOME/.infisical/.switch.lock}"
|
||||||
|
|
||||||
|
if [ ! -f "$config" ]; then
|
||||||
|
warn_infisical_profile "Infisical config not found at $config. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
warn_infisical_profile "jq is required to select Infisical profile. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v flock >/dev/null 2>&1; then
|
||||||
|
warn_infisical_profile "flock is required to safely select Infisical profile. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! mkdir -p "$(dirname "$lock")"; then
|
||||||
|
warn_infisical_profile "could not create Infisical lock directory. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
(
|
||||||
|
if ! flock 9; then
|
||||||
|
warn_infisical_profile "could not lock Infisical profile config. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
user_count="$(jq -r '(.loggedInUsers // []) | length' "$config" 2>/dev/null)" || {
|
||||||
|
warn_infisical_profile "could not parse Infisical config. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$user_count" -lt 2 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_email="$(jq -r '.loggedInUserEmail // ""' "$config" 2>/dev/null)" || {
|
||||||
|
warn_infisical_profile "could not read current Infisical profile. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$current_email" = "$expected_email" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
domain="$(
|
||||||
|
jq -er --arg email "$expected_email" \
|
||||||
|
'.loggedInUsers[] | select(.email == $email) | .domain' \
|
||||||
|
"$config" 2>/dev/null
|
||||||
|
)" || {
|
||||||
|
warn_infisical_profile "Infisical profile $expected_email is not logged in locally. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp="$(mktemp)" || {
|
||||||
|
warn_infisical_profile "could not create temp file for Infisical profile switch. Continuing with current Infisical profile."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if jq --arg email "$expected_email" --arg domain "$domain" \
|
||||||
|
'.loggedInUserEmail = $email | .LoggedInUserDomain = $domain' \
|
||||||
|
"$config" > "$tmp"; then
|
||||||
|
chmod 600 "$tmp"
|
||||||
|
mv "$tmp" "$config"
|
||||||
|
else
|
||||||
|
rm -f "$tmp"
|
||||||
|
warn_infisical_profile "failed to update Infisical profile. Continuing with current Infisical profile."
|
||||||
|
fi
|
||||||
|
) 9>"$lock"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
VALUE_FILE=""
|
||||||
|
|
||||||
|
readonly RUNTIME_ENV_NAMES=(
|
||||||
|
AUTH_AUTHENTIK_ID
|
||||||
|
AUTH_AUTHENTIK_SECRET
|
||||||
|
AUTH_AUTHENTIK_ISSUER
|
||||||
|
USESEND_API_KEY
|
||||||
|
USESEND_URL
|
||||||
|
USESEND_FROM_EMAIL
|
||||||
|
)
|
||||||
|
readonly DEPLOYMENT_ENV_NAMES=(
|
||||||
|
CONVEX_SELF_HOSTED_URL
|
||||||
|
CONVEX_SELF_HOSTED_ADMIN_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
die() { printf 'sync-local-convex-env: %s\n' "$*" >&2; exit 1; }
|
||||||
|
cleanup() {
|
||||||
|
if [ -n "$VALUE_FILE" ] && [ -f "$VALUE_FILE" ]; then
|
||||||
|
rm -f "$VALUE_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM HUP
|
||||||
|
|
||||||
|
for name in "${DEPLOYMENT_ENV_NAMES[@]}" "${RUNTIME_ENV_NAMES[@]}"; do
|
||||||
|
[ -n "${!name:-}" ] || die "Missing required environment variable: $name"
|
||||||
|
done
|
||||||
|
|
||||||
|
VALUE_FILE="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-convex-env.value.XXXXXX")"
|
||||||
|
chmod 600 "$VALUE_FILE"
|
||||||
|
|
||||||
|
for name in "${RUNTIME_ENV_NAMES[@]}"; do
|
||||||
|
printf '%s' "${!name}" > "$VALUE_FILE"
|
||||||
|
(
|
||||||
|
cd "$ROOT_DIR/packages/backend"
|
||||||
|
env -u CONVEX_DEPLOY_KEY -u CONVEX_DEPLOYMENT \
|
||||||
|
CONVEX_SELF_HOSTED_URL="$CONVEX_SELF_HOSTED_URL" \
|
||||||
|
CONVEX_SELF_HOSTED_ADMIN_KEY="$CONVEX_SELF_HOSTED_ADMIN_KEY" \
|
||||||
|
bunx convex env set "$name" --from-file "$VALUE_FILE" >/dev/null
|
||||||
|
)
|
||||||
|
done
|
||||||
|
|
||||||
|
printf 'Local Convex runtime environment synchronized.\n'
|
||||||
+5
-2
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://v2-9-18.turborepo.dev/schema.json",
|
"$schema": "https://v2-11-6.turborepo.dev/schema.json",
|
||||||
"globalDependencies": [".infisical.json", ".local/*.env"],
|
"globalDependencies": [".infisical.json", ".local/*.env"],
|
||||||
"globalEnv": [
|
"globalEnv": [
|
||||||
"NODE_ENV",
|
"NODE_ENV",
|
||||||
@@ -8,7 +8,9 @@
|
|||||||
"SENTRY_DISABLE_AUTO_UPLOAD",
|
"SENTRY_DISABLE_AUTO_UPLOAD",
|
||||||
"NEXT_PUBLIC_SITE_URL",
|
"NEXT_PUBLIC_SITE_URL",
|
||||||
"NEXT_PUBLIC_CONVEX_URL",
|
"NEXT_PUBLIC_CONVEX_URL",
|
||||||
"NEXT_PUBLIC_PLAUSIBLE_URL",
|
"NEXT_PUBLIC_PLAUSIBLE_SRC",
|
||||||
|
"NEXT_PUBLIC_POSTHOG_KEY",
|
||||||
|
"NEXT_PUBLIC_POSTHOG_HOST",
|
||||||
"NEXT_PUBLIC_SENTRY_DSN",
|
"NEXT_PUBLIC_SENTRY_DSN",
|
||||||
"NEXT_PUBLIC_SENTRY_URL",
|
"NEXT_PUBLIC_SENTRY_URL",
|
||||||
"NEXT_PUBLIC_SENTRY_ORG",
|
"NEXT_PUBLIC_SENTRY_ORG",
|
||||||
@@ -47,6 +49,7 @@
|
|||||||
],
|
],
|
||||||
"globalPassThroughEnv": ["NODE_ENV"],
|
"globalPassThroughEnv": ["NODE_ENV"],
|
||||||
"ui": "tui",
|
"ui": "tui",
|
||||||
|
"agentGuidance": false,
|
||||||
"tasks": {
|
"tasks": {
|
||||||
"topo": {
|
"topo": {
|
||||||
"dependsOn": ["^topo"]
|
"dependsOn": ["^topo"]
|
||||||
|
|||||||
Reference in new issue
Block a user