-
Notifications
You must be signed in to change notification settings - Fork 388
[FEAT] [CLI] Make Hatchet Lite version configurable #3925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,18 @@ func printInfo(message string) { | |
| fmt.Println(infoStyle.Render(fmt.Sprintf(" %s", message))) | ||
| } | ||
|
|
||
| // PullPolicy controls when Docker images are pulled, mirroring Docker Compose's pull_policy. | ||
| type PullPolicy string | ||
|
|
||
| const ( | ||
| // PullPolicyAlways always pulls the image from the registry (default, existing behavior). | ||
| PullPolicyAlways PullPolicy = "always" | ||
| // PullPolicyMissing only pulls if the image is not already available locally. | ||
| PullPolicyMissing PullPolicy = "missing" | ||
| // PullPolicyNever never pulls; the image must already exist locally. | ||
| PullPolicyNever PullPolicy = "never" | ||
| ) | ||
|
|
||
|
Comment on lines
+39
to
+44
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO we can remove these comments. Perhaps just leave the original on
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
| // hatchet lite opts | ||
| const ( | ||
| defaultpostgresName = "postgres" | ||
|
|
@@ -54,6 +66,8 @@ type HatchetLiteOpts struct { | |
| serviceName string | ||
| overrideDashboardPort int | ||
| overrideGrpcPort int | ||
| imageTag string | ||
| pullPolicy PullPolicy | ||
| } | ||
|
|
||
| func initDefaultHatchetLiteOpts() *HatchetLiteOpts { | ||
|
|
@@ -62,6 +76,8 @@ func initDefaultHatchetLiteOpts() *HatchetLiteOpts { | |
| hatchetName: defaulthatchetName, | ||
| projectName: defaultprojectName, | ||
| serviceName: defaultserviceName, | ||
| imageTag: "latest", | ||
| pullPolicy: PullPolicyAlways, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -133,6 +149,31 @@ func WithOverrideGrpcPort(port int) HatchetLiteOpt { | |
| } | ||
| } | ||
|
|
||
| // WithImageTag sets the image tag for the hatchet-lite container (e.g. "v0.83.1"). | ||
| func WithImageTag(tag string) HatchetLiteOpt { | ||
| return func(o *HatchetLiteOpts) error { | ||
| if tag == "" { | ||
| return fmt.Errorf("image tag must not be empty") | ||
| } | ||
|
|
||
| o.imageTag = tag | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| // WithPullPolicy sets the image pull policy. Valid values are "always", "missing", and "never". | ||
| func WithPullPolicy(policy string) HatchetLiteOpt { | ||
| return func(o *HatchetLiteOpts) error { | ||
| switch PullPolicy(policy) { | ||
| case PullPolicyAlways, PullPolicyMissing, PullPolicyNever: | ||
| o.pullPolicy = PullPolicy(policy) | ||
| return nil | ||
| default: | ||
| return fmt.Errorf("invalid pull policy %q: must be \"always\", \"missing\", or \"never\"", policy) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func (d *DockerDriver) RunHatchetLite(ctx context.Context, opts ...HatchetLiteOpt) error { | ||
| hatchetLiteOpts := initDefaultHatchetLiteOpts() | ||
|
|
||
|
|
@@ -249,14 +290,9 @@ func (d *DockerDriver) startPostgresContainer(ctx context.Context, opts *Hatchet | |
| imageName := "postgres:17" | ||
| containerName := canonicalContainerName(opts.projectName, opts.postgresName) | ||
|
|
||
| out, err := d.apiClient.ImagePull(ctx, imageName, image.PullOptions{}) | ||
| if err != nil { | ||
| if err := d.pullImageWithPolicy(ctx, imageName, opts.pullPolicy); err != nil { | ||
| return fmt.Errorf("could not pull image %s: %w", imageName, err) | ||
| } | ||
| defer out.Close() | ||
|
|
||
| // Display progress while pulling the image | ||
| displayImagePullProgress(out, imageName) | ||
|
|
||
| // Get image details for proper labeling | ||
| imageInspect, err := d.apiClient.ImageInspect(ctx, imageName) | ||
|
|
@@ -356,17 +392,12 @@ func (d *DockerDriver) stopPostgresContainer(ctx context.Context, opts *HatchetL | |
| } | ||
|
|
||
| func (d *DockerDriver) startHatchetLiteContainer(ctx context.Context, opts *HatchetLiteOpts, networkId string, dashboardPort, grpcPort int, sharedLabels map[string]string) error { | ||
| imageName := "ghcr.io/hatchet-dev/hatchet/hatchet-lite:latest" | ||
| imageName := "ghcr.io/hatchet-dev/hatchet/hatchet-lite:" + opts.imageTag | ||
| containerName := canonicalContainerName(opts.projectName, opts.hatchetName) | ||
|
|
||
| out, err := d.apiClient.ImagePull(ctx, imageName, image.PullOptions{}) | ||
| if err != nil { | ||
| if err := d.pullImageWithPolicy(ctx, imageName, opts.pullPolicy); err != nil { | ||
| return fmt.Errorf("could not pull image %s: %w", imageName, err) | ||
| } | ||
| defer out.Close() | ||
|
|
||
| // Display progress while pulling the image | ||
| displayImagePullProgress(out, imageName) | ||
|
|
||
| // Get image details for proper labeling | ||
| imageInspect, err := d.apiClient.ImageInspect(ctx, imageName) | ||
|
|
@@ -552,3 +583,43 @@ func getSharedLabels(opts *HatchetLiteOpts) map[string]string { | |
| "com.docker.compose.project": opts.projectName, | ||
| } | ||
| } | ||
|
|
||
| // pullImageWithPolicy pulls an image according to the specified pull policy. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. super-nit: don't think we need docs IMO -- this is a private + internal function unlikely to be used anywhere else in the codebase.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apologies for the over documentation, I was using some AI assistance to ensure the logic was clear but definitely went a bit overboard |
||
| func (d *DockerDriver) pullImageWithPolicy(ctx context.Context, imageName string, policy PullPolicy) error { | ||
| switch policy { | ||
| case PullPolicyNever: | ||
| // Verify the image exists locally. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we remove these comments? Would help tighten up the diff!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure |
||
| _, err := d.apiClient.ImageInspect(ctx, imageName) | ||
| if err != nil { | ||
| return fmt.Errorf("image %s not found locally and pull policy is \"never\": %w", imageName, err) | ||
| } | ||
|
|
||
| printInfo(fmt.Sprintf("Using local image %s (pull policy: never)", imageName)) | ||
|
|
||
| return nil | ||
| case PullPolicyMissing: | ||
| // Only pull if the image is not present locally. | ||
| if _, err := d.apiClient.ImageInspect(ctx, imageName); err == nil { | ||
| printInfo(fmt.Sprintf("Image %s already exists locally, skipping pull (pull policy: missing)", imageName)) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // Image not found locally, fall through to pull. | ||
| printInfo(fmt.Sprintf("Image %s not found locally, pulling... (pull policy: missing)", imageName)) | ||
| case PullPolicyAlways: | ||
| // Always pull, this is the default behavior. | ||
| default: | ||
| return fmt.Errorf("unknown pull policy: %q", policy) | ||
| } | ||
|
|
||
| out, err := d.apiClient.ImagePull(ctx, imageName, image.PullOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("could not pull image %s: %w", imageName, err) | ||
| } | ||
| defer out.Close() | ||
|
|
||
| displayImagePullProgress(out, imageName) | ||
|
|
||
| return nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,15 +31,26 @@ var startCmd = &cobra.Command{ | |
| hatchet server start --dashboard-port 9000 --grpc-port 8077 --project-name my-hatchet | ||
|
|
||
| # Start server with custom profile name | ||
| hatchet server start --profile my-local`, | ||
| hatchet server start --profile my-local | ||
|
|
||
| # Start server with a specific hatchet-lite version | ||
| hatchet server start --tag v0.83.1 | ||
|
|
||
| # Start server without pulling images (use local images only) | ||
| hatchet server start --pull-policy never | ||
|
|
||
| # Only pull images if they are not already available locally | ||
| hatchet server start --pull-policy missing`, | ||
| Run: func(cmd *cobra.Command, args []string) { | ||
| // Get flag values | ||
| dashboardPort, _ := cmd.Flags().GetInt("dashboard-port") | ||
| grpcPort, _ := cmd.Flags().GetInt("grpc-port") | ||
| projectName, _ := cmd.Flags().GetString("project-name") | ||
| profileName, _ := cmd.Flags().GetString("profile") | ||
| tag, _ := cmd.Flags().GetString("tag") | ||
| pullPolicy, _ := cmd.Flags().GetString("pull-policy") | ||
|
|
||
| result, err := startLocalServer(cmd, profileName, dashboardPort, grpcPort, projectName) | ||
| result, err := startLocalServer(cmd, profileName, dashboardPort, grpcPort, projectName, tag, pullPolicy) | ||
| if err != nil { | ||
| cli.Logger.Fatalf("%v", err) | ||
| } | ||
|
|
@@ -94,7 +105,7 @@ type ServerStartResult struct { | |
| } | ||
|
|
||
| // startLocalServer starts a local Hatchet server and returns connection details | ||
| func startLocalServer(cmd *cobra.Command, profileName string, dashboardPort, grpcPort int, projectName string) (*ServerStartResult, error) { | ||
| func startLocalServer(cmd *cobra.Command, profileName string, dashboardPort, grpcPort int, projectName, tag, pullPolicy string) (*ServerStartResult, error) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: function signature is getting quite long. Wdyt of passing in a struct here instead? Or perhaps instead replacing all of these with varadic
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well second option will be better i think, refactored startLocalServer to use the variadic HatchetLiteOpt pattern. Its much cleaner now. Thanks for the tip |
||
| dockerDriver, err := docker.NewDockerDriver(cmd.Context()) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("Docker is required to run a local server. Please ensure Docker is installed and running: %w", err) | ||
|
|
@@ -126,6 +137,14 @@ func startLocalServer(cmd *cobra.Command, profileName string, dashboardPort, grp | |
| opts = append(opts, docker.WithProjectName(projectName)) | ||
| } | ||
|
|
||
| if tag != "" { | ||
| opts = append(opts, docker.WithImageTag(tag)) | ||
| } | ||
|
|
||
| if pullPolicy != "" { | ||
| opts = append(opts, docker.WithPullPolicy(pullPolicy)) | ||
| } | ||
|
|
||
| err = dockerDriver.RunHatchetLite(cmd.Context(), opts...) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("could not start hatchet-lite container: %w", err) | ||
|
|
@@ -183,6 +202,8 @@ func init() { | |
| startCmd.Flags().IntP("grpc-port", "g", 0, "Port for the Hatchet gRPC server (default: auto-detect starting at 7077)") | ||
| startCmd.Flags().StringP("project-name", "p", "", "Docker project name for containers (default: hatchet-cli)") | ||
| startCmd.Flags().StringP("profile", "n", "local", "Name for the local profile (default: local)") | ||
| startCmd.Flags().StringP("tag", "t", "latest", `Image tag for the hatchet-lite container (e.g. "v0.83.1")`) | ||
| startCmd.Flags().String("pull-policy", "always", `Image pull policy: "always", "missing", or "never"`) | ||
|
|
||
| stopCmd.Flags().StringP("project-name", "p", "", "Docker project name for containers (default: hatchet-cli)") | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should
PullPolicybe public?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for overlooking that, it should not be public, since its only used internally within the docker package