From 330b81d908b575393c171d560023b74ccba1a604 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 21 Apr 2026 08:49:48 +0530 Subject: [PATCH] chore: add update guide --- docs/deployment/update.mdx | 113 ++++++++++++++++++++++++ docs/docs.json | 1 + ui/src/components/layout/AppSidebar.tsx | 64 +++++++++++--- ui/src/hooks/useLatestReleaseVersion.ts | 107 ++++++++++++++++++++++ 4 files changed, 275 insertions(+), 10 deletions(-) create mode 100644 docs/deployment/update.mdx create mode 100644 ui/src/hooks/useLatestReleaseVersion.ts diff --git a/docs/deployment/update.mdx b/docs/deployment/update.mdx new file mode 100644 index 0000000..d4dc3a1 --- /dev/null +++ b/docs/deployment/update.mdx @@ -0,0 +1,113 @@ +--- +title: "Update" +description: "Update your self-hosted Dograh stack to a newer image version" +--- + +This guide covers updating a Dograh stack you've already deployed with [Docker](/deployment/docker) or a [custom domain](/deployment/custom-domain). You run commands from the same directory that contains your `docker-compose.yaml` (this is the `dograh/` directory if you used `setup_remote.sh`). + +## Find an image version + +Dograh publishes two images — `dograh-api` and `dograh-ui` — to both container registries: + +- **GitHub Container Registry** — [github.com/orgs/dograh-hq/packages](https://github.com/orgs/dograh-hq/packages) +- **Docker Hub** — [hub.docker.com/u/dograhai](https://hub.docker.com/u/dograhai) + +Each release is published under two kinds of tags: + +| Tag style | Example | When to use | +|-----------|---------|-------------| +| **Release tag** | `v0.8.2` | Stable, recommended for production | +| **Git commit SHA** | `a1b2c3d` | Bleeding edge — any commit merged to `main` | +| `latest` | `latest` | Tracks the most recent release tag | + + +Always update **`dograh-api`** and **`dograh-ui`** to the **same tag**. The two images are built from the same commit and the UI expects API responses in a matching shape — mixing versions will break the app. + + +## Option A: Update to the latest release + +If your `docker-compose.yaml` uses `:latest` (the default), just pull and restart: + + +```bash Local deployment +docker compose down +docker compose up --pull always +``` +```bash Remote deployment +cd dograh +sudo docker compose --profile remote down +sudo docker compose --profile remote up --pull always +``` + + +`--pull always` forces Docker to fetch the latest `:latest` from the registry instead of reusing your cached image. + +## Option B: Pin a specific tag + +To update (or roll back) to a specific release or commit, edit `docker-compose.yaml` and change the `image:` lines for both `api` and `ui` services to the same tag. + +Open the file: + +```bash +nano docker-compose.yaml +``` + +Find these two lines: + +```yaml + api: + image: ${REGISTRY:-dograhai}/dograh-api:latest + ui: + image: ${REGISTRY:-dograhai}/dograh-ui:latest +``` + +Replace `:latest` with your chosen tag on **both** services — for example: + +```yaml + api: + image: ${REGISTRY:-dograhai}/dograh-api:v0.8.2 + ui: + image: ${REGISTRY:-dograhai}/dograh-ui:v0.8.2 +``` + + +You can use either registry. Leave `REGISTRY` unset for Docker Hub (`dograhai`), or export `REGISTRY=ghcr.io/dograh-hq` to pull from GitHub Container Registry. + + +Then bring the stack down and back up: + + +```bash Local deployment +docker compose down +docker compose up --pull always +``` +```bash Remote deployment +cd dograh +sudo docker compose --profile remote down +sudo docker compose --profile remote up --pull always +``` + + +## Verify the update + +Check the running image tags: + +```bash +docker compose ps --format "table {{.Service}}\t{{.Image}}" +``` + +You should see the API and UI both running the tag you pinned. + +Hit the health endpoint to confirm the API is responding: + +```bash +curl http://localhost:8000/api/v1/health +``` + +## Roll back + +If something breaks, roll back by pinning the previous tag using the same process in **Option B** and restarting. Your Postgres data volume persists across `down`/`up` cycles, so agents and call history are preserved. + + +Rolling back across a database migration is not always safe — if the newer release ran a schema migration, downgrading may leave the DB in a state the older API doesn't understand. If in doubt, [open an issue](https://github.com/dograh-hq/dograh/issues) before rolling back. + diff --git a/docs/docs.json b/docs/docs.json index 95fa29a..ddb4405 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -141,6 +141,7 @@ "deployment/introduction", "deployment/docker", "deployment/custom-domain", + "deployment/update", "deployment/web-widget", "deployment/heroku" ] diff --git a/ui/src/components/layout/AppSidebar.tsx b/ui/src/components/layout/AppSidebar.tsx index 62685e0..8e5870c 100644 --- a/ui/src/components/layout/AppSidebar.tsx +++ b/ui/src/components/layout/AppSidebar.tsx @@ -2,6 +2,7 @@ import type { Team } from "@stackframe/stack"; import { + ArrowUpCircle, AudioLines, Brain, ChevronLeft, @@ -54,6 +55,7 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { useAppConfig } from "@/context/AppConfigContext"; +import { useLatestReleaseVersion } from "@/hooks/useLatestReleaseVersion"; import type { LocalUser } from "@/lib/auth"; import { useAuth } from "@/lib/auth"; import { cn } from "@/lib/utils"; @@ -89,6 +91,12 @@ export function AppSidebar() { // Version info from app config context const versionInfo = config ? { ui: config.uiVersion, api: config.apiVersion } : null; + // Check for updates only on self-hosted (OSS) deployments — cloud is managed for the user. + const { latest: latestRelease, isBehind, isLatest } = useLatestReleaseVersion( + versionInfo?.ui, + { enabled: config?.deploymentMode === "oss" }, + ); + const isActive = (path: string) => { return pathname.startsWith(path); }; @@ -232,17 +240,53 @@ export function AppSidebar() {
{/* Logo - only show when expanded */} {effectiveState === "expanded" && ( - - Dograh - {versionInfo && ( - - v{versionInfo.ui} - +
+ + Dograh + {versionInfo && ( + + v{versionInfo.ui} + + )} + + {isBehind && latestRelease && ( + + + + + + Update + + + +

Latest: {latestRelease} — click to see the update guide

+
+
+
)} - + {isLatest && ( + + + + + Latest + + + +

You're running the latest release

+
+
+
+ )} +
)} {/* Toggle button - center it when collapsed */} l[i]) return false; + } + return false; +} + +export function useLatestReleaseVersion( + currentVersion: string | undefined, + { enabled }: Options, +): Result { + const [latest, setLatest] = useState(null); + + useEffect(() => { + if (!enabled || !currentVersion) return; + + try { + const raw = localStorage.getItem(CACHE_KEY); + if (raw) { + const parsed = JSON.parse(raw) as { tag: string; fetchedAt: number }; + if (Date.now() - parsed.fetchedAt < CACHE_TTL_MS) { + setLatest(parsed.tag); + return; + } + } + } catch { + // ignore malformed cache + } + + let cancelled = false; + fetch("https://api.github.com/repos/dograh-hq/dograh/releases/latest") + .then((res) => (res.ok ? res.json() : null)) + .then((data) => { + if (cancelled || !data?.tag_name) return; + const tag = data.tag_name as string; + try { + localStorage.setItem( + CACHE_KEY, + JSON.stringify({ tag, fetchedAt: Date.now() }), + ); + } catch { + // storage may be full or disabled + } + setLatest(tag); + }) + .catch(() => { + // silent — don't break the sidebar if GitHub is unreachable + }); + + return () => { + cancelled = true; + }; + }, [enabled, currentVersion]); + + const normalizedCurrent = currentVersion + ? currentVersion.startsWith("v") + ? currentVersion + : `v${currentVersion}` + : null; + + const currentParsed = normalizedCurrent ? parseSemver(normalizedCurrent) : null; + const latestParsed = latest ? parseSemver(latest) : null; + + const isBehind = !!( + normalizedCurrent && + latest && + isOlder(normalizedCurrent, latest) + ); + + const isLatest = !!( + currentParsed && + latestParsed && + currentParsed[0] === latestParsed[0] && + currentParsed[1] === latestParsed[1] && + currentParsed[2] === latestParsed[2] + ); + + return { latest, isBehind, isLatest }; +}