From b941c93ddb2b5c09bd3cc67dd4faa9fd619fae2d Mon Sep 17 00:00:00 2001 From: Anisa Oshafi Date: Wed, 25 Mar 2026 17:13:42 +0100 Subject: [PATCH] Use stable image by default --- README.md | 4 ++-- internal/config/config.go | 4 ++-- internal/config/containers.go | 4 ++-- internal/config/default_config.toml | 2 +- internal/container/start.go | 4 ++-- test/integration/version_resolution_test.go | 5 ++++- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index de18c7d3..e4d14bc7 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ lstk --config /path/to/config.toml start ```toml [[containers]] type = "aws" # Emulator type. Currently supported: "aws" -tag = "latest" # Docker image tag, e.g. "latest", "2026.03" +tag = "stable" # Docker image tag, e.g. "stable", "latest", "2026.03" port = "4566" # Host port the emulator will be accessible on # volume = "" # Host directory for persistent state (default: OS cache dir) # env = [] # Named environment profiles to apply (see [env.*] sections below) @@ -102,7 +102,7 @@ port = "4566" # Host port the emulator will be accessible on **Fields:** - `type`: emulator type; only `"aws"` is supported for now -- `tag`: Docker image tag for LocalStack (e.g. `"latest"`, `"4.14.0"`); useful for pinning a version +- `tag`: Docker image tag for LocalStack (e.g. `"latest"`, `"2026.03"`); useful for pinning a version - `port`: port LocalStack listens on (default `4566`) - `volume`: (optional) host directory for persistent emulator state (default: OS cache dir) - `env`: (optional) list of named environment variable groups to inject into the container (see below) diff --git a/internal/config/config.go b/internal/config/config.go index eea38fbf..5dad8bc3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -16,14 +16,14 @@ var defaultConfigTemplate string type Config struct { Containers []ContainerConfig `mapstructure:"containers"` Env map[string]map[string]string `mapstructure:"env"` - UpdatePrompt bool `mapstructure:"update_prompt"` + UpdatePrompt bool `mapstructure:"update_prompt"` } func setDefaults() { viper.SetDefault("containers", []map[string]any{ { "type": "aws", - "tag": "latest", + "tag": "stable", "port": "4566", }, }) diff --git a/internal/config/containers.go b/internal/config/containers.go index fea88c8f..c7d08674 100644 --- a/internal/config/containers.go +++ b/internal/config/containers.go @@ -97,10 +97,10 @@ func (c *ContainerConfig) Image() (string, error) { return fmt.Sprintf("%s/%s:%s", dockerRegistry, productName, tag), nil } -// Name returns the container name: "localstack-{type}" or "localstack-{type}-{tag}" if tag != latest +// Name returns the container name: "localstack-{type}" or "localstack-{type}-{tag}" if tag is a pinned version func (c *ContainerConfig) Name() string { tag := c.Tag - if tag == "" || tag == "latest" { + if tag == "" || tag == "latest" || tag == "stable" { return fmt.Sprintf("localstack-%s", c.Type) } return fmt.Sprintf("localstack-%s-%s", c.Type, tag) diff --git a/internal/config/default_config.toml b/internal/config/default_config.toml index 45acb3bb..907930a7 100644 --- a/internal/config/default_config.toml +++ b/internal/config/default_config.toml @@ -5,7 +5,7 @@ # You can define multiple to run them side by side. [[containers]] type = "aws" # Emulator type. Currently supported: "aws" -tag = "latest" # Docker image tag, e.g. "latest", "2026.03" +tag = "stable" # Docker image tag, e.g. "stable", "latest", "2026.03" port = "4566" # Host port the emulator will be accessible on # volume = "" # Host directory for persistent state (default: OS cache dir) # env = [] # Named environment profiles to apply (see [env.*] sections below) diff --git a/internal/container/start.go b/internal/container/start.go index e6cadf38..0f6ebdc4 100644 --- a/internal/container/start.go +++ b/internal/container/start.go @@ -265,12 +265,12 @@ func pullImages(ctx context.Context, rt runtime.Runtime, sink output.Sink, tel * } // Validates licenses before pulling where the version is known. -// Pinned tags are validated immediately; "latest" tags are resolved via the catalog API. +// Pinned tags are validated immediately; "latest" and "stable" tags are resolved via the catalog API. // Returns containers that couldn't be resolved (catalog unavailable) for post-pull validation. func tryPrePullLicenseValidation(ctx context.Context, sink output.Sink, opts StartOptions, tel *telemetry.Client, containers []runtime.ContainerConfig, token string) ([]runtime.ContainerConfig, error) { var needsPostPull []runtime.ContainerConfig for _, c := range containers { - if c.Tag != "" && c.Tag != "latest" { + if c.Tag != "" && c.Tag != "latest" && c.Tag != "stable" { if err := validateLicense(ctx, sink, opts, tel, c, token); err != nil { return nil, err } diff --git a/test/integration/version_resolution_test.go b/test/integration/version_resolution_test.go index de0733f8..c8696f68 100644 --- a/test/integration/version_resolution_test.go +++ b/test/integration/version_resolution_test.go @@ -53,7 +53,7 @@ func createVersionResolutionMockServer(t *testing.T, catalogVersion string, lice } // Verifies that when the catalog API returns a version, the license request uses -// that version (not "latest"), allowing license validation to happen before pulling the image. +// that version (not "stable" or "latest"), allowing license validation to happen before pulling the image. func TestVersionResolvedViaCatalog(t *testing.T) { requireDocker(t) _ = env.Require(t, env.AuthToken) @@ -71,6 +71,8 @@ func TestVersionResolvedViaCatalog(t *testing.T) { "license request should carry the version returned by the catalog API") assert.NotEqual(t, "latest", *capturedVersion, "license request should not use the unresolved 'latest' tag") + assert.NotEqual(t, "stable", *capturedVersion, + "license request should not use the unresolved 'stable' tag") } // Verifies that when the catalog endpoint is unavailable, the version is resolved @@ -91,6 +93,7 @@ func TestVersionFallsBackToImageInspectionWhenCatalogFails(t *testing.T) { assert.NotEmpty(t, *capturedVersion, "license request should carry a version resolved from image inspection") assert.NotEqual(t, "latest", *capturedVersion, "resolved version should not be the unresolved 'latest' tag") + assert.NotEqual(t, "stable", *capturedVersion, "resolved version should not be the unresolved 'stable' tag") } // Verifies that when both the catalog endpoint is unavailable and the license