Skip to content

Commit 7ca3d49

Browse files
authored
fix(ci): add production kit email config (#1870)
* feat: add posthog config to production deploys * fix(ci): add production kit email config
1 parent 9486881 commit 7ca3d49

5 files changed

Lines changed: 134 additions & 11 deletions

File tree

.github/docker/Dockerfile.web

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
FROM oven/bun:1.2.18 AS build
2+
3+
WORKDIR /app
4+
COPY . .
5+
6+
ARG BASEURL=http://localhost:3000/api
7+
ARG GOOGLE_CLIENT_ID=
8+
ARG POSTHOG_KEY=
9+
ARG POSTHOG_HOST=
10+
ARG COMPASS_BUILD_REF=self-host
11+
12+
ENV COMPASS_BUILD_REF=${COMPASS_BUILD_REF}
13+
ENV NODE_ENV=production
14+
ENV WEB_PORT=9080
15+
16+
RUN printf '%s\n' \
17+
'runtime:' \
18+
' nodeEnv: production' \
19+
' timezone: Etc/UTC' \
20+
'web:' \
21+
' url: http://localhost:9080' \
22+
'backend:' \
23+
" apiUrl: ${BASEURL}" \
24+
' compassToken: unused-web-build' \
25+
'mongo:' \
26+
' uri: mongodb://localhost:27017/unused' \
27+
'supertokens:' \
28+
' uri: http://localhost:3567' \
29+
' key: unused-web-build' \
30+
'google:' \
31+
" clientId: ${GOOGLE_CLIENT_ID}" \
32+
> compass.yaml
33+
34+
RUN if [ -n "$POSTHOG_KEY" ] && [ -n "$POSTHOG_HOST" ]; then \
35+
printf '%s\n' \
36+
'posthog:' \
37+
" key: ${POSTHOG_KEY}" \
38+
" host: ${POSTHOG_HOST}" \
39+
>> compass.yaml; \
40+
fi
41+
42+
RUN bun install --frozen-lockfile
43+
RUN cd packages/web && bun run build.ts
44+
45+
FROM oven/bun:1.2.18-slim AS runtime
46+
47+
WORKDIR /app
48+
49+
RUN addgroup --system --gid 1001 compass \
50+
&& adduser --system --uid 1001 --ingroup compass --no-create-home compass
51+
52+
ENV WEB_PORT=9080
53+
ENV WEB_ROOT=/app/build/web
54+
55+
COPY --from=build --chown=compass:compass /app/build/web ./build/web
56+
COPY --from=build --chown=compass:compass /app/self-host/serve-web.ts ./self-host/serve-web.ts
57+
58+
USER compass
59+
60+
EXPOSE 9080
61+
CMD ["bun", "self-host/serve-web.ts"]

.github/workflows/_deploy-environment.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
uses: docker/build-push-action@v7
4848
with:
4949
context: .
50-
file: self-host/Dockerfile.web
50+
file: .github/docker/Dockerfile.web
5151
push: true
5252
build-args: |
5353
BASEURL=${{ vars.BACKEND_API_URL }}
@@ -67,6 +67,7 @@ jobs:
6767
GCAL_NOTIFICATION_EXPIRATION_MIN: ${{ vars.GCAL_NOTIFICATION_EXPIRATION_MIN }}
6868
GOOGLE_CLIENT_ID: ${{ vars.GOOGLE_CLIENT_ID }}
6969
IMAGE_VERSION: ${{ steps.version.outputs.image_version }}
70+
KIT_USER_TAG_ID: ${{ inputs.environment == 'production' && vars.KIT_USER_TAG_ID || '' }}
7071
POSTHOG_KEY: ${{ (inputs.environment == 'production' || inputs.environment == 'staging-cloud') && vars.POSTHOG_KEY || '' }}
7172
POSTHOG_HOST: ${{ (inputs.environment == 'production' || inputs.environment == 'staging-cloud') && vars.POSTHOG_HOST || '' }}
7273
RELEASE_TAG: ${{ inputs.tag }}
@@ -77,6 +78,7 @@ jobs:
7778
COMPASS_SYNC_TOKEN: ${{ secrets.COMPASS_SYNC_TOKEN }}
7879
GCAL_NOTIFICATION_TOKEN: ${{ secrets.GCAL_NOTIFICATION_TOKEN }}
7980
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
81+
KIT_API_SECRET: ${{ inputs.environment == 'production' && secrets.KIT_API_SECRET || '' }}
8082
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
8183
MONGO_REPLICA_SET_KEY: ${{ secrets.MONGO_REPLICA_SET_KEY }}
8284
MONGO_URI: ${{ secrets.MONGO_URI }}
@@ -86,6 +88,12 @@ jobs:
8688
SUPERTOKENS_URI: ${{ secrets.SUPERTOKENS_URI }}
8789
run: |
8890
echo "Deploying Compass ${RELEASE_TAG} (image version: ${IMAGE_VERSION}) to ${{ inputs.environment }}"
91+
if [ "${{ inputs.environment }}" = "production" ]; then
92+
if [ -z "$KIT_API_SECRET" ] || [ -z "$KIT_USER_TAG_ID" ]; then
93+
echo "Production deploy requires KIT_API_SECRET and KIT_USER_TAG_ID." >&2
94+
exit 1
95+
fi
96+
fi
8997
mkdir -p ~/.ssh
9098
echo "$SSH_PRIVATE_KEY" > ~/.ssh/staging_key
9199
chmod 600 ~/.ssh/staging_key
@@ -129,6 +137,12 @@ jobs:
129137
" key: \"${POSTHOG_KEY}\"" \
130138
" host: \"${POSTHOG_HOST}\""
131139
fi
140+
if [ -n "$KIT_API_SECRET" ] && [ -n "$KIT_USER_TAG_ID" ]; then
141+
printf '%s\n' \
142+
'email:' \
143+
" kitApiSecret: \"${KIT_API_SECRET}\"" \
144+
" kitUserTagId: \"${KIT_USER_TAG_ID}\""
145+
fi
132146
} | ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" \
133147
"umask 077 && mkdir -p ~/compass && cat > ~/compass/compass.yaml && chmod 644 ~/compass/compass.yaml"
134148
COMPOSE_GIT_REF="${COMPOSE_GIT_REF:-${RELEASE_TAG}}"

docs/CI-CD/workflows.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,8 @@ The workflow deploys to the GitHub `production` environment through
144144
`switchbacktech/compass-web:production-<version>`, then runs
145145
`deploy-health-check.yml` with the `cloud` profile. Production is expected to use
146146
external MongoDB and SuperTokens Cloud rather than self-hosted data services.
147+
148+
For cloud web image builds, `_deploy-environment.yml` uses
149+
`.github/docker/Dockerfile.web` so frontend-only cloud config, such as PostHog,
150+
is baked into the bundle without adding cloud-only settings to the self-host
151+
Dockerfile.

self-host/Dockerfile.web

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ COPY . .
55

66
ARG BASEURL=http://localhost:3000/api
77
ARG GOOGLE_CLIENT_ID=
8-
ARG POSTHOG_KEY=
9-
ARG POSTHOG_HOST=
108
ARG COMPASS_BUILD_REF=self-host
119

1210
ENV COMPASS_BUILD_REF=${COMPASS_BUILD_REF}
@@ -31,14 +29,6 @@ RUN printf '%s\n' \
3129
" clientId: ${GOOGLE_CLIENT_ID}" \
3230
> compass.yaml
3331

34-
RUN if [ -n "$POSTHOG_KEY" ] && [ -n "$POSTHOG_HOST" ]; then \
35-
printf '%s\n' \
36-
'posthog:' \
37-
" key: ${POSTHOG_KEY}" \
38-
" host: ${POSTHOG_HOST}" \
39-
>> compass.yaml; \
40-
fi
41-
4232
RUN bun install --frozen-lockfile
4333
RUN cd packages/web && bun run build.ts
4434

self-host/docker-compose.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ describe("self-host docker compose", () => {
8585
expect(dockerfile).not.toContain("--environment");
8686
});
8787

88+
it("keeps PostHog out of the self-host web image", () => {
89+
const dockerfile = readRepoFile("self-host/Dockerfile.web");
90+
91+
expect(dockerfile).not.toContain("COMPASS_WEB_BUILD_CONFIG_B64");
92+
expect(dockerfile).not.toContain("POSTHOG_");
93+
expect(dockerfile).not.toContain("posthog:");
94+
});
95+
8896
it("mounts compass.yaml into the backend container", () => {
8997
const compose = readFileSync(join(import.meta.dir, "compose.yaml"), {
9098
encoding: "utf8",
@@ -168,6 +176,51 @@ describe("staging deploy workflow", () => {
168176
);
169177
});
170178

179+
it("builds cloud deploy web images from a GitHub-only Dockerfile with PostHog config", () => {
180+
const workflow = readRepoFile(".github/workflows/_deploy-environment.yml");
181+
const dockerfile = readRepoFile(".github/docker/Dockerfile.web");
182+
183+
expect(workflow).toContain("file: .github/docker/Dockerfile.web");
184+
expect(workflow).toContain("POSTHOG_KEY=$");
185+
expect(workflow).toContain("POSTHOG_HOST=$");
186+
expect(workflow).not.toContain("COMPASS_WEB_BUILD_CONFIG_B64");
187+
expect(workflow).not.toContain("base64");
188+
expect(dockerfile).toContain("ARG POSTHOG_KEY=");
189+
expect(dockerfile).toContain("ARG POSTHOG_HOST=");
190+
expect(dockerfile).toContain("'posthog:'");
191+
});
192+
193+
it("writes Kit email config only for production deploys", () => {
194+
const workflow = readRepoFile(".github/workflows/_deploy-environment.yml");
195+
196+
expect(workflow).toContain(
197+
"KIT_USER_TAG_ID: $".concat(
198+
"{{ inputs.environment == 'production' && vars.KIT_USER_TAG_ID || '' }}",
199+
),
200+
);
201+
expect(workflow).toContain(
202+
"KIT_API_SECRET: $".concat(
203+
"{{ inputs.environment == 'production' && secrets.KIT_API_SECRET || '' }}",
204+
),
205+
);
206+
expect(workflow).toContain(
207+
'if [ "$'.concat('{{ inputs.environment }}" = "production" ]; then'),
208+
);
209+
expect(workflow).toContain(
210+
"Production deploy requires KIT_API_SECRET and KIT_USER_TAG_ID",
211+
);
212+
expect(workflow).toContain(
213+
'if [ -n "$KIT_API_SECRET" ] && [ -n "$KIT_USER_TAG_ID" ]; then',
214+
);
215+
expect(workflow).toContain("'email:'");
216+
expect(workflow).toContain(
217+
'kitApiSecret: \\"$'.concat('{KIT_API_SECRET}\\"'),
218+
);
219+
expect(workflow).toContain(
220+
'kitUserTagId: \\"$'.concat('{KIT_USER_TAG_ID}\\"'),
221+
);
222+
});
223+
171224
it("runs deploy health checks after each staging deploy", () => {
172225
const workflow = readRepoFile(".github/workflows/deploy-staging.yml");
173226

0 commit comments

Comments
 (0)