Skip to content

Commit 9250a47

Browse files
CLI: Update hypeman SDK to b99ed488ace3 and add new flags
- Updated hypeman-go to b99ed488ace35cbd4f84d1bec1f180adf3b7367f - Added --volume/-v flag on `hypeman run` for InstanceNewParams.Volumes - Added --base-image-digest, --cache-scope, --global-cache-key, --is-admin-build, --secrets flags on `hypeman build` for BuildNewParams Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7a5ea30 commit 9250a47

4 files changed

Lines changed: 106 additions & 3 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/google/go-containerregistry v0.20.7
1111
github.com/gorilla/websocket v1.5.3
1212
github.com/itchyny/json2yaml v0.1.4
13-
github.com/kernel/hypeman-go v0.9.7-0.20260211203915-a9a0d6c96059
13+
github.com/kernel/hypeman-go v0.9.8-0.20260211234143-b99ed488ace3
1414
github.com/muesli/reflow v0.3.0
1515
github.com/stretchr/testify v1.11.1
1616
github.com/tidwall/gjson v1.18.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV
7272
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
7373
github.com/itchyny/json2yaml v0.1.4 h1:/pErVOXGG5iTyXHi/QKR4y3uzhLjGTEmmJIy97YT+k8=
7474
github.com/itchyny/json2yaml v0.1.4/go.mod h1:6iudhBZdarpjLFRNj+clWLAkGft+9uCcjAZYXUH9eGI=
75-
github.com/kernel/hypeman-go v0.9.7-0.20260211203915-a9a0d6c96059 h1:C5ixkSnUllJJSWBVAusdj8uX7FcS/eJE1TbzqGAvwQc=
76-
github.com/kernel/hypeman-go v0.9.7-0.20260211203915-a9a0d6c96059/go.mod h1:guRrhyP9QW/ebUS1UcZ0uZLLJeGAAhDNzSi68U4M9hI=
75+
github.com/kernel/hypeman-go v0.9.8-0.20260211234143-b99ed488ace3 h1:hhJTGHB6wrBoIBCxg3yhRqTZ7pDrIWwd9fsA05zWc68=
76+
github.com/kernel/hypeman-go v0.9.8-0.20260211234143-b99ed488ace3/go.mod h1:guRrhyP9QW/ebUS1UcZ0uZLLJeGAAhDNzSi68U4M9hI=
7777
github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co=
7878
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
7979
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=

pkg/cmd/build.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,26 @@ Examples:
5353
Usage: "Build timeout in seconds",
5454
Value: 600,
5555
},
56+
&cli.StringFlag{
57+
Name: "base-image-digest",
58+
Usage: "Pinned base image digest for reproducible builds",
59+
},
60+
&cli.StringFlag{
61+
Name: "cache-scope",
62+
Usage: "Tenant-specific cache key prefix",
63+
},
64+
&cli.StringFlag{
65+
Name: "global-cache-key",
66+
Usage: `Global cache identifier (e.g., "node", "python", "ubuntu")`,
67+
},
68+
&cli.StringFlag{
69+
Name: "is-admin-build",
70+
Usage: `Set to "true" to grant push access to global cache (operator-only)`,
71+
},
72+
&cli.StringFlag{
73+
Name: "secrets",
74+
Usage: `JSON array of secret references to inject during build (e.g., '[{"id":"npm_token"}]')`,
75+
},
5676
},
5777
Commands: []*cli.Command{
5878
&buildListCmd,
@@ -130,6 +150,22 @@ func handleBuild(ctx context.Context, cmd *cli.Command) error {
130150
params.Dockerfile = hypeman.Opt(dockerfileContent)
131151
}
132152

153+
if v := cmd.String("base-image-digest"); v != "" {
154+
params.BaseImageDigest = hypeman.Opt(v)
155+
}
156+
if v := cmd.String("cache-scope"); v != "" {
157+
params.CacheScope = hypeman.Opt(v)
158+
}
159+
if v := cmd.String("global-cache-key"); v != "" {
160+
params.GlobalCacheKey = hypeman.Opt(v)
161+
}
162+
if v := cmd.String("is-admin-build"); v != "" {
163+
params.IsAdminBuild = hypeman.Opt(v)
164+
}
165+
if v := cmd.String("secrets"); v != "" {
166+
params.Secrets = hypeman.Opt(v)
167+
}
168+
133169
// Start build
134170
build, err := client.Builds.New(ctx, params, opts...)
135171
if err != nil {

pkg/cmd/run.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ Examples:
108108
Name: "skip-kernel-headers",
109109
Usage: "Skip kernel headers installation during boot for faster startup (DKMS will not work)",
110110
},
111+
// Volume mount flags
112+
&cli.StringSliceFlag{
113+
Name: "volume",
114+
Aliases: []string{"v"},
115+
Usage: `Attach volume at creation (format: volume-id:/mount/path[:ro[:overlay=SIZE]]). Can be repeated.`,
116+
},
111117
},
112118
Action: handleRun,
113119
HideHelpCommand: true,
@@ -236,6 +242,20 @@ func handleRun(ctx context.Context, cmd *cli.Command) error {
236242
params.SkipKernelHeaders = hypeman.Opt(cmd.Bool("skip-kernel-headers"))
237243
}
238244

245+
// Volume mounts
246+
volumeSpecs := cmd.StringSlice("volume")
247+
if len(volumeSpecs) > 0 {
248+
var mounts []hypeman.VolumeMountParam
249+
for _, spec := range volumeSpecs {
250+
mount, err := parseVolumeSpec(spec)
251+
if err != nil {
252+
return fmt.Errorf("invalid volume spec %q: %w", spec, err)
253+
}
254+
mounts = append(mounts, mount)
255+
}
256+
params.Volumes = mounts
257+
}
258+
239259
fmt.Fprintf(os.Stderr, "Creating instance %s...\n", name)
240260

241261
var opts []option.RequestOption
@@ -315,6 +335,53 @@ func waitForImageReady(ctx context.Context, client *hypeman.Client, img *hypeman
315335
}
316336
}
317337

338+
// parseVolumeSpec parses a volume mount specification string.
339+
// Format: volume-id:/mount/path[:ro[:overlay=SIZE]]
340+
// Examples:
341+
//
342+
// my-vol:/data
343+
// my-vol:/data:ro
344+
// my-vol:/data:ro:overlay=10GB
345+
func parseVolumeSpec(spec string) (hypeman.VolumeMountParam, error) {
346+
parts := strings.SplitN(spec, ":", 2)
347+
if len(parts) < 2 {
348+
return hypeman.VolumeMountParam{}, fmt.Errorf("expected format volume-id:/mount/path[:ro[:overlay=SIZE]]")
349+
}
350+
351+
volumeID := parts[0]
352+
if volumeID == "" {
353+
return hypeman.VolumeMountParam{}, fmt.Errorf("volume ID cannot be empty")
354+
}
355+
356+
remaining := parts[1]
357+
// Split remaining by colon to get mount path and options
358+
segments := strings.Split(remaining, ":")
359+
mountPath := segments[0]
360+
if mountPath == "" {
361+
return hypeman.VolumeMountParam{}, fmt.Errorf("mount path cannot be empty")
362+
}
363+
364+
mount := hypeman.VolumeMountParam{
365+
VolumeID: volumeID,
366+
MountPath: mountPath,
367+
}
368+
369+
// Parse optional flags
370+
for _, seg := range segments[1:] {
371+
switch {
372+
case seg == "ro":
373+
mount.Readonly = hypeman.Opt(true)
374+
case strings.HasPrefix(seg, "overlay="):
375+
mount.Overlay = hypeman.Opt(true)
376+
mount.OverlaySize = hypeman.Opt(strings.TrimPrefix(seg, "overlay="))
377+
default:
378+
return hypeman.VolumeMountParam{}, fmt.Errorf("unknown option %q", seg)
379+
}
380+
}
381+
382+
return mount, nil
383+
}
384+
318385
// showImageStatus prints image build status to stderr
319386
func showImageStatus(img *hypeman.Image) {
320387
switch img.Status {

0 commit comments

Comments
 (0)