From ae72092ed78690d1199702f8933a0e498f795d17 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sun, 8 Mar 2026 11:00:06 -0700 Subject: [PATCH] =?UTF-8?q?C2(jobsync):=20finalize=20=E2=80=94=20rework=20?= =?UTF-8?q?cards=20into=20standalone=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delete mirror-jobsync and integrate-jobsync-ollama (no standalone value). Rework build-jobsync-container and deploy-jobsync into operational reference documentation. Add changelog fragment. Co-Authored-By: Claude Opus 4.6 --- docs/changelog.d/mikado-jobsync.feature.md | 1 + docs/how-to/how-to.md | 2 - .../how-to/jobsync/build-jobsync-container.md | 61 ++++++++----------- docs/how-to/jobsync/deploy-jobsync.md | 49 ++++++++------- .../jobsync/integrate-jobsync-ollama.md | 44 ------------- docs/how-to/jobsync/mirror-jobsync.md | 26 -------- 6 files changed, 54 insertions(+), 129 deletions(-) create mode 100644 docs/changelog.d/mikado-jobsync.feature.md delete mode 100644 docs/how-to/jobsync/integrate-jobsync-ollama.md delete mode 100644 docs/how-to/jobsync/mirror-jobsync.md diff --git a/docs/changelog.d/mikado-jobsync.feature.md b/docs/changelog.d/mikado-jobsync.feature.md new file mode 100644 index 0000000..bdd3eb7 --- /dev/null +++ b/docs/changelog.d/mikado-jobsync.feature.md @@ -0,0 +1 @@ +Deploy JobSync to ringtail k3s — nix-built container, Tailscale Ingress, Caddy route at `jobsync.ops.eblu.me`, Ollama integration for AI features. diff --git a/docs/how-to/how-to.md b/docs/how-to/how-to.md index 5f1bd4f..0ca60a6 100644 --- a/docs/how-to/how-to.md +++ b/docs/how-to/how-to.md @@ -92,8 +92,6 @@ tags: - [[deploy-jobsync]] - [[build-jobsync-container]] -- [[mirror-jobsync]] -- [[integrate-jobsync-ollama]] ## Forgejo Runner diff --git a/docs/how-to/jobsync/build-jobsync-container.md b/docs/how-to/jobsync/build-jobsync-container.md index 82f13e4..de75915 100644 --- a/docs/how-to/jobsync/build-jobsync-container.md +++ b/docs/how-to/jobsync/build-jobsync-container.md @@ -1,70 +1,61 @@ --- title: Build JobSync Container modified: 2026-03-08 -requires: - - mirror-jobsync tags: - how-to - jobsync + - nix --- # Build JobSync Container -Build a nix container image for JobSync using `dockerTools.buildLayeredImage`, following the ntfy pattern. +Build and release the JobSync nix container image. -## Context +```fish +mise run container-release jobsync 1.1.4 +``` -JobSync is a Next.js standalone app with Prisma (SQLite). The nix build needs to: +The derivation is at `containers/jobsync/default.nix`. It uses `buildNpmPackage` for the Next.js app and `dockerTools.buildLayeredImage` for the container. The entrypoint (`containers/jobsync/entrypoint.sh`) runs `prisma migrate deploy` then starts `node server.js`. -1. Fetch source from `forge.ops.eblu.me/mirrors/jobsync` (v1.1.4) -2. `buildNpmPackage` — install deps, run `prisma generate`, run `next build` -3. Package the standalone output with `nodejs` runtime into a layered image -4. Include an entrypoint that runs `prisma migrate deploy` before `node server.js` +## Upgrading JobSync -## Key Details +1. Update the forge mirror: `mise run mirror-sync jobsync` +2. Update `version` in `default.nix` to match the new upstream tag +3. Clear `hash` in `fetchgit` (set to `""`), build, grab the correct hash from the error +4. Clear `npmDepsHash` (set to `""`), build again, grab the correct hash +5. Check if `postPatch` still applies — the Google Fonts import may change between versions +6. `mise run container-release jobsync ` +7. Update `newTag` in `argocd/manifests/jobsync/kustomization.yaml` -- **Runtime dependency:** `nodejs_20` must be in the image (unlike Go apps that compile to static binaries) -- **Prisma native engine:** Use `pkgs.prisma-engines` from nixpkgs — do NOT let Prisma download engines at build time (nix sandbox blocks network) -- **`npmDepsHash`:** Computed on first build (set to empty, let it fail, grab the hash) -- **Standalone output:** Next.js `output: "standalone"` produces a self-contained `server.js` with minimal `node_modules` +## Nix + Prisma + Next.js Pitfalls -## Nix Container Pitfalls +### Prisma engine downloads blocked by sandbox -### Prisma engine downloads in nix sandbox +Prisma tries to download platform-specific engine binaries during `prisma generate`. The nix sandbox blocks network access at build time. -Prisma tries to download platform-specific engine binaries during `prisma generate`. The nix sandbox blocks network access, causing the build to fail. +**Fix:** Use `pkgs.prisma-engines` from nixpkgs and set env vars pointing at the nix store paths: `PRISMA_QUERY_ENGINE_LIBRARY`, `PRISMA_QUERY_ENGINE_BINARY`, `PRISMA_SCHEMA_ENGINE_BINARY`, `PRISMA_FMT_BINARY`. Set `PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1` to tolerate minor version mismatch. -**Fix:** Use `pkgs.prisma-engines` from nixpkgs and set environment variables: -- `PRISMA_QUERY_ENGINE_LIBRARY`, `PRISMA_QUERY_ENGINE_BINARY`, `PRISMA_SCHEMA_ENGINE_BINARY`, `PRISMA_FMT_BINARY` — point at nixpkgs engines -- `PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1` — tolerate minor version mismatch between nixpkgs engines and npm prisma package +### Google Fonts blocked by sandbox -### Google Fonts blocked by nix sandbox - -`next/font/google` (Inter font) fetches from `fonts.googleapis.com` during `next build`. Nix sandbox blocks this. +`next/font/google` fetches from `fonts.googleapis.com` during `next build`. **Fix:** Patch `src/app/layout.tsx` in `postPatch` to replace the Google font import with a no-op object. The app falls back to system sans-serif. -### Prisma devDependency pruning +### Missing FHS paths in nix containers -`buildNpmPackage` runs `npm prune --omit=dev` between `buildPhase` and `installPhase`. The `prisma` CLI and `@prisma/engines` are devDependencies, so they get removed. But they're needed at runtime for `prisma migrate deploy` in the entrypoint. +Nix containers lack `/usr/bin/env`, `/tmp`, etc. `npx`-downloaded packages use `#!/usr/bin/env node` shebangs. -**Fix:** Save prisma packages to a temp directory during `postBuild` (before prune), then copy them into the output during `installPhase`. +**Fix:** In `extraCommands`: `mkdir -p tmp data usr/bin` and `ln -s ${pkgs.coreutils}/bin/env usr/bin/env`. -### Entrypoint filesystem paths +### Runtime migrations via npx -Nix containers have no `/app` directory. The app lives at `/nix/store//app/` and the container's `WorkingDir` is set to that path. The entrypoint must not `cd /app` — it should rely on the `WorkingDir` set in the container config. +The nix sandbox blocks network at build time, but runtime has full network access. Use `npx -y prisma@ migrate deploy` in the entrypoint — npx downloads the prisma CLI on first run. ### Build on ringtail, not via Dagger -The Dagger `build-nix` pipeline runs in a container matching the host architecture. On macOS (arm64), this produces `linux-arm64` builds, which won't run on ringtail (x86_64). Build directly on ringtail or use the CI workflow. - -## Files - -- `containers/jobsync/default.nix` — nix derivation -- `containers/jobsync/entrypoint.sh` — startup script (migrations + server) +The Dagger `build-nix` pipeline runs in host architecture. On macOS (arm64), this produces arm64 images. Build on ringtail (x86_64) using the CI workflow or `mise run container-release`. ## Related -- [[mirror-jobsync]] - [[deploy-jobsync]] - [[build-container-image]] diff --git a/docs/how-to/jobsync/deploy-jobsync.md b/docs/how-to/jobsync/deploy-jobsync.md index c3596c9..6b72ad7 100644 --- a/docs/how-to/jobsync/deploy-jobsync.md +++ b/docs/how-to/jobsync/deploy-jobsync.md @@ -1,10 +1,6 @@ --- title: Deploy JobSync modified: 2026-03-08 -branch: mikado/jobsync -requires: - - build-jobsync-container - - integrate-jobsync-ollama tags: - how-to - jobsync @@ -12,21 +8,25 @@ tags: # Deploy JobSync -Deploy [JobSync](https://github.com/Gsync/jobsync) — a self-hosted job application tracker — to ringtail's k3s cluster via ArgoCD. +[JobSync](https://github.com/Gsync/jobsync) is a self-hosted job application tracker (Next.js + Prisma/SQLite) running on ringtail's k3s cluster via ArgoCD. -## Context +- **URL:** `https://jobsync.ops.eblu.me` +- **Auth:** Local accounts (email/password), no SSO +- **Storage:** 5Gi PVC at `/data` (SQLite DB + resume uploads) +- **AI:** Ollama at `ollama.ollama.svc.cluster.local:11434` -JobSync is a Next.js app with SQLite storage that provides job application tracking, resume management, and AI-powered resume review/job matching. It runs as a single container with persistent storage at `/data` (SQLite DB + uploaded files). +## Manifests -## What This Card Covers +All in `argocd/manifests/jobsync/`: -With the container built and Ollama integration configured, this card wires up the deployment: - -- ArgoCD Application targeting `ringtail.tail8d86e.ts.net:6443` -- k8s manifests: Deployment, Service, Tailscale Ingress, PVC, ExternalSecret -- PVC using k3s local-path for `/data` (SQLite + resume uploads) -- ExternalSecret for `ENCRYPTION_KEY` and `AUTH_SECRET` from 1Password -- Caddy route: `jobsync.ops.eblu.me` → Tailscale ingress +| File | Purpose | +|------|---------| +| `deployment.yaml` | Single-replica deployment | +| `service.yaml` | ClusterIP on port 3000 | +| `ingress-tailscale.yaml` | Tailscale Ingress (ProxyGroup) | +| `pvc.yaml` | 5Gi local-path for `/data` | +| `external-secret.yaml` | `auth_secret` + `encryption_key` from 1Password | +| `kustomization.yaml` | Image tag override | ## Environment Variables @@ -40,16 +40,21 @@ With the container built and Ollama integration configured, this card wires up t | `TZ` | Hardcoded | `America/Los_Angeles` | | `OLLAMA_BASE_URL` | Hardcoded | `http://ollama.ollama.svc.cluster.local:11434` | -## Deployment Notes +## Updating the Container -- **`service-versions.yaml`:** Add a `jobsync` entry before committing container changes — the `container-version-check` pre-commit hook rejects commits touching `containers//` without a matching entry. -- **Image tag format:** `container-build-and-release` produces tags like `v1.1.4--nix`, not bare `v1.1.4`. Set `newTag` in `kustomization.yaml` to the full tag from `mise run container-list`. -- **1Password item:** "JobSync" in blumeops vault, with `auth_secret` and `encryption_key` fields (already created). -- **Nix container FHS:** Nix containers lack `/usr/bin/env` — add `ln -s ${pkgs.coreutils}/bin/env usr/bin/env` in `extraCommands`. Also `mkdir -p tmp` for `/tmp`. -- **Runtime migrations:** Use `npx -y prisma@ migrate deploy` — nix sandbox blocks network at build time but runtime has full network access. +1. Build and push: `mise run container-release jobsync ` +2. Update `newTag` in `kustomization.yaml` to the full tag (e.g. `v1.1.4-e51ec83-nix`) +3. Sync: `argocd app sync jobsync` + +See [[build-jobsync-container]] for nix build details. + +## Notes + +- **1Password item:** "JobSync" in blumeops vault, fields `auth_secret` and `encryption_key` +- **Caddy route:** `jobsync.ops.eblu.me` → `https://jobsync.tail8d86e.ts.net` (in `ansible/roles/caddy/defaults/main.yml`) +- **`service-versions.yaml`:** Must have a `jobsync` entry or the pre-commit hook rejects container changes ## Related - [[build-jobsync-container]] -- [[integrate-jobsync-ollama]] - [[deploy-k8s-service]] diff --git a/docs/how-to/jobsync/integrate-jobsync-ollama.md b/docs/how-to/jobsync/integrate-jobsync-ollama.md deleted file mode 100644 index 65268a2..0000000 --- a/docs/how-to/jobsync/integrate-jobsync-ollama.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Integrate JobSync with Ollama -modified: 2026-03-08 -tags: - - how-to - - jobsync ---- - -# Integrate JobSync with Ollama - -Configure JobSync to use the existing Ollama deployment on ringtail for AI features (resume review, job matching). - -## Context - -Ollama already runs on ringtail's k3s cluster at `ollama.ollama.svc.cluster.local:11434` with several models available (qwen2.5:14b, deepseek-r1:14b, phi4:14b, gemma3:12b, qwen3.5:9b). - -JobSync supports Ollama natively via `OLLAMA_BASE_URL`. Since both services run on the same k3s cluster, this is a cluster-internal connection with no auth required. - -## Configuration - -Set in the JobSync deployment manifest: - -```yaml -env: - - name: OLLAMA_BASE_URL - value: "http://ollama.ollama.svc.cluster.local:11434" -``` - -## Verification - -After deployment: - -1. Open JobSync settings -2. Select Ollama as the AI provider -3. Verify model list populates from the Ollama instance -4. Test resume review with one of the available models - -## Model Recommendations - -For resume review and job matching, `qwen2.5:14b` or `gemma3:12b` are good choices — capable enough for structured text analysis without being overly slow on the RTX 4080. - -## Related - -- [[deploy-jobsync]] diff --git a/docs/how-to/jobsync/mirror-jobsync.md b/docs/how-to/jobsync/mirror-jobsync.md deleted file mode 100644 index 46a30cc..0000000 --- a/docs/how-to/jobsync/mirror-jobsync.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Mirror JobSync -modified: 2026-03-07 -tags: - - how-to - - jobsync ---- - -# Mirror JobSync - -Mirror the upstream [JobSync](https://github.com/Gsync/jobsync) repository to `forge.ops.eblu.me/mirrors/jobsync` for supply chain control. - -## Context - -JobSync is not in nixpkgs. Building a nix container requires fetching source from a controlled mirror on forge, following the same pattern as [[mirror-authentik-build-deps]] and the ntfy mirror. - -## Steps - -1. Create the mirror: `mise run mirror-create jobsync https://github.com/Gsync/jobsync.git` -2. Verify the mirror syncs: check `forge.ops.eblu.me/mirrors/jobsync` -3. Note the tag for v1.1.4 (current upstream release) - -## Related - -- [[build-jobsync-container]] -- [[manage-forgejo-mirrors]]