Skip to content

Commit 952b2ca

Browse files
tyler-daneclaudeUarmagan
authored
refactor(config): move version from compose.version to runtime.version (#1770)
* refactor(config): move version from compose.version to runtime.version Consolidates the Docker image version field into the existing runtime section, eliminating the standalone compose block. Updates the Zod schema, all example YAML files, the self-host installer (read + write paths), the staging deploy workflow, and docs. Also renames SSH_KEY to SSH_PRIVATE_KEY in the staging workflow for clarity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(config): reorder runtime block fields to version, nodeEnv, logLevel, timezone Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): omit replicaSetKey from generated config when secret is unset Allows staging-cloud to skip MONGO_REPLICA_SET_KEY without writing an empty field into compass.yaml. Uses ${VAR:+word} so staging-selfhosted still writes the key when the secret is present. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(config): clarify which keys are self-host only vs all installs Normalises the runtime table to use the Required column convention, marks mongo.username, mongo.password, and mongo.replicaSetKey as Self-host (not Yes), and moves mongo.uri to the top of its table since it is the only field required for all installs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): omit supertokens postgres block from generated config when secret is unset Same pattern as the replicaSetKey fix: uses \${VAR:+word} on each line so staging-cloud can omit SUPERTOKENS_POSTGRES_PASSWORD without writing empty fields into compass.yaml. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ci): make Google Calendar channel expiration configurable per environment Adds GCAL_NOTIFICATION_EXPIRATION_MIN as a workflow var that writes channelExpirationMin into the generated compass.yaml. Omitted when unset so environments without Google configured are unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): replace hardcoded mongo URI with MONGO_URI secret Removes the hardcoded mongodb://...staging_calendar connection string so each environment can supply its own URI, matching how the hosted staging environment is configured. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): replace hardcoded SuperTokens URI with SUPERTOKENS_URI secret Removes the docker-internal http://supertokens:3567 address so each environment can point at its own SuperTokens instance (self-hosted container or managed cloud). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(ci): split deploy logic into reusable workflow per environment Extracts deploy steps into _deploy-environment.yml (workflow_call) which takes tag and environment as inputs. deploy-staging.yml becomes a thin caller with two jobs — staging-cloud and staging-selfhosted — each resolving secrets from their own GitHub Environment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(infra): use Docker Compose profiles to make local services optional Tags mongo, supertokens, and supertokens-db with profiles: [selfhost] so cloud environments skip those containers entirely. install.sh always activates the selfhost profile. The deploy workflow passes COMPOSE_PROFILES from a per-environment GitHub variable so staging-selfhosted gets the full local stack and staging-cloud only runs web and backend. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): add COMPOSE_GIT_REF override for testing pre-release compose changes Defaults to RELEASE_TAG so production behaviour is unchanged. Set the GitHub environment variable to a branch name to test compose.yaml or the compass helper from that ref without cutting a new release. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(self-host): preserve empty COMPOSE_PROFILES from deploy workflow The installer's set_compose_env unconditionally set COMPOSE_PROFILES=selfhost, overwriting the empty string passed by the CI deploy workflow for cloud environments. This caused mongo/supertokens to start on staging-cloud. Use ${COMPOSE_PROFILES-selfhost} (no colon) so an explicitly empty string is preserved — allowing cloud deploys to omit selfhost services — while still defaulting to 'selfhost' when a human runs the installer directly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): tear down all profiled services before deploy update Docker Compose's --remove-orphans flag does not remove containers from inactive profiles, so switching from COMPOSE_PROFILES=selfhost to an empty set on a re-deploy left mongo/supertokens running on cloud environments. Add an explicit 'compose down' with COMPOSE_PROFILES=selfhost before the 'compass update' step so all previously-started services are removed before the new profile set is started. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(self-host): read helper version from runtime config (#1771) * fix(self-host): default helper to selfhost profile (#1772) * fix(codex): update hook syntax * docs: update cli and server guide * fix: resolve lint issues (#1774) * fix(self-host): preserve default compose profile --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Ugur Armagan <ugurarmagan93@gmail.com>
1 parent 14b174c commit 952b2ca

15 files changed

Lines changed: 198 additions & 260 deletions

File tree

.codex/config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[features]
2-
codex_hooks = true
2+
hooks = true
33
goals = true
44

55

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Deploy to environment
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
tag:
7+
description: 'Release tag being deployed, such as v1.2.3'
8+
required: true
9+
type: string
10+
environment:
11+
description: 'GitHub Environment to deploy to'
12+
required: true
13+
type: string
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
deploy:
20+
name: Deploy ${{ inputs.tag }} to ${{ inputs.environment }}
21+
runs-on: ubuntu-latest
22+
environment: ${{ inputs.environment }}
23+
24+
steps:
25+
- name: Deploy release to ${{ inputs.environment }}
26+
env:
27+
# Non-sensitive
28+
BACKEND_API_URL: ${{ vars.BACKEND_API_URL }}
29+
COMPOSE_GIT_REF: ${{ vars.COMPOSE_GIT_REF }}
30+
COMPOSE_PROFILES: ${{ vars.COMPOSE_PROFILES }}
31+
FRONTEND_URL: ${{ vars.FRONTEND_URL }}
32+
GCAL_NOTIFICATION_EXPIRATION_MIN: ${{ vars.GCAL_NOTIFICATION_EXPIRATION_MIN }}
33+
GOOGLE_CLIENT_ID: ${{ vars.GOOGLE_CLIENT_ID }}
34+
RELEASE_TAG: ${{ inputs.tag }}
35+
SSH_HOST: ${{ vars.SSH_HOST }}
36+
SSH_USER: ${{ vars.SSH_USER }}
37+
# Sensitive
38+
COMPASS_SYNC_TOKEN: ${{ secrets.COMPASS_SYNC_TOKEN }}
39+
GCAL_NOTIFICATION_TOKEN: ${{ secrets.GCAL_NOTIFICATION_TOKEN }}
40+
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
41+
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
42+
MONGO_REPLICA_SET_KEY: ${{ secrets.MONGO_REPLICA_SET_KEY }}
43+
MONGO_URI: ${{ secrets.MONGO_URI }}
44+
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
45+
SUPERTOKENS_KEY: ${{ secrets.SUPERTOKENS_KEY }}
46+
SUPERTOKENS_POSTGRES_PASSWORD: ${{ secrets.SUPERTOKENS_POSTGRES_PASSWORD }}
47+
SUPERTOKENS_URI: ${{ secrets.SUPERTOKENS_URI }}
48+
run: |
49+
# Strip 'v' prefix for Docker image tags (v0.5.18 -> 0.5.18)
50+
IMAGE_VERSION="${RELEASE_TAG#v}"
51+
echo "Deploying Compass ${RELEASE_TAG} (image version: ${IMAGE_VERSION}) to ${{ inputs.environment }}"
52+
mkdir -p ~/.ssh
53+
echo "$SSH_PRIVATE_KEY" > ~/.ssh/staging_key
54+
chmod 600 ~/.ssh/staging_key
55+
ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts
56+
printf '%s\n' \
57+
'runtime:' \
58+
" version: \"${IMAGE_VERSION}\"" \
59+
' nodeEnv: production' \
60+
' timezone: Etc/UTC' \
61+
'web:' \
62+
' port: 9080' \
63+
" url: \"${FRONTEND_URL}\"" \
64+
'backend:' \
65+
' port: 3000' \
66+
" apiUrl: \"${BACKEND_API_URL}\"" \
67+
' originsAllowed:' \
68+
" - \"${FRONTEND_URL}\"" \
69+
" compassToken: \"${COMPASS_SYNC_TOKEN}\"" \
70+
'mongo:' \
71+
' username: compass' \
72+
" password: \"${MONGO_PASSWORD}\"" \
73+
${MONGO_REPLICA_SET_KEY:+" replicaSetKey: \"${MONGO_REPLICA_SET_KEY}\""} \
74+
" uri: \"${MONGO_URI}\"" \
75+
'supertokens:' \
76+
" uri: \"${SUPERTOKENS_URI}\"" \
77+
" key: \"${SUPERTOKENS_KEY}\"" \
78+
${SUPERTOKENS_POSTGRES_PASSWORD:+' postgres:'} \
79+
${SUPERTOKENS_POSTGRES_PASSWORD:+' user: supertokens'} \
80+
${SUPERTOKENS_POSTGRES_PASSWORD:+" password: \"${SUPERTOKENS_POSTGRES_PASSWORD}\""} \
81+
${SUPERTOKENS_POSTGRES_PASSWORD:+' database: supertokens'} \
82+
'google:' \
83+
" clientId: \"${GOOGLE_CLIENT_ID}\"" \
84+
" clientSecret: \"${GOOGLE_CLIENT_SECRET}\"" \
85+
" notificationToken: \"${GCAL_NOTIFICATION_TOKEN}\"" \
86+
${GCAL_NOTIFICATION_EXPIRATION_MIN:+" channelExpirationMin: ${GCAL_NOTIFICATION_EXPIRATION_MIN}"} \
87+
| ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" \
88+
"umask 077 && mkdir -p ~/compass && cat > ~/compass/compass.yaml && chmod 644 ~/compass/compass.yaml"
89+
COMPOSE_GIT_REF="${COMPOSE_GIT_REF:-${RELEASE_TAG}}"
90+
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "curl -fsSL https://raw.githubusercontent.com/SwitchbackTech/compass/${COMPOSE_GIT_REF}/self-host/compose.yaml -o ~/compass/compose.yaml"
91+
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "curl -fsSL https://raw.githubusercontent.com/SwitchbackTech/compass/${COMPOSE_GIT_REF}/self-host/compass -o ~/compass/compass && chmod +x ~/compass/compass"
92+
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "cd ~/compass && COMPOSE_PROFILES=selfhost docker compose --project-name compass -f compose.yaml down 2>/dev/null || true"
93+
if [ -n "$COMPOSE_PROFILES" ]; then
94+
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "cd ~/compass && COMPOSE_PROFILES='${COMPOSE_PROFILES}' ./compass update"
95+
else
96+
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "cd ~/compass && ./compass update"
97+
fi

.github/workflows/deploy-staging.yml

Lines changed: 14 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,71 +18,18 @@ permissions:
1818
contents: read
1919

2020
jobs:
21-
deploy:
22-
name: Deploy release to staging
23-
runs-on: ubuntu-latest
24-
environment: Staging
21+
deploy-cloud:
22+
name: Deploy to staging-cloud
23+
uses: ./.github/workflows/_deploy-environment.yml
24+
with:
25+
tag: ${{ inputs.tag }}
26+
environment: staging-cloud
27+
secrets: inherit
2528

26-
steps:
27-
- name: Deploy release to staging
28-
env:
29-
# Non-sensitive
30-
BACKEND_API_URL: ${{ vars.BACKEND_API_URL }}
31-
FRONTEND_URL: ${{ vars.FRONTEND_URL }}
32-
GOOGLE_CLIENT_ID: ${{ vars.GOOGLE_CLIENT_ID }}
33-
RELEASE_TAG: ${{ inputs.tag }}
34-
SSH_USER: ${{ vars.SSH_USER }}
35-
SSH_HOST: ${{ vars.SSH_HOST }}
36-
# Sensitive
37-
COMPASS_SYNC_TOKEN: ${{ secrets.COMPASS_SYNC_TOKEN }}
38-
GCAL_NOTIFICATION_TOKEN: ${{ secrets.GCAL_NOTIFICATION_TOKEN }}
39-
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
40-
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
41-
MONGO_REPLICA_SET_KEY: ${{ secrets.MONGO_REPLICA_SET_KEY }}
42-
SSH_KEY: ${{ secrets.SSH_KEY }}
43-
SUPERTOKENS_KEY: ${{ secrets.SUPERTOKENS_KEY }}
44-
SUPERTOKENS_POSTGRES_PASSWORD: ${{ secrets.SUPERTOKENS_POSTGRES_PASSWORD }}
45-
run: |
46-
# Strip 'v' prefix for Docker image tags (v0.5.18 -> 0.5.18)
47-
IMAGE_VERSION="${RELEASE_TAG#v}"
48-
echo "Deploying Compass ${RELEASE_TAG} (image version: ${IMAGE_VERSION}) to staging"
49-
mkdir -p ~/.ssh
50-
echo "$SSH_KEY" > ~/.ssh/staging_key
51-
chmod 600 ~/.ssh/staging_key
52-
ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts
53-
printf '%s\n' \
54-
'compose:' \
55-
" version: \"${IMAGE_VERSION}\"" \
56-
'runtime:' \
57-
' nodeEnv: production' \
58-
' timezone: Etc/UTC' \
59-
'web:' \
60-
' port: 9080' \
61-
" url: \"${FRONTEND_URL}\"" \
62-
'backend:' \
63-
' port: 3000' \
64-
" apiUrl: \"${BACKEND_API_URL}\"" \
65-
' originsAllowed:' \
66-
" - \"${FRONTEND_URL}\"" \
67-
" compassToken: \"${COMPASS_SYNC_TOKEN}\"" \
68-
'mongo:' \
69-
' username: compass' \
70-
" password: \"${MONGO_PASSWORD}\"" \
71-
" replicaSetKey: \"${MONGO_REPLICA_SET_KEY}\"" \
72-
" uri: \"mongodb://compass:${MONGO_PASSWORD}@mongo:27017/staging_calendar?authSource=admin&replicaSet=rs0\"" \
73-
'supertokens:' \
74-
' uri: http://supertokens:3567' \
75-
" key: \"${SUPERTOKENS_KEY}\"" \
76-
' postgres:' \
77-
' user: supertokens' \
78-
" password: \"${SUPERTOKENS_POSTGRES_PASSWORD}\"" \
79-
' database: supertokens' \
80-
'google:' \
81-
" clientId: \"${GOOGLE_CLIENT_ID}\"" \
82-
" clientSecret: \"${GOOGLE_CLIENT_SECRET}\"" \
83-
" notificationToken: \"${GCAL_NOTIFICATION_TOKEN}\"" \
84-
| ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" \
85-
"umask 077 && mkdir -p ~/compass && cat > ~/compass/compass.yaml && chmod 644 ~/compass/compass.yaml"
86-
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "curl -fsSL https://raw.githubusercontent.com/SwitchbackTech/compass/${RELEASE_TAG}/self-host/compose.yaml -o ~/compass/compose.yaml"
87-
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "curl -fsSL https://raw.githubusercontent.com/SwitchbackTech/compass/${RELEASE_TAG}/self-host/compass -o ~/compass/compass && chmod +x ~/compass/compass"
88-
ssh -i ~/.ssh/staging_key "$SSH_USER@$SSH_HOST" "cd ~/compass && ./compass update"
29+
deploy-selfhosted:
30+
name: Deploy to staging-selfhosted
31+
uses: ./.github/workflows/_deploy-environment.yml
32+
with:
33+
tag: ${{ inputs.tag }}
34+
environment: staging-selfhosted
35+
secrets: inherit

compass.example.yaml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@
55
# - Do not commit this file; it contains secrets.
66
# - For detailed explanations, see: https://docs.compasscalendar.com/docs/config
77

8-
compose:
9-
version: latest
10-
118
runtime:
9+
version: latest
1210
nodeEnv: development
13-
timezone: Etc/UTC
1411
logLevel: debug
12+
timezone: Etc/UTC
1513

1614
web:
1715
port: 9080

docs/CI-CD/cli-and-maintenance-commands.md

Lines changed: 0 additions & 151 deletions
This file was deleted.

docs/Config/README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Compass YAML Configuration
1+
# Configuration
22

33
Compass uses `compass.yaml` for self-hosting and local development. The file is visible, diffable, and contains secrets, so keep it out of git and back it up with the Docker volumes.
44

@@ -7,14 +7,14 @@ Examples:
77
- local development: `packages/backend/compass.example.yaml`
88
- self-hosting: `self-host/compass.example.yaml`
99

10-
## Top-Level Sections
10+
## Runtime
1111

12-
| key | Default | Description |
12+
| key | Required | Description |
1313
|---|---|---|
14-
| `compose.version` | `latest` | Docker image tag used by the self-host compose stack. Pin this for reproducible installs. |
15-
| `runtime.nodeEnv` | `production` | Runtime mode. Self-hosted installs should use `production`; local development uses `development`. |
16-
| `runtime.timezone` | `Etc/UTC` | Backend timezone. Only `Etc/UTC` and `UTC` are accepted. |
17-
| `runtime.logLevel` | `info` | Winston log level. |
14+
| `runtime.version` | Self-host | Docker image tag used by the self-host compose stack. Defaults to `latest`. Pin this for reproducible installs. |
15+
| `runtime.nodeEnv` | Yes | Runtime mode. Use `production` for self-hosted and staging; `development` for local dev. |
16+
| `runtime.timezone` | Yes | Backend timezone. Only `Etc/UTC` and `UTC` are accepted. |
17+
| `runtime.logLevel` | No | Winston log level. Defaults to `info`. |
1818

1919
## Web
2020

@@ -36,10 +36,10 @@ Examples:
3636

3737
| key | Required | Description |
3838
|---|---|---|
39-
| `mongo.username` | Self-host | MongoDB root username created on first container startup. Must match `mongo.uri`. |
39+
| `mongo.uri` | Yes | Backend MongoDB connection string. For self-hosted installs, must include `authSource=admin` and `replicaSet=rs0`. |
40+
| `mongo.username` | Self-host | MongoDB root username created on first container startup. Must match the credentials in `mongo.uri`. |
4041
| `mongo.password` | Self-host | MongoDB root password. Changing it after first startup requires a MongoDB user migration. |
41-
| `mongo.replicaSetKey` | Self-host | MongoDB replica set key for the single-node `rs0` replica set. |
42-
| `mongo.uri` | Yes | Backend MongoDB connection string. Self-hosted installs must include `authSource=admin` and `replicaSet=rs0`. |
42+
| `mongo.replicaSetKey` | Self-host | Shared secret used for internal authentication between replica set members. |
4343

4444
## SuperTokens
4545

0 commit comments

Comments
 (0)