diff --git a/cli/src/__tests__/messages.test.ts b/cli/src/__tests__/messages.test.ts index 01c957aa..ad7ef1d3 100644 --- a/cli/src/__tests__/messages.test.ts +++ b/cli/src/__tests__/messages.test.ts @@ -81,7 +81,7 @@ describe("buildLocalConnectMessage", () => { expect(message).toContain("Start the server:") expect(message).toContain( - `cd ${localDefaults.targetDir} && docker compose up -d`, + `cd "${localDefaults.targetDir}" && docker compose up -d`, ) }) @@ -119,6 +119,15 @@ describe("buildLocalConnectMessage", () => { expect(message).toContain("deploy/local/README.md") }) + it("includes the image update command with the targetDir", () => { + const message = buildLocalConnectMessage(localDefaults) + + expect(message).toContain("Update to the latest release:") + expect(message).toContain( + `cd "${localDefaults.targetDir}" && docker compose pull && docker compose up -d`, + ) + }) + it("includes the OAuth connect walkthrough", () => { const message = buildLocalConnectMessage(localDefaults) @@ -201,7 +210,7 @@ describe("buildRemoteConnectMessage", () => { expect(message).toContain("Start the server:") expect(message).toContain( - `cd ${remoteDefaults.targetDir} && docker compose up -d`, + `cd "${remoteDefaults.targetDir}" && docker compose up -d`, ) }) @@ -256,6 +265,15 @@ describe("buildRemoteConnectMessage", () => { ) }) + it("includes the image update command with the targetDir", () => { + const message = buildRemoteConnectMessage(remoteDefaults) + + expect(message).toContain("Update to the latest release:") + expect(message).toContain( + `cd "${remoteDefaults.targetDir}" && docker compose pull && docker compose up -d`, + ) + }) + it("includes the remote docs link", () => { const message = buildRemoteConnectMessage(remoteDefaults) diff --git a/cli/src/messages.ts b/cli/src/messages.ts index 0c5c32ae..d6c2147e 100644 --- a/cli/src/messages.ts +++ b/cli/src/messages.ts @@ -33,8 +33,10 @@ const sectionRule = (label: string): string => `── ${label} ${"─".repeat(Math.max(0, RULE_WIDTH - label.length - 4))}`, ) +// targetDir is quoted: these lines are meant to be copy-pasted into a +// shell, and an unquoted path breaks on spaces or special characters. const composeUpCommand = (targetDir: string): string => - `cd ${targetDir} && docker compose up -d` + `cd "${targetDir}" && docker compose up -d` const startServerLine = (targetDir: string): string => `Start the server:\n ${composeUpCommand(targetDir)}` @@ -105,6 +107,12 @@ const smokeTest = (healthUrl: string): string => `Smoke test: curl ${healthUrl}` +// Compose does not pull new images on `up` — without this hint users stay +// on the image from init day forever while believing they track releases. +const updateGuidance = (targetDir: string): string => + `Update to the latest release: + cd "${targetDir}" && docker compose pull && docker compose up -d` + /** * Local-mode "Connect" message. port comes from the .env on disk: a kept file * may override the default, so the message must describe the server that will @@ -165,6 +173,8 @@ Optional settings (timezone, memory folder, port, logging) are commented out in ${targetDir}/.env — uncomment, set a value, then apply with "docker compose up -d" (restart alone does not re-read .env). +${updateGuidance(targetDir)} + Full docs: https://github.com/aliasunder/vault-cortex/blob/main/deploy/local/README.md ${bottomRule()}` @@ -244,6 +254,8 @@ behavior) are commented out in ${targetDir}/.env — uncomment, set a value, then apply with "docker compose up -d" (restart alone does not re-read .env). +${updateGuidance(targetDir)} + For HTTPS options (API Gateway, Caddy, Cloudflare Tunnel), see: https://github.com/aliasunder/vault-cortex/blob/main/deploy/remote/README.md#https-access diff --git a/deploy/local/README.md b/deploy/local/README.md index df3ba0a5..f28e1916 100644 --- a/deploy/local/README.md +++ b/deploy/local/README.md @@ -116,6 +116,20 @@ curl http://localhost:8000/healthz curl http://localhost:8000/.well-known/oauth-protected-resource ``` +## Updating + +Compose does **not** pull new images on `up` — once `:latest` is on your +machine, you stay on that exact image until you pull explicitly: + +```bash +# Pull the latest image and recreate the container: +docker compose pull && docker compose up -d +``` + +Your search index persists in its Docker volume across updates; unchanged +notes are not re-embedded (content-hash cache), so restarts after an update +are fast. + ## Stop ```bash diff --git a/deploy/remote/README.md b/deploy/remote/README.md index 4260ae2f..75175ccc 100644 --- a/deploy/remote/README.md +++ b/deploy/remote/README.md @@ -207,6 +207,30 @@ docker logs -f vault-cortex docker compose ps ``` +## Updating + +Compose does **not** pull new images on `up` — once `:remote` is on the +server, you stay on that exact image until you pull explicitly: + +```bash +# Pull the latest image and recreate the container: +docker compose pull && docker compose up -d +``` + +Named volumes persist across updates: your synced vault, search index, and +Obsidian login state all carry over — no re-sync, no device re-registration, +and unchanged notes are not re-embedded. + +Running with plain `docker run` instead of Compose? There's no in-place +update — pull, then recreate the container with the same `docker run` +command you started it with: + +```bash +docker pull ghcr.io/aliasunder/vault-cortex:remote +docker rm -f vault-cortex +# then re-run your original docker run command +``` + ## Restart The server runs startup tasks on every boot: rebuilds the search index, creates