Skip to content

Expose remaining hypeman-go SDK surface in the CLI#56

Merged
rgarcia merged 2 commits into
mainfrom
hypeman/cli-sdk-coverage
Jun 15, 2026
Merged

Expose remaining hypeman-go SDK surface in the CLI#56
rgarcia merged 2 commits into
mainfrom
hypeman/cli-sdk-coverage

Conversation

@rgarcia

@rgarcia rgarcia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

The CLI called 50 of the SDK's 52 methods, and a few useful params (workload health check, restart policy, multi-rule ingress) were only reachable through compose. This brings the CLI to full coverage of the published SDK surface.

  • update health-check / update restart-policy — new subcommands that PATCH Instances.Update (previously these InstanceUpdateParams fields were never set by the CLI).
  • run --health-* / --restart-* flags — imperative parity with what compose could already declare, including HTTP scheme, expected-status, and exec working-dir.
  • Shared mapping pathcompose, run, and the new update subcommands now build HealthCheckParam/RestartPolicyParam through one set of builders (lib/compose/policy.go) so the logic can't drift. compose behavior is unchanged.
  • volume create --from-archive <path|-> — routes to Volumes.NewFromArchive (gzip body; - reads stdin). Was entirely unused.
  • ingress create --rule (repeatable)hostname[:host-port]=instance:port[,tls][,redirect-http], exposing the Rules array the SDK supports. When any --rule is given the single-rule shorthand flags are rejected (unambiguous precedence); a rule that omits the instance falls back to the positional <instance>.
  • health — top-level command calling Health.Check (GET /health, distinct from resources).

Test plan

  • go build ./..., go vet ./..., gofmt -l clean
  • go test ./... passes, including a new TestParseIngressRuleSpec (8 cases) and the existing compose tests (confirming the shared-builder refactor preserves behavior)
  • manual smoke against a live API (update health-check, run --health-*, volume create --from-archive, ingress create --rule, health)

🤖 Generated with Claude Code


Note

Medium Risk
Changes affect instance create/update (health/restart), ingress routing, and volume creation from archives; shared builders reduce mapping drift but incorrect flags could still send wrong API payloads.

Overview
Brings the CLI in line with the published hypeman-go SDK by wiring previously unused APIs and params that were only reachable through compose.

Shared policy builders — Health check and restart policy mapping moves into lib/compose/policy.go (BuildHealthCheckParam / BuildRestartPolicyParam). Compose, run, and the new update flows all use the same path so behavior stays aligned; compose still only sets MaxAttempts when > 0, while CLI update can send explicit 0 for unlimited.

Instance lifecyclerun gains --health-* and --restart-* flags. New update health-check and update restart-policy subcommands PATCH Instances.Update. Reusable flag parsing lives in policyflags.go with validation (e.g. HTTP probe requires --http-port).

Ingressingress create supports repeatable --rule (hostname[:host-port]=instance:port[,tls][,redirect-http]); shorthand --hostname/--port flags are mutually exclusive with --rule. Single-rule mode now validates required flags at runtime instead of via Required on the flag.

Volumes & API healthvolume create --from-archive streams a tar.gz (file or - stdin) to Volumes.NewFromArchive. Top-level health calls Health.Check (server liveness, separate from resources).

Reviewed by Cursor Bugbot for commit f713a72. Bugbot is set up for automated code reviews on this repo. Configure here.

Closes the gap between the SDK's published methods/params and what the CLI
exposed. The CLI previously called 50 of 52 SDK methods and left several
params (health-check, restart-policy, multi-rule ingress) reachable only via
compose.

- add `update health-check` and `update restart-policy` subcommands
- add `run --health-* / --restart-*` flags for imperative parity with compose
- share one health-check/restart-policy mapping path across compose, run, and
  update (new lib/compose/policy.go) so the logic can't drift
- add `volume create --from-archive <path|->` (Volumes.NewFromArchive)
- add repeatable `ingress create --rule` for multi-rule ingresses
- add top-level `health` command (Health.Check)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@rgarcia

rgarcia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: HTTP probe allows zero port
    • Health-check parsing now errors when HTTP metadata flags are used without --http-port, preventing generation of an HTTP probe with port 0.
  • ✅ Fixed: Update ignores unlimited restarts
    • Update restart-policy now explicitly sets max_attempts whenever --max-attempts is provided, so a value of 0 correctly clears the limit to unlimited.

Create PR

Or push these changes by commenting:

@cursor push 9526924db2
Preview (9526924db2)
diff --git a/pkg/cmd/policyflags.go b/pkg/cmd/policyflags.go
--- a/pkg/cmd/policyflags.go
+++ b/pkg/cmd/policyflags.go
@@ -1,6 +1,8 @@
 package cmd
 
 import (
+	"fmt"
+
 	"github.com/kernel/hypeman-cli/lib/compose"
 	"github.com/urfave/cli/v3"
 )
@@ -83,7 +85,7 @@
 	}
 }
 
-func parseHealthCheckInput(cmd *cli.Command, prefix string) (compose.HealthCheckInput, bool) {
+func parseHealthCheckInput(cmd *cli.Command, prefix string) (compose.HealthCheckInput, bool, error) {
 	typeFlag := prefix + "type"
 	intervalFlag := prefix + "interval"
 	timeoutFlag := prefix + "timeout"
@@ -98,7 +100,15 @@
 	execFlag := prefix + "exec"
 	execWorkingDirFlag := prefix + "exec-working-dir"
 
-	httpSet := cmd.IsSet(httpPortFlag) || cmd.IsSet(httpPathFlag) || cmd.IsSet(httpSchemeFlag) || cmd.IsSet(httpExpectedStatusFlag)
+	httpPortSet := cmd.IsSet(httpPortFlag)
+	httpMetadataSet := cmd.IsSet(httpPathFlag) || cmd.IsSet(httpSchemeFlag) || cmd.IsSet(httpExpectedStatusFlag)
+	if httpMetadataSet && !httpPortSet {
+		return compose.HealthCheckInput{}, false, fmt.Errorf(
+			"--%s is required when using --%s, --%s, or --%s",
+			httpPortFlag, httpPathFlag, httpSchemeFlag, httpExpectedStatusFlag,
+		)
+	}
+	httpSet := httpPortSet
 	tcpSet := cmd.IsSet(tcpPortFlag)
 	// Gate the exec block on --exec only: HealthCheckExecParam.Command is required,
 	// so building it from a lone --exec-working-dir would send an empty command.
@@ -106,9 +116,9 @@
 
 	set := cmd.IsSet(typeFlag) || cmd.IsSet(intervalFlag) || cmd.IsSet(timeoutFlag) ||
 		cmd.IsSet(startPeriodFlag) || cmd.IsSet(failureThresholdFlag) || cmd.IsSet(successThresholdFlag) ||
-		httpSet || tcpSet || execSet
+		httpSet || httpMetadataSet || tcpSet || execSet
 	if !set {
-		return compose.HealthCheckInput{}, false
+		return compose.HealthCheckInput{}, false, nil
 	}
 
 	in := compose.HealthCheckInput{
@@ -136,7 +146,7 @@
 			WorkingDir: cmd.String(execWorkingDirFlag),
 		}
 	}
-	return in, true
+	return in, true, nil
 }
 
 func parseRestartPolicyInput(cmd *cli.Command, prefix string) (compose.RestartPolicyInput, bool) {

diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go
--- a/pkg/cmd/run.go
+++ b/pkg/cmd/run.go
@@ -273,7 +273,11 @@
 	if autoStandbySet {
 		params.AutoStandby = autoStandbyPolicy
 	}
-	if healthInput, ok := parseHealthCheckInput(cmd, "health-"); ok {
+	healthInput, healthSet, err := parseHealthCheckInput(cmd, "health-")
+	if err != nil {
+		return err
+	}
+	if healthSet {
 		params.HealthCheck = compose.BuildHealthCheckParam(healthInput)
 	}
 	if restartInput, ok := parseRestartPolicyInput(cmd, "restart-"); ok {

diff --git a/pkg/cmd/update.go b/pkg/cmd/update.go
--- a/pkg/cmd/update.go
+++ b/pkg/cmd/update.go
@@ -149,7 +149,10 @@
 		return fmt.Errorf("instance ID or name required\nUsage: hypeman update health-check <instance> [flags]")
 	}
 
-	input, set := parseHealthCheckInput(cmd, "")
+	input, set, err := parseHealthCheckInput(cmd, "")
+	if err != nil {
+		return err
+	}
 	if !set {
 		return fmt.Errorf("at least one health-check flag is required")
 	}
@@ -210,8 +213,13 @@
 		return err
 	}
 
+	restartPolicy := compose.BuildRestartPolicyParam(input)
+	if cmd.IsSet("max-attempts") {
+		restartPolicy.MaxAttempts = hypeman.Int(input.MaxAttempts)
+	}
+
 	params := hypeman.InstanceUpdateParams{
-		RestartPolicy: compose.BuildRestartPolicyParam(input),
+		RestartPolicy: restartPolicy,
 	}
 
 	var opts []option.RequestOption

You can send follow-ups to the cloud agent here.

Comment thread pkg/cmd/policyflags.go
Comment thread lib/compose/policy.go
- require --http-port (and --exec) before engaging the HTTP/exec health probe,
  so a secondary flag alone (--http-path/-scheme/-expected-status,
  --exec-working-dir) can't build a probe with a zero/empty api:"required" field
- send restart max_attempts only when explicitly set, so `--max-attempts 0`
  (documented as unlimited) clears the limit on a PATCH instead of being
  omitted as a no-op; compose's omit-when-zero behavior is preserved
- add policyflags tests for both

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@rgarcia

rgarcia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit f713a72. Configure here.

@rgarcia rgarcia marked this pull request as ready for review June 15, 2026 15:52
@rgarcia rgarcia merged commit aa0dfd3 into main Jun 15, 2026
7 checks passed
@rgarcia rgarcia deleted the hypeman/cli-sdk-coverage branch June 15, 2026 15:53
@firetiger-agent

Copy link
Copy Markdown

Created a monitoring plan for this PR.

What this PR does: Gives CLI users direct imperative access to workload health checks, restart policies, multi-rule ingress, and volume seeding from an archive — features that were previously only reachable through compose.

Intended effect: No production telemetry signal exists for this change — hypeman-cli is a distributed CLI tool with no service deployment, so new commands only produce API traffic when users explicitly invoke them. Confirmation comes from the manual smoke tests listed in the PR (marked unchecked): update health-check, update restart-policy, run --health-*, volume create --from-archive, ingress create --rule, and health should all return without error against a live Hypeman API.

Risks:

  • Compose regression from shared-builder refactorbuildComposeHealthCheck/buildComposeRestartPolicy now delegate to BuildHealthCheckParam/BuildRestartPolicyParam; compose tests cover this, but watch Kernel API OTel error rate: alert if sustained error rate > 0.5% for 2+ hours post-release (baseline: ~0.01–0.07% active hours)
  • Volumes.NewFromArchive first activation — this SDK path was entirely unused by the CLI before; a content-type or serialization mismatch would produce a 4xx from Hypeman; confirm via smoke test before calling the release healthy
  • Ingress validation change--hostname/--port no longer Required: true; now runtime-checked; invocation without flags should still return a clear error message (manual checkpoint)
  • SDK/API version skew — CLI pins hypeman-go v0.20.0; if target Hypeman server doesn't expose Health.Check or Volumes.NewFromArchive, users will see 404/400; confirm server version alignment before release

Status updates will be posted automatically on this PR as monitoring progresses.

View monitor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant