0.35.0: per-stack Update button on the Stacks page

Adds an inline "Update (pull latest images & recreate)" action to each
row of the stacks table, next to start/stop/restart/edit — for both the
local host and remote agents. Wires the existing updateImages action and
the agent "update" lifecycle action through StacksTable's new onUpdate prop.

Also bumps backend/version.py to 0.35.0 so it tracks the frontend version
again (it had drifted to 0.33.0 while package.json moved to 0.34.x).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-16 18:44:21 +00:00
co-authored by Claude Opus 4.8
parent 844655d1c8
commit a4b1bbcdd1
5 changed files with 20 additions and 4 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
"""Single source of truth for the StackPilot release version."""
APP_VERSION = "0.33.0"
APP_VERSION = "0.35.0"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "stackpilot-frontend",
"private": true,
"version": "0.34.1",
"version": "0.35.0",
"type": "module",
"scripts": {
"dev": "vite",
@@ -81,6 +81,7 @@ export function AgentStacksSection({ agent, isAdmin }: { agent: Agent; isAdmin:
onStart={(id) => run("start", "Starting", id)}
onStop={(id) => run("stop", "Stopping", id)}
onRestart={(id) => run("restart", "Restarting", id)}
onUpdate={isAdmin ? (id) => run("update", "Updating", id) : undefined}
emptyText="No stacks on this host."
/>
)}
+15 -1
View File
@@ -1,7 +1,7 @@
import { useState } from "react";
import { Link } from "react-router-dom";
import { useQueryClient } from "@tanstack/react-query";
import { Play, Square, RotateCw, Pencil, Trash2 } from "lucide-react";
import { Play, Square, RotateCw, ArrowUpCircle, Pencil, Trash2 } from "lucide-react";
import { toast } from "sonner";
import { Card, Spinner, StatusDot, Badge } from "@/components/ui";
import { ConfirmDialog } from "@/components/ui/ConfirmDialog";
@@ -26,6 +26,7 @@ export function StacksTable({
onStart,
onStop,
onRestart,
onUpdate,
emptyText,
}: {
stacks: StackSummary[] | undefined;
@@ -41,6 +42,7 @@ export function StacksTable({
onStart: (id: string) => void;
onStop: (id: string) => void;
onRestart: (id: string) => void;
onUpdate?: (id: string) => void;
emptyText: string;
}) {
if (loading) return <Spinner />;
@@ -78,6 +80,7 @@ export function StacksTable({
onStart={onStart}
onStop={onStop}
onRestart={onRestart}
onUpdate={onUpdate}
/>
))}
</tbody>
@@ -99,6 +102,7 @@ function StackRow({
onStart,
onStop,
onRestart,
onUpdate,
}: {
stack: StackSummary;
stats?: StackStats;
@@ -112,6 +116,7 @@ function StackRow({
onStart: (id: string) => void;
onStop: (id: string) => void;
onRestart: (id: string) => void;
onUpdate?: (id: string) => void;
}) {
const qc = useQueryClient();
const running = stack.running_count > 0;
@@ -195,6 +200,15 @@ function StackRow({
<Play className="h-4 w-4 text-green-500" />
</IconBtn>
)}
{onUpdate && (
<IconBtn
title="Update (pull latest images & recreate)"
onClick={() => onUpdate(stack.id)}
disabled={busy}
>
<ArrowUpCircle className="h-4 w-4" />
</IconBtn>
)}
{showEdit && (
<Link
to={`${linkBase}/${stack.id}/edit`}
+2 -1
View File
@@ -23,7 +23,7 @@ const STATUS_FILTERS: Record<string, StatusFilter> = {
export function Stacks() {
const isAdmin = useAuthStore((s) => s.user?.role === "admin");
const { busyId, start, stop, restart } = useStackActions();
const { busyId, start, stop, restart, updateImages } = useStackActions();
// The dashboard explore bar deep-links here with ?q= / ?filter=.
const [params] = useSearchParams();
const [q, setQ] = useState(params.get("q") ?? "");
@@ -137,6 +137,7 @@ export function Stacks() {
onStart={start}
onStop={stop}
onRestart={restart}
onUpdate={isAdmin ? updateImages : undefined}
emptyText={q ? "No stacks match your search." : "No stacks yet. Create one with “New Stack”."}
/>
</section>