Skip to content
Merged
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
61 changes: 61 additions & 0 deletions .github/docker/Dockerfile.web
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
FROM oven/bun:1.2.18 AS build

WORKDIR /app
COPY . .

ARG BASEURL=http://localhost:3000/api
ARG GOOGLE_CLIENT_ID=
ARG POSTHOG_KEY=
ARG POSTHOG_HOST=
ARG COMPASS_BUILD_REF=self-host

ENV COMPASS_BUILD_REF=${COMPASS_BUILD_REF}
ENV NODE_ENV=production
ENV WEB_PORT=9080

RUN printf '%s\n' \
'runtime:' \
' nodeEnv: production' \
' timezone: Etc/UTC' \
'web:' \
' url: http://localhost:9080' \
'backend:' \
" apiUrl: ${BASEURL}" \
' compassToken: unused-web-build' \
'mongo:' \
' uri: mongodb://localhost:27017/unused' \
'supertokens:' \
' uri: http://localhost:3567' \
' key: unused-web-build' \
'google:' \
" clientId: ${GOOGLE_CLIENT_ID}" \
> compass.yaml

RUN if [ -n "$POSTHOG_KEY" ] && [ -n "$POSTHOG_HOST" ]; then \
printf '%s\n' \
'posthog:' \
" key: ${POSTHOG_KEY}" \
" host: ${POSTHOG_HOST}" \
>> compass.yaml; \
fi

RUN bun install --frozen-lockfile
RUN cd packages/web && bun run build.ts

FROM oven/bun:1.2.18-slim AS runtime

WORKDIR /app

RUN addgroup --system --gid 1001 compass \
&& adduser --system --uid 1001 --ingroup compass --no-create-home compass

ENV WEB_PORT=9080
ENV WEB_ROOT=/app/build/web

COPY --from=build --chown=compass:compass /app/build/web ./build/web
COPY --from=build --chown=compass:compass /app/self-host/serve-web.ts ./self-host/serve-web.ts

USER compass

EXPOSE 9080
CMD ["bun", "self-host/serve-web.ts"]
16 changes: 15 additions & 1 deletion .github/workflows/_deploy-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
uses: docker/build-push-action@v7
with:
context: .
file: self-host/Dockerfile.web
file: .github/docker/Dockerfile.web
push: true
build-args: |
BASEURL=${{ vars.BACKEND_API_URL }}
Expand All @@ -67,6 +67,7 @@ jobs:
GCAL_NOTIFICATION_EXPIRATION_MIN: ${{ vars.GCAL_NOTIFICATION_EXPIRATION_MIN }}
GOOGLE_CLIENT_ID: ${{ vars.GOOGLE_CLIENT_ID }}
IMAGE_VERSION: ${{ steps.version.outputs.image_version }}
KIT_USER_TAG_ID: ${{ inputs.environment == 'production' && vars.KIT_USER_TAG_ID || '' }}
POSTHOG_KEY: ${{ (inputs.environment == 'production' || inputs.environment == 'staging-cloud') && vars.POSTHOG_KEY || '' }}
POSTHOG_HOST: ${{ (inputs.environment == 'production' || inputs.environment == 'staging-cloud') && vars.POSTHOG_HOST || '' }}
RELEASE_TAG: ${{ inputs.tag }}
Expand All @@ -77,6 +78,7 @@ jobs:
COMPASS_SYNC_TOKEN: ${{ secrets.COMPASS_SYNC_TOKEN }}
GCAL_NOTIFICATION_TOKEN: ${{ secrets.GCAL_NOTIFICATION_TOKEN }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
KIT_API_SECRET: ${{ inputs.environment == 'production' && secrets.KIT_API_SECRET || '' }}
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
MONGO_REPLICA_SET_KEY: ${{ secrets.MONGO_REPLICA_SET_KEY }}
MONGO_URI: ${{ secrets.MONGO_URI }}
Expand All @@ -86,6 +88,12 @@ jobs:
SUPERTOKENS_URI: ${{ secrets.SUPERTOKENS_URI }}
run: |
echo "Deploying Compass ${RELEASE_TAG} (image version: ${IMAGE_VERSION}) to ${{ inputs.environment }}"
if [ "${{ inputs.environment }}" = "production" ]; then
if [ -z "$KIT_API_SECRET" ] || [ -z "$KIT_USER_TAG_ID" ]; then
echo "Production deploy requires KIT_API_SECRET and KIT_USER_TAG_ID." >&2
exit 1
fi
fi
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/staging_key
chmod 600 ~/.ssh/staging_key
Expand Down Expand Up @@ -129,6 +137,12 @@ jobs:
" key: \"${POSTHOG_KEY}\"" \
" host: \"${POSTHOG_HOST}\""
fi
if [ -n "$KIT_API_SECRET" ] && [ -n "$KIT_USER_TAG_ID" ]; then
printf '%s\n' \
'email:' \
" kitApiSecret: \"${KIT_API_SECRET}\"" \
" kitUserTagId: \"${KIT_USER_TAG_ID}\""
fi
} | ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" \
"umask 077 && mkdir -p ~/compass && cat > ~/compass/compass.yaml && chmod 644 ~/compass/compass.yaml"
COMPOSE_GIT_REF="${COMPOSE_GIT_REF:-${RELEASE_TAG}}"
Expand Down
5 changes: 5 additions & 0 deletions docs/CI-CD/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,8 @@ The workflow deploys to the GitHub `production` environment through
`switchbacktech/compass-web:production-<version>`, then runs
`deploy-health-check.yml` with the `cloud` profile. Production is expected to use
external MongoDB and SuperTokens Cloud rather than self-hosted data services.

For cloud web image builds, `_deploy-environment.yml` uses
`.github/docker/Dockerfile.web` so frontend-only cloud config, such as PostHog,
is baked into the bundle without adding cloud-only settings to the self-host
Dockerfile.
10 changes: 0 additions & 10 deletions self-host/Dockerfile.web
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ COPY . .

ARG BASEURL=http://localhost:3000/api
ARG GOOGLE_CLIENT_ID=
ARG POSTHOG_KEY=
ARG POSTHOG_HOST=
ARG COMPASS_BUILD_REF=self-host

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

RUN if [ -n "$POSTHOG_KEY" ] && [ -n "$POSTHOG_HOST" ]; then \
printf '%s\n' \
'posthog:' \
" key: ${POSTHOG_KEY}" \
" host: ${POSTHOG_HOST}" \
>> compass.yaml; \
fi

RUN bun install --frozen-lockfile
RUN cd packages/web && bun run build.ts

Expand Down
53 changes: 53 additions & 0 deletions self-host/docker-compose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ describe("self-host docker compose", () => {
expect(dockerfile).not.toContain("--environment");
});

it("keeps PostHog out of the self-host web image", () => {
const dockerfile = readRepoFile("self-host/Dockerfile.web");

expect(dockerfile).not.toContain("COMPASS_WEB_BUILD_CONFIG_B64");
expect(dockerfile).not.toContain("POSTHOG_");
expect(dockerfile).not.toContain("posthog:");
});

it("mounts compass.yaml into the backend container", () => {
const compose = readFileSync(join(import.meta.dir, "compose.yaml"), {
encoding: "utf8",
Expand Down Expand Up @@ -168,6 +176,51 @@ describe("staging deploy workflow", () => {
);
});

it("builds cloud deploy web images from a GitHub-only Dockerfile with PostHog config", () => {
const workflow = readRepoFile(".github/workflows/_deploy-environment.yml");
const dockerfile = readRepoFile(".github/docker/Dockerfile.web");

expect(workflow).toContain("file: .github/docker/Dockerfile.web");
expect(workflow).toContain("POSTHOG_KEY=$");
expect(workflow).toContain("POSTHOG_HOST=$");
expect(workflow).not.toContain("COMPASS_WEB_BUILD_CONFIG_B64");
expect(workflow).not.toContain("base64");
expect(dockerfile).toContain("ARG POSTHOG_KEY=");
expect(dockerfile).toContain("ARG POSTHOG_HOST=");
expect(dockerfile).toContain("'posthog:'");
});

it("writes Kit email config only for production deploys", () => {
const workflow = readRepoFile(".github/workflows/_deploy-environment.yml");

expect(workflow).toContain(
"KIT_USER_TAG_ID: $".concat(
"{{ inputs.environment == 'production' && vars.KIT_USER_TAG_ID || '' }}",
),
);
expect(workflow).toContain(
"KIT_API_SECRET: $".concat(
"{{ inputs.environment == 'production' && secrets.KIT_API_SECRET || '' }}",
),
);
expect(workflow).toContain(
'if [ "$'.concat('{{ inputs.environment }}" = "production" ]; then'),
);
expect(workflow).toContain(
"Production deploy requires KIT_API_SECRET and KIT_USER_TAG_ID",
);
expect(workflow).toContain(
'if [ -n "$KIT_API_SECRET" ] && [ -n "$KIT_USER_TAG_ID" ]; then',
);
expect(workflow).toContain("'email:'");
expect(workflow).toContain(
'kitApiSecret: \\"$'.concat('{KIT_API_SECRET}\\"'),
);
expect(workflow).toContain(
'kitUserTagId: \\"$'.concat('{KIT_USER_TAG_ID}\\"'),
);
});

it("runs deploy health checks after each staging deploy", () => {
const workflow = readRepoFile(".github/workflows/deploy-staging.yml");

Expand Down