Web: Move watcher release config from env vars to admin-only settings page - #74
Merged
Conversation
… page Replaces the WATCHER_LATEST_VERSION / WATCHER_MIN_SUPPORTED_VERSION / WATCHER_RELEASE_CHANNEL / WATCHER_MANDATORY_UPDATE env vars with a singleton `watcher_release_config` row, edited via a new admin-only /settings/watcher-release page (PUT /api/v1/watcher-release, gated by requireAdmin). The update-check endpoint reads from the DB and falls back to latest_version: null when no row exists, preserving the existing "unset means skip self-update" contract for the watcher fleet. Both test harnesses now seed the singleton row directly; resetDb() re-seeds after each cycle because TRUNCATE CASCADE on "user" wipes the row via the updated_by FK regardless of ON DELETE SET NULL. Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
wasimxyz
marked this pull request as ready for review
May 17, 2026 17:21
…d layout Three follow-ups to the previous watcher release commit: * Replace the hand-rolled per-field validation in the PUT route with a Zod `strictObject` schema. Drops `normalizeOptionalString` and the bespoke `unknown_fields` detail in favor of zod issues, and unifies PUT semantics so all four fields take their defaults uniformly when omitted rather than silently preserving existing values for the two version columns. * Move the API surface from `/api/v1/watcher-release` to `/api/v1/settings/watcher-release` and the admin page from `/settings/watcher-release` to `/settings/watchers`. Reorder the settings sidebar to Access Tokens → Watchers → Members. Comment and doc references throughout (schema, env example, integration tests, shared testing helper, upgrade guide) updated to match. * UI polish: drop the duplicate "Settings" H1 from the settings layout (the sidebar already labels the section), rename the on-page heading to "Watcher Version", and wrap the entire form (heading, description, fields, footer) in a full-width Card. CardHeader, CardContent and the inner footer row are capped at max-w-2xl so the input rows stay at a readable measure while the card's top border and full-width footer separator span the page. Co-authored-by: Cursor <cursoragent@cursor.com>
Replaces hand-rolled useState/useTransition wiring in the watcher release settings form with @tanstack/react-form. Field state, isSubmitting, and validation are now owned by the form library, and the same Zod schema is checked onBlur (live feedback after a field is touched) and onSubmit (final guard) — mirroring VERSION_REGEX from the server route so typos are caught locally instead of round-tripping to a 400. - Adds the shadcn Field primitive (Field, FieldGroup, FieldLabel, FieldDescription, FieldError, FieldContent) used by the docs' TanStack Form examples. - Scopes re-renders to small subtrees via form.Subscribe selectors for the mandatory-switch row and the Save button. - Preserves the existing UX (visually-off-when-disabled mandatory switch, "" → null wire normalisation, last-updated-by footer, router.refresh on success). Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WATCHER_LATEST_VERSION/WATCHER_MIN_SUPPORTED_VERSION/WATCHER_RELEASE_CHANNEL/WATCHER_MANDATORY_UPDATEenv vars with a singletonwatcher_release_configPostgres row (migration0024), so release rollouts no longer require a Vercel env-var bump.web/app/settings/watchers/page.tsx) andPUT /api/v1/settings/watcher-releaseroute (session-only viarequireAdmin(), matching the members PATCH precedent). The update-check endpoint reads from the DB and falls back tolatest_version: nullwhen no row exists, preserving the existing "unset means skip self-update" contract for the watcher fleet.resetDb()re-seeds after every cycle becauseTRUNCATE … CASCADEon `"user"` wipes the row via theupdated_byFK regardless ofON DELETE SET NULL.Design notes
id boolean PRIMARY KEY DEFAULT true+CHECK (id = true)— exactly 0 or 1 rows enforced at the DB layer, so routes can skipLIMIT 1/ ordering discipline.GET /api/v1/watchers/:id/update-checkreturns the same four-field shape; the watcher fleet doesn't need to know the source moved from env to DB.docs/guides/upgrading-the-watcher.mdstill applies — just edited through the UI instead ofvercel envcommands./api/v1/users/[userId]PATCH); 401, not 403, for PAT callers so the failure mode reads as "session required".useTransition+router.refresh()instead of a manualuseStateloading flag; the Mandatory checkbox derives its disabled state during render fromlatest_version's blank-ness (nouseEffectsync).Verification
make check-all— clean (ruff, ruff-format, pyright, prettier, eslint, tsc).make fe-test-integration— 181 tests across 13 files pass, including the new `watcher-release.test.ts` (8 tests: admin-gate negatives + end-to-end upsert → update-check + singleton CHECK constraint).make fe-test-mcp— 55 tests pass.make py-test-unit— 547 tests pass.watcher/tests/integration/test_update_check.py— 2 tests pass against the new DB-seeded path (verifies the shared Python test harness fix).Post-merge
Once each Vercel environment's admin has saved a release on the settings page, the four
WATCHER_*env vars can be unset in the Vercel project. Out of scope for this PR.Test plan
0024_add_watcher_release_config.sqlon staging./api/v1/watchers/<id>/update-checkreturns the new value.Made with Cursor