From 22d8a09208c11b17f9e7e2dd58289eae297888c3 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 6 Jun 2026 12:29:58 -0500 Subject: [PATCH 1/6] fix(provider): resync built-in provider configs from embedded YAML Built-in provider configs persisted exec.command verbatim into provider.json at init time, so a CLI command rename (helper sh -> internal sh) left a stale wrapper that broke the inner agent commands (e.g. workspace delete failed with a bare "exit status 1"). LoadProviderConfig now refreshes internal (Source.Internal) providers from the embedded provider.yaml, which is the source of truth. A parse failure of the embedded YAML is logged rather than silently falling back to the stale stored config. Bumps all built-in providers to v1.0.0 so a refreshed config is visibly newer than the legacy v0.0.1. --- pkg/provider/dir.go | 30 +++++++++++ pkg/provider/load_internal_test.go | 83 ++++++++++++++++++++++++++++++ providers/docker/provider.yaml | 2 +- providers/kubernetes/provider.yaml | 2 +- providers/podman/provider.yaml | 2 +- providers/pro/provider.yaml | 2 +- 6 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 pkg/provider/load_internal_test.go diff --git a/pkg/provider/dir.go b/pkg/provider/dir.go index 1a922c8a1..da7e3f182 100644 --- a/pkg/provider/dir.go +++ b/pkg/provider/dir.go @@ -12,6 +12,8 @@ import ( "github.com/devsy-org/devsy/pkg/config" config2 "github.com/devsy-org/devsy/pkg/devcontainer/config" "github.com/devsy-org/devsy/pkg/id" + "github.com/devsy-org/devsy/pkg/log" + "github.com/devsy-org/devsy/providers" ) const ( @@ -303,9 +305,37 @@ func LoadProviderConfig(context, provider string) (*ProviderConfig, error) { return nil, err } + // Refresh built-in providers from the embedded YAML so a CLI change (e.g. + // renaming the command wrapper) can't leave a stale exec.command in the + // provider.json written at init time. + if providerConfig.Source.Internal { + if embedded := embeddedProviderConfig(providerConfig.Name); embedded != nil { + return embedded, nil + } + } + return providerConfig, nil } +// embeddedProviderConfig parses the built-in provider definition for name, +// returning nil when name is not built-in or the YAML fails to parse. +func embeddedProviderConfig(name string) *ProviderConfig { + raw := providers.GetBuiltInProviders()[name] + if raw == "" { + return nil + } + parsed, err := ParseProvider(strings.NewReader(raw)) + if err != nil { + // The YAML is go:embed-compiled, so a parse failure is a build-time + // regression. Log it: the silent fallback to the stored config would + // otherwise reintroduce the stale-command bug this refresh defeats. + log.Errorf("built-in provider %q failed to parse: %v", name, err) + return nil + } + parsed.Source.Internal = true + return parsed +} + func LoadMachineConfig(context, machineID string) (*Machine, error) { machineDir, err := GetMachineDir(context, machineID) if err != nil { diff --git a/pkg/provider/load_internal_test.go b/pkg/provider/load_internal_test.go new file mode 100644 index 000000000..29922b533 --- /dev/null +++ b/pkg/provider/load_internal_test.go @@ -0,0 +1,83 @@ +package provider + +import ( + "strings" + "testing" + + "github.com/devsy-org/devsy/pkg/config" + "github.com/stretchr/testify/require" +) + +// TestLoadProviderConfig_RefreshesInternalProvider verifies that a stored +// built-in provider config with a stale exec.command (e.g. the removed +// "helper sh" wrapper baked in before a CLI rename) is refreshed from the +// embedded provider definition on load. +func TestLoadProviderConfig_RefreshesInternalProvider(t *testing.T) { + setupTestHome(t) + + stale := &ProviderConfig{ + Name: "docker", + Version: "v0.0.0-stale", + Source: ProviderSource{Internal: true, Raw: "docker"}, + Exec: ProviderCommands{ + Command: []string{`"${DEVSY}" helper sh -c "${COMMAND}"`}, + }, + } + require.NoError(t, SaveProviderConfig(config.DefaultContext, stale)) + + loaded, err := LoadProviderConfig(config.DefaultContext, "docker") + require.NoError(t, err) + require.Len(t, loaded.Exec.Command, 1) + require.NotContains(t, loaded.Exec.Command[0], "helper sh", + "internal provider must be refreshed from embedded yaml, not the stale stored config") + require.True(t, strings.Contains(loaded.Exec.Command[0], "internal sh"), + "refreshed command should use the current 'internal sh' wrapper") + // The embedded YAML is the source of truth for internal providers, so the + // whole definition (not just exec.command) is taken from embed: the stale + // stored Version is replaced and Source.Internal stays set. + require.NotEqual(t, "v0.0.0-stale", loaded.Version, + "internal refresh replaces the stored definition wholesale, including Version") + require.True(t, loaded.Source.Internal, "refreshed provider must remain internal") +} + +// TestLoadProviderConfig_UnknownInternalFallsBack ensures an internal provider +// whose name is not a built-in (e.g. a removed/renamed provider lingering on +// disk) falls back to the stored config rather than failing or returning empty. +func TestLoadProviderConfig_UnknownInternalFallsBack(t *testing.T) { + setupTestHome(t) + + stored := &ProviderConfig{ + Name: "retired-provider", + Version: "v0.0.1", + Source: ProviderSource{Internal: true, Raw: "retired-provider"}, + Exec: ProviderCommands{ + Command: []string{`"${DEVSY}" internal sh -c "${COMMAND}"`}, + }, + } + require.NoError(t, SaveProviderConfig(config.DefaultContext, stored)) + + loaded, err := LoadProviderConfig(config.DefaultContext, "retired-provider") + require.NoError(t, err) + require.Equal(t, stored.Exec.Command, loaded.Exec.Command) + require.Equal(t, "v0.0.1", loaded.Version) +} + +// TestLoadProviderConfig_PreservesExternalProvider ensures non-internal +// providers are loaded verbatim from disk (no embedded refresh). +func TestLoadProviderConfig_PreservesExternalProvider(t *testing.T) { + setupTestHome(t) + + external := &ProviderConfig{ + Name: "docker", + Version: "v0.0.1", + Source: ProviderSource{Github: "some-org/some-provider"}, + Exec: ProviderCommands{ + Command: []string{`"${DEVSY}" helper sh -c "${COMMAND}"`}, + }, + } + require.NoError(t, SaveProviderConfig(config.DefaultContext, external)) + + loaded, err := LoadProviderConfig(config.DefaultContext, "docker") + require.NoError(t, err) + require.Equal(t, external.Exec.Command, loaded.Exec.Command) +} diff --git a/providers/docker/provider.yaml b/providers/docker/provider.yaml index 90037881a..75085908c 100644 --- a/providers/docker/provider.yaml +++ b/providers/docker/provider.yaml @@ -1,5 +1,5 @@ name: docker -version: v0.0.1 +version: v1.0.0 icon: https://devsy.sh/assets/docker.svg home: https://github.com/devsy-org/devsy description: |- diff --git a/providers/kubernetes/provider.yaml b/providers/kubernetes/provider.yaml index be120bd83..965c968cd 100644 --- a/providers/kubernetes/provider.yaml +++ b/providers/kubernetes/provider.yaml @@ -1,5 +1,5 @@ name: kubernetes -version: v0.0.1 +version: v1.0.0 icon: https://devsy.sh/assets/kubernetes.svg home: https://github.com/devsy-org/devsy description: |- diff --git a/providers/podman/provider.yaml b/providers/podman/provider.yaml index f8533ffaa..60ba8f810 100644 --- a/providers/podman/provider.yaml +++ b/providers/podman/provider.yaml @@ -1,5 +1,5 @@ name: podman -version: v0.0.1 +version: v1.0.0 icon: https://devsy.sh/assets/podman.svg home: https://github.com/devsy-org/devsy description: |- diff --git a/providers/pro/provider.yaml b/providers/pro/provider.yaml index d7fa41c9a..fbe4401bc 100644 --- a/providers/pro/provider.yaml +++ b/providers/pro/provider.yaml @@ -1,5 +1,5 @@ name: devsy-pro -version: v0.0.1 +version: v1.0.0 icon: https://devsy.sh/assets/devsy.svg home: https://github.com/devsy-org/devsy description: Devsy Pro From 4c28ac5d1edd6ec723c71c21cef8fa734860b8af Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 6 Jun 2026 12:47:36 -0500 Subject: [PATCH 2/6] style(provider): use DockerDriver constant in tests Replace repeated "docker" string literals with the existing DockerDriver constant to satisfy goconst. --- pkg/provider/load_internal_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/provider/load_internal_test.go b/pkg/provider/load_internal_test.go index 29922b533..e41d9a275 100644 --- a/pkg/provider/load_internal_test.go +++ b/pkg/provider/load_internal_test.go @@ -16,16 +16,16 @@ func TestLoadProviderConfig_RefreshesInternalProvider(t *testing.T) { setupTestHome(t) stale := &ProviderConfig{ - Name: "docker", + Name: DockerDriver, Version: "v0.0.0-stale", - Source: ProviderSource{Internal: true, Raw: "docker"}, + Source: ProviderSource{Internal: true, Raw: DockerDriver}, Exec: ProviderCommands{ Command: []string{`"${DEVSY}" helper sh -c "${COMMAND}"`}, }, } require.NoError(t, SaveProviderConfig(config.DefaultContext, stale)) - loaded, err := LoadProviderConfig(config.DefaultContext, "docker") + loaded, err := LoadProviderConfig(config.DefaultContext, DockerDriver) require.NoError(t, err) require.Len(t, loaded.Exec.Command, 1) require.NotContains(t, loaded.Exec.Command[0], "helper sh", @@ -68,7 +68,7 @@ func TestLoadProviderConfig_PreservesExternalProvider(t *testing.T) { setupTestHome(t) external := &ProviderConfig{ - Name: "docker", + Name: DockerDriver, Version: "v0.0.1", Source: ProviderSource{Github: "some-org/some-provider"}, Exec: ProviderCommands{ @@ -77,7 +77,7 @@ func TestLoadProviderConfig_PreservesExternalProvider(t *testing.T) { } require.NoError(t, SaveProviderConfig(config.DefaultContext, external)) - loaded, err := LoadProviderConfig(config.DefaultContext, "docker") + loaded, err := LoadProviderConfig(config.DefaultContext, DockerDriver) require.NoError(t, err) require.Equal(t, external.Exec.Command, loaded.Exec.Command) } From f097123a7a75ddb3abf21facaead78e7dd7e0f74 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 6 Jun 2026 13:26:12 -0500 Subject: [PATCH 3/6] docs(provider): trim verbose comments in internal-provider refresh --- pkg/provider/dir.go | 10 ++++------ pkg/provider/load_internal_test.go | 5 ++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/pkg/provider/dir.go b/pkg/provider/dir.go index da7e3f182..f053f95ef 100644 --- a/pkg/provider/dir.go +++ b/pkg/provider/dir.go @@ -305,9 +305,8 @@ func LoadProviderConfig(context, provider string) (*ProviderConfig, error) { return nil, err } - // Refresh built-in providers from the embedded YAML so a CLI change (e.g. - // renaming the command wrapper) can't leave a stale exec.command in the - // provider.json written at init time. + // Refresh built-in providers from embedded YAML: a CLI change (e.g. renaming + // the command wrapper) must not be shadowed by a stale provider.json. if providerConfig.Source.Internal { if embedded := embeddedProviderConfig(providerConfig.Name); embedded != nil { return embedded, nil @@ -326,9 +325,8 @@ func embeddedProviderConfig(name string) *ProviderConfig { } parsed, err := ParseProvider(strings.NewReader(raw)) if err != nil { - // The YAML is go:embed-compiled, so a parse failure is a build-time - // regression. Log it: the silent fallback to the stored config would - // otherwise reintroduce the stale-command bug this refresh defeats. + // Embedded YAML failing to parse is a build-time regression; log rather + // than silently fall back to the stale stored config this refresh fixes. log.Errorf("built-in provider %q failed to parse: %v", name, err) return nil } diff --git a/pkg/provider/load_internal_test.go b/pkg/provider/load_internal_test.go index e41d9a275..71799d475 100644 --- a/pkg/provider/load_internal_test.go +++ b/pkg/provider/load_internal_test.go @@ -32,9 +32,8 @@ func TestLoadProviderConfig_RefreshesInternalProvider(t *testing.T) { "internal provider must be refreshed from embedded yaml, not the stale stored config") require.True(t, strings.Contains(loaded.Exec.Command[0], "internal sh"), "refreshed command should use the current 'internal sh' wrapper") - // The embedded YAML is the source of truth for internal providers, so the - // whole definition (not just exec.command) is taken from embed: the stale - // stored Version is replaced and Source.Internal stays set. + // Refresh replaces the whole definition from embed, not just exec.command, + // so the stale stored Version is dropped and Source.Internal stays set. require.NotEqual(t, "v0.0.0-stale", loaded.Version, "internal refresh replaces the stored definition wholesale, including Version") require.True(t, loaded.Source.Internal, "refreshed provider must remain internal") From 3cd31ad257620b116bc5ab0f07a114059e6b4931 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 6 Jun 2026 13:50:32 -0500 Subject: [PATCH 4/6] fix(provider): refresh built-ins by source id, not name The built-in provider map is keyed by source id (e.g. "pro"), but some providers have a different Name ("pro" -> "devsy-pro"). Keying the refresh lookup off providerConfig.Name missed those, leaving a stale provider.json. Use Source.Raw (the source id) instead. --- pkg/provider/dir.go | 14 ++++++++------ pkg/provider/load_internal_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/pkg/provider/dir.go b/pkg/provider/dir.go index f053f95ef..f9fede5a2 100644 --- a/pkg/provider/dir.go +++ b/pkg/provider/dir.go @@ -308,7 +308,7 @@ func LoadProviderConfig(context, provider string) (*ProviderConfig, error) { // Refresh built-in providers from embedded YAML: a CLI change (e.g. renaming // the command wrapper) must not be shadowed by a stale provider.json. if providerConfig.Source.Internal { - if embedded := embeddedProviderConfig(providerConfig.Name); embedded != nil { + if embedded := embeddedProviderConfig(providerConfig.Source.Raw); embedded != nil { return embedded, nil } } @@ -316,10 +316,12 @@ func LoadProviderConfig(context, provider string) (*ProviderConfig, error) { return providerConfig, nil } -// embeddedProviderConfig parses the built-in provider definition for name, -// returning nil when name is not built-in or the YAML fails to parse. -func embeddedProviderConfig(name string) *ProviderConfig { - raw := providers.GetBuiltInProviders()[name] +// embeddedProviderConfig parses the built-in provider definition for the given +// source key (e.g. "pro"), returning nil when it is not built-in or the YAML +// fails to parse. The built-in map is keyed by source id, which differs from +// the provider Name for some providers (pro -> name "devsy-pro"). +func embeddedProviderConfig(sourceID string) *ProviderConfig { + raw := providers.GetBuiltInProviders()[sourceID] if raw == "" { return nil } @@ -327,7 +329,7 @@ func embeddedProviderConfig(name string) *ProviderConfig { if err != nil { // Embedded YAML failing to parse is a build-time regression; log rather // than silently fall back to the stale stored config this refresh fixes. - log.Errorf("built-in provider %q failed to parse: %v", name, err) + log.Errorf("built-in provider %q failed to parse: %v", sourceID, err) return nil } parsed.Source.Internal = true diff --git a/pkg/provider/load_internal_test.go b/pkg/provider/load_internal_test.go index 71799d475..b5b7769d1 100644 --- a/pkg/provider/load_internal_test.go +++ b/pkg/provider/load_internal_test.go @@ -39,6 +39,30 @@ func TestLoadProviderConfig_RefreshesInternalProvider(t *testing.T) { require.True(t, loaded.Source.Internal, "refreshed provider must remain internal") } +// TestLoadProviderConfig_RefreshesProBySourceID guards the case where a +// built-in's provider Name differs from its source id: pro is stored as +// "devsy-pro" but keyed in the embedded map as "pro". Refresh must key off +// Source.Raw (the source id), not Name, or the pro provider keeps a stale config. +func TestLoadProviderConfig_RefreshesProBySourceID(t *testing.T) { + setupTestHome(t) + + stale := &ProviderConfig{ + Name: "devsy-pro", + Version: "v0.0.0-stale", + Source: ProviderSource{Internal: true, Raw: "pro"}, + Exec: ProviderCommands{ + Command: []string{`"${DEVSY}" helper sh -c "${COMMAND}"`}, + }, + } + require.NoError(t, SaveProviderConfig(config.DefaultContext, stale)) + + loaded, err := LoadProviderConfig(config.DefaultContext, "devsy-pro") + require.NoError(t, err) + require.NotEqual(t, "v0.0.0-stale", loaded.Version, + "pro provider must be refreshed from embedded yaml via Source.Raw") + require.True(t, loaded.Source.Internal) +} + // TestLoadProviderConfig_UnknownInternalFallsBack ensures an internal provider // whose name is not a built-in (e.g. a removed/renamed provider lingering on // disk) falls back to the stored config rather than failing or returning empty. From 3e3f57c270d455e551be8c776a7cc3f094c32973 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 6 Jun 2026 14:15:37 -0500 Subject: [PATCH 5/6] style(provider): extract stale helper-sh command constant The new pro refresh test pushed the "helper sh" exec.command literal to 3 occurrences, tripping goconst. Hoist it to a named constant. --- pkg/provider/load_internal_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/provider/load_internal_test.go b/pkg/provider/load_internal_test.go index b5b7769d1..e741ff8f8 100644 --- a/pkg/provider/load_internal_test.go +++ b/pkg/provider/load_internal_test.go @@ -8,6 +8,10 @@ import ( "github.com/stretchr/testify/require" ) +// staleHelperShCommand is the removed "helper sh" exec.command wrapper that +// stale provider.json files carry before a refresh rewrites them. +const staleHelperShCommand = `"${DEVSY}" helper sh -c "${COMMAND}"` + // TestLoadProviderConfig_RefreshesInternalProvider verifies that a stored // built-in provider config with a stale exec.command (e.g. the removed // "helper sh" wrapper baked in before a CLI rename) is refreshed from the @@ -20,7 +24,7 @@ func TestLoadProviderConfig_RefreshesInternalProvider(t *testing.T) { Version: "v0.0.0-stale", Source: ProviderSource{Internal: true, Raw: DockerDriver}, Exec: ProviderCommands{ - Command: []string{`"${DEVSY}" helper sh -c "${COMMAND}"`}, + Command: []string{staleHelperShCommand}, }, } require.NoError(t, SaveProviderConfig(config.DefaultContext, stale)) @@ -51,7 +55,7 @@ func TestLoadProviderConfig_RefreshesProBySourceID(t *testing.T) { Version: "v0.0.0-stale", Source: ProviderSource{Internal: true, Raw: "pro"}, Exec: ProviderCommands{ - Command: []string{`"${DEVSY}" helper sh -c "${COMMAND}"`}, + Command: []string{staleHelperShCommand}, }, } require.NoError(t, SaveProviderConfig(config.DefaultContext, stale)) @@ -95,7 +99,7 @@ func TestLoadProviderConfig_PreservesExternalProvider(t *testing.T) { Version: "v0.0.1", Source: ProviderSource{Github: "some-org/some-provider"}, Exec: ProviderCommands{ - Command: []string{`"${DEVSY}" helper sh -c "${COMMAND}"`}, + Command: []string{staleHelperShCommand}, }, } require.NoError(t, SaveProviderConfig(config.DefaultContext, external)) From 1e34a49b9bb4b94bd8e6e5bb95b4f59a9dcb0fbb Mon Sep 17 00:00:00 2001 From: Samuel K Date: Sat, 6 Jun 2026 14:41:06 -0500 Subject: [PATCH 6/6] fix(provider): overlay only exec on internal refresh Wholesale-replacing the stored config with the embedded definition clobbered user customizations: a built-in added with a custom --name or --option (e.g. `devsy provider add docker --name foo`) lost its Name and resolved Options on load, breaking `devsy up`. Only the exec/agent.exec blocks carry the stale command wrapper, so overlay just those and keep the rest of the stored config. --- pkg/provider/dir.go | 10 +++--- pkg/provider/load_internal_test.go | 55 ++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/pkg/provider/dir.go b/pkg/provider/dir.go index f9fede5a2..c42fa46cd 100644 --- a/pkg/provider/dir.go +++ b/pkg/provider/dir.go @@ -305,11 +305,14 @@ func LoadProviderConfig(context, provider string) (*ProviderConfig, error) { return nil, err } - // Refresh built-in providers from embedded YAML: a CLI change (e.g. renaming - // the command wrapper) must not be shadowed by a stale provider.json. + // Refresh the exec commands of built-in providers from embedded YAML: a CLI + // change (e.g. renaming the command wrapper) must not be shadowed by a stale + // provider.json. Only the exec blocks are overlaid so user customizations + // (custom --name, resolved options) survive. if providerConfig.Source.Internal { if embedded := embeddedProviderConfig(providerConfig.Source.Raw); embedded != nil { - return embedded, nil + providerConfig.Exec = embedded.Exec + providerConfig.Agent.Exec = embedded.Agent.Exec } } @@ -332,7 +335,6 @@ func embeddedProviderConfig(sourceID string) *ProviderConfig { log.Errorf("built-in provider %q failed to parse: %v", sourceID, err) return nil } - parsed.Source.Internal = true return parsed } diff --git a/pkg/provider/load_internal_test.go b/pkg/provider/load_internal_test.go index e741ff8f8..5cc0053f7 100644 --- a/pkg/provider/load_internal_test.go +++ b/pkg/provider/load_internal_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/devsy-org/devsy/pkg/config" + "github.com/devsy-org/devsy/pkg/types" "github.com/stretchr/testify/require" ) @@ -12,16 +13,18 @@ import ( // stale provider.json files carry before a refresh rewrites them. const staleHelperShCommand = `"${DEVSY}" helper sh -c "${COMMAND}"` +const staleVersion = "v0.0.0-stale" + // TestLoadProviderConfig_RefreshesInternalProvider verifies that a stored // built-in provider config with a stale exec.command (e.g. the removed -// "helper sh" wrapper baked in before a CLI rename) is refreshed from the -// embedded provider definition on load. +// "helper sh" wrapper baked in before a CLI rename) has its exec block +// refreshed from the embedded provider definition on load. func TestLoadProviderConfig_RefreshesInternalProvider(t *testing.T) { setupTestHome(t) stale := &ProviderConfig{ Name: DockerDriver, - Version: "v0.0.0-stale", + Version: staleVersion, Source: ProviderSource{Internal: true, Raw: DockerDriver}, Exec: ProviderCommands{ Command: []string{staleHelperShCommand}, @@ -36,11 +39,35 @@ func TestLoadProviderConfig_RefreshesInternalProvider(t *testing.T) { "internal provider must be refreshed from embedded yaml, not the stale stored config") require.True(t, strings.Contains(loaded.Exec.Command[0], "internal sh"), "refreshed command should use the current 'internal sh' wrapper") - // Refresh replaces the whole definition from embed, not just exec.command, - // so the stale stored Version is dropped and Source.Internal stays set. - require.NotEqual(t, "v0.0.0-stale", loaded.Version, - "internal refresh replaces the stored definition wholesale, including Version") - require.True(t, loaded.Source.Internal, "refreshed provider must remain internal") +} + +// TestLoadProviderConfig_PreservesCustomizations verifies the exec refresh +// overlays only the embedded exec block, leaving a user's custom Name and +// resolved Options intact (a built-in added with `--name`/`--option`). +func TestLoadProviderConfig_PreservesCustomizations(t *testing.T) { + setupTestHome(t) + + stored := &ProviderConfig{ + Name: "my-docker", + Version: staleVersion, + Source: ProviderSource{Internal: true, Raw: DockerDriver}, + Options: map[string]*types.Option{ + "DOCKER_PATH": {Default: "podman"}, + }, + Exec: ProviderCommands{ + Command: []string{staleHelperShCommand}, + }, + } + require.NoError(t, SaveProviderConfig(config.DefaultContext, stored)) + + loaded, err := LoadProviderConfig(config.DefaultContext, "my-docker") + require.NoError(t, err) + require.Equal(t, "my-docker", loaded.Name, "custom provider name must survive refresh") + require.Equal(t, "v0.0.0-stale", loaded.Version, "stored version must survive refresh") + require.Equal(t, "podman", loaded.Options["DOCKER_PATH"].Default, + "resolved options must survive refresh") + require.NotContains(t, loaded.Exec.Command[0], "helper sh", + "exec block must still be refreshed from embedded yaml") } // TestLoadProviderConfig_RefreshesProBySourceID guards the case where a @@ -52,19 +79,17 @@ func TestLoadProviderConfig_RefreshesProBySourceID(t *testing.T) { stale := &ProviderConfig{ Name: "devsy-pro", - Version: "v0.0.0-stale", + Version: staleVersion, Source: ProviderSource{Internal: true, Raw: "pro"}, - Exec: ProviderCommands{ - Command: []string{staleHelperShCommand}, - }, + Exec: ProviderCommands{Daemon: &DaemonCommands{Start: []string{"stale start"}}}, } require.NoError(t, SaveProviderConfig(config.DefaultContext, stale)) loaded, err := LoadProviderConfig(config.DefaultContext, "devsy-pro") require.NoError(t, err) - require.NotEqual(t, "v0.0.0-stale", loaded.Version, - "pro provider must be refreshed from embedded yaml via Source.Raw") - require.True(t, loaded.Source.Internal) + require.NotNil(t, loaded.Exec.Daemon, "pro exec must be refreshed from embedded yaml") + require.NotEqual(t, []string{"stale start"}, []string(loaded.Exec.Daemon.Start), + "pro provider exec must be refreshed from embedded yaml via Source.Raw") } // TestLoadProviderConfig_UnknownInternalFallsBack ensures an internal provider