Stage 3: App — program/deployment model & controls
- ProgramActions: install/uninstall (activate) shown only for tools and no-service frontends. Daemons activate via a service/job, so the program page no longer offers a misleading Install/Uninstall (which secretly enabled/disabled the service). - Program page: new Deployments section listing the services/jobs that run the program, as links (a program → 0-N services/jobs). - Service page: 'Runs' row from the run_target summary field (the old code dug into manifest.run, which was empty pre-Stage-1), a Host row for proxy_host, and a convenience link to the referenced program. - lifecycle.activate on a daemon with no service now errors toward 'castle expose' instead of silently installing its binary to PATH. core 94 green; ruff + app build clean.
This commit is contained in:
@@ -42,10 +42,22 @@ interface ProgramActionsProps {
|
||||
name: string
|
||||
actions: string[]
|
||||
active?: boolean | null
|
||||
behavior?: string | null
|
||||
/** Names of services/jobs deploying this program — daemons activate via these. */
|
||||
deployedAs?: string[]
|
||||
compact?: boolean
|
||||
onOutput?: (output: ActionOutput) => void
|
||||
}
|
||||
|
||||
/** install/uninstall (activate) is meaningful only for tools (PATH) and static
|
||||
* frontends (served). A daemon activates through a service/job, so it never
|
||||
* shows install/uninstall here — its run controls live on the deployment page. */
|
||||
function showsActivation(behavior: string | null | undefined, deployedAs: string[]): boolean {
|
||||
if (behavior === "daemon") return false
|
||||
if (behavior === "frontend") return deployedAs.length === 0 // self-serving frontend → its service
|
||||
return true // tools (and unspecified)
|
||||
}
|
||||
|
||||
function visibleActions(
|
||||
actions: string[],
|
||||
active: boolean | null | undefined,
|
||||
@@ -78,11 +90,23 @@ function visibleActions(
|
||||
return visible
|
||||
}
|
||||
|
||||
export function ProgramActions({ name, actions, active, compact, onOutput }: ProgramActionsProps) {
|
||||
export function ProgramActions({
|
||||
name,
|
||||
actions,
|
||||
active,
|
||||
behavior,
|
||||
deployedAs = [],
|
||||
compact,
|
||||
onOutput,
|
||||
}: ProgramActionsProps) {
|
||||
const { mutate, isPending } = useProgramAction()
|
||||
const [runningAction, setRunningAction] = useState<string | null>(null)
|
||||
|
||||
const visible = visibleActions(actions, active, !!compact)
|
||||
// Drop install/uninstall for behaviors that activate via a deployment.
|
||||
const allowed = showsActivation(behavior, deployedAs)
|
||||
? actions
|
||||
: actions.filter((a) => a !== "install" && a !== "uninstall")
|
||||
const visible = visibleActions(allowed, active, !!compact)
|
||||
|
||||
const handleAction = (action: string) => {
|
||||
setRunningAction(action)
|
||||
|
||||
Reference in New Issue
Block a user