stacklok / stacklok/toolhive-studio

refactor(workload-upgrade): migrate manual upgrade flow to new upgrade-check / upgrade API

Open
#2,309 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement Task
Dominant language
TypeScript
Stars
167
Forks
24
Avg merge
11h 32m
Merged PRs (30d)
91

Description

Context

The ToolHive CLI bump to v0.29.0 (PR #2305) ships new REST endpoints dedicated to workload upgrades:

  • GET /api/v1beta/workloads/upgrade-check — bulk drift check (filter by group, include stopped via all=true)
  • GET /api/v1beta/workloads/{name}/upgrade-check — per-workload drift check
  • POST /api/v1beta/workloads/{name}/upgrade — apply upgrade (optional { env, secrets } merge body)

These cover end-to-end what the renderer is doing today by hand.

Current state (what we do "a mano")

Drift detection (client-side):

  • useIsServerFromRegistry (renderer/src/features/mcp-servers/hooks/use-is-server-from-registry.ts) parses the running image tag, matches the workload image against the registry catalog, and flags drift when registryTag !== localTag.
  • The card surfaces an amber ArrowUpCircle button when isFromRegistry && drift (renderer/src/features/mcp-servers/components/card-mcp-server/index.tsx:64).

Apply (via the generic edit endpoint):

  • useUpdateVersion (renderer/src/features/mcp-servers/hooks/use-update-version.tsx) orchestrates two paths:
    1. Direct update when there is no env-var drift: confirm dialog → useMutationUpdateWorkloadPOST /workloads/{name}/edit with the new image.
    2. Edit & review when env-var drift is detected: opens the full edit dialog pre-populated with imageOverride, envVarsOverride, secretsOverride, then submits via the same edit mutation.
  • The env-var diff itself is computed client-side from the registry entry.

New API surface (after #2305 lands)

GET .../upgrade-check returns a full UpgradeCheckResult:

  • status: up-to-date | upgrade-available | not-registry-sourced | server-not-found | unknown
  • current_image, candidate_image, registry_server
  • env_var_drift.added / .removed (with name, description, default, required, secret) — same data we recompute today for the review dialog
  • config_drift.transport / config_drift.permission_profiledrift we currently ignore

POST .../upgrade accepts PkgApiV1UpgradeRequest:

  • env?: Record<string, string> — overrides/merge
  • secrets?: string[] — secret references in <name>,target=<env> form
  • Empty body = upgrade preserving existing configuration

Proposed migration (two independent steps)

Step 1 — replace client-side drift detection
  • Replace useIsServerFromRegistry consumers with getApiV1BetaWorkloadsUpgradeCheckOptions (bulk on list views) and the per-name variant where granularity is needed.
  • Delete the tag parsing / registry comparison logic in the renderer.
  • The card's "update available" indicator becomes `status === 'upgrade-available'`.
Step 2 — replace the apply mutation
  • In use-update-version.tsx, swap useMutationUpdateWorkload for postApiV1BetaWorkloadsByNameUpgradeMutation.
  • Direct upgrade path → POST .../upgrade with empty body.
  • Edit-&-review path → map the form values for newly added vars/secrets into { env, secrets } and call upgrade instead of edit.
  • Continue using the edit endpoint only for unrelated config changes (it stays the right tool for arbitrary edits).

Wins

  • Backend becomes the source of truth for drift; renderer stops parsing image tags.
  • We start surfacing config_drift (transport, permission profile) which is currently silently ignored.
  • Clean semantic separation: edit for arbitrary edits, upgrade for the registry-driven version bump.

Caveats / open questions

  • PkgApiV1UpgradeRequest does not accept cmd_arguments or arbitrary workload fields. If we want to allow tweaking those at upgrade time, we either keep using edit for that case or split it into two steps (upgrade then edit).
  • The bulk endpoint takes all and group query params — confirm the list view's current filter semantics still work with one call.
  • Decide UX for status === 'not-registry-sourced' | 'server-not-found' | 'unknown' (today these workloads are silently treated as "no drift").

Acceptance criteria

  • Step 1: list view and card use getApiV1BetaWorkloadsUpgradeCheck* instead of useIsServerFromRegistry; the hook is removed (or reduced to non-upgrade callers, if any).
  • Step 2: useUpdateVersion uses postApiV1BetaWorkloadsByNameUpgrade for both direct and edit-&-review paths.
  • config_drift (transport / permission profile) is surfaced in the review dialog.
  • Existing tests pass / are updated; new tests cover the upgrade-status branches (upgrade-available, up-to-date, not-registry-sourced, etc.).

References

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with renderer/src/features/mcp-servers/hooks/use-is-server-from-registry.ts, renderer/src/features/mcp-servers/components/card-mcp-server/index.tsx, and renderer/src/features/mcp-servers/hooks/use-update-version.tsx. Trace the existing list and card upgrade flows, then inspect the generated upgrade-check and upgrade API entry points. Done means both flows use the new endpoints, config drift appears in review, and the existing tests are updated with status-branch coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, frontend
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.