From a7ab0828a20d4a8c988c7f0821da6a1325f2673d Mon Sep 17 00:00:00 2001 From: Angel Marin Date: Thu, 9 Jul 2026 12:23:06 +0200 Subject: [PATCH] HYPERFLEET-1290 - fix: restore backward compat for precondition api_call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Downgrade the hard validation error introduced in HYPERFLEET-1290 to a deprecation warning so existing adapter configs using api_call directly on preconditions continue to work without changes. The precondition executor already handles api_call at runtime — the breakage was purely in the semantic validator. Callers are now warned at startup to migrate to the params source.api_call style. Co-Authored-By: Claude Sonnet 4.6 --- internal/configloader/loader.go | 3 +++ internal/configloader/validator.go | 15 +++++++++++---- internal/configloader/validator_test.go | 14 +++++++++----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/internal/configloader/loader.go b/internal/configloader/loader.go index 7c436ff..e874a73 100644 --- a/internal/configloader/loader.go +++ b/internal/configloader/loader.go @@ -181,6 +181,9 @@ func LoadConfig(opts ...LoadOption) (*Config, error) { if err := taskValidator.ValidateSemantic(); err != nil { return nil, fmt.Errorf("task config semantic validation failed: %w", err) } + for _, w := range taskValidator.Warnings() { + o.logger.Warn(o.ctx, w) + } } // 3. Merge into unified Config diff --git a/internal/configloader/validator.go b/internal/configloader/validator.go index ae09a6d..f17c621 100644 --- a/internal/configloader/validator.go +++ b/internal/configloader/validator.go @@ -58,6 +58,7 @@ func (v *AdapterConfigValidator) ValidateStructure() error { type TaskConfigValidator struct { config *AdapterTaskConfig errors *ValidationErrors + warnings []string definedVars map[string]bool celEnv *cel.Env baseDir string @@ -72,6 +73,11 @@ func NewTaskConfigValidator(config *AdapterTaskConfig, baseDir string) *TaskConf } } +// Warnings returns deprecation warnings collected during validation. +func (v *TaskConfigValidator) Warnings() []string { + return v.warnings +} + // ValidateStructure validates the structural requirements of AdapterTaskConfig func (v *TaskConfigValidator) ValidateStructure() error { if v.config == nil { @@ -182,15 +188,16 @@ func (v *TaskConfigValidator) validatePreconditionAPICallForbidden() { for i, precond := range v.config.Preconditions { if precond.APICall != nil { path := fmt.Sprintf("%s[%d].%s", FieldPreconditions, i, FieldAPICall) - v.errors.Add(path, fmt.Sprintf( - "precondition %q contains api_call. api_call is no longer valid in the precondition phase.\n"+ - "Move the api_call block to a params entry:\n"+ + v.warnings = append(v.warnings, fmt.Sprintf( + "%s: DEPRECATED: precondition %q uses api_call directly. "+ + "Move the api_call block to a params entry with source.api_call instead. "+ + "Direct api_call on preconditions will be removed in a future release.\n"+ " params:\n"+ " - name: %q\n"+ " source:\n"+ " api_call:\n"+ " ...", - precond.Name, precond.Name)) + path, precond.Name, precond.Name)) } } } diff --git a/internal/configloader/validator_test.go b/internal/configloader/validator_test.go index bc4f95a..d1212b1 100644 --- a/internal/configloader/validator_test.go +++ b/internal/configloader/validator_test.go @@ -1228,7 +1228,7 @@ func TestValidateParamsAPICallSource(t *testing.T) { } func TestValidatePreconditionAPICallForbidden(t *testing.T) { - t.Run("precondition with api_call produces migration error", func(t *testing.T) { + t.Run("precondition with api_call produces deprecation warning not error", func(t *testing.T) { cfg := baseTaskConfig() cfg.Preconditions = []Precondition{{ ActionBase: ActionBase{ @@ -1239,12 +1239,15 @@ func TestValidatePreconditionAPICallForbidden(t *testing.T) { v := newTaskValidator(cfg) _ = v.ValidateStructure() err := v.ValidateSemantic() - require.Error(t, err) - assert.Contains(t, err.Error(), "api_call is no longer valid in the precondition phase") - assert.Contains(t, err.Error(), "fetchCluster") + require.NoError(t, err) + warnings := v.Warnings() + require.Len(t, warnings, 1) + assert.Contains(t, warnings[0], "DEPRECATED") + assert.Contains(t, warnings[0], "fetchCluster") + assert.Contains(t, warnings[0], "source.api_call") }) - t.Run("precondition without api_call passes", func(t *testing.T) { + t.Run("precondition without api_call passes with no warnings", func(t *testing.T) { cfg := baseTaskConfig() cfg.Preconditions = []Precondition{{ ActionBase: ActionBase{Name: "check"}, @@ -1253,5 +1256,6 @@ func TestValidatePreconditionAPICallForbidden(t *testing.T) { v := newTaskValidator(cfg) require.NoError(t, v.ValidateStructure()) require.NoError(t, v.ValidateSemantic()) + assert.Empty(t, v.Warnings()) }) }