diff --git a/cmd/wfctl/type_registry.go b/cmd/wfctl/type_registry.go index 3f540734..b14b6f2e 100644 --- a/cmd/wfctl/type_registry.go +++ b/cmd/wfctl/type_registry.go @@ -702,6 +702,83 @@ func KnownStepTypes() map[string]StepTypeInfo { Plugin: "pipelinesteps", ConfigKeys: []string{"failure_threshold", "reset_timeout", "step", "fallback"}, }, + "step.json_parse": { + Type: "step.json_parse", + Plugin: "pipelinesteps", + ConfigKeys: []string{"source", "target"}, + }, + "step.raw_response": { + Type: "step.raw_response", + Plugin: "pipelinesteps", + ConfigKeys: []string{"content_type", "status", "headers", "body", "body_from"}, + }, + "step.auth_validate": { + Type: "step.auth_validate", + Plugin: "pipelinesteps", + ConfigKeys: []string{"auth_module", "token_source", "subject_field"}, + }, + "step.authz_check": { + Type: "step.authz_check", + Plugin: "pipelinesteps", + ConfigKeys: []string{"policy_engine", "subject_field", "input_from"}, + }, + "step.hash": { + Type: "step.hash", + Plugin: "pipelinesteps", + ConfigKeys: []string{"algorithm", "input"}, + }, + "step.regex_match": { + Type: "step.regex_match", + Plugin: "pipelinesteps", + ConfigKeys: []string{"pattern", "input"}, + }, + "step.parallel": { + Type: "step.parallel", + Plugin: "pipelinesteps", + ConfigKeys: []string{"steps", "error_strategy"}, + }, + "step.field_reencrypt": { + Type: "step.field_reencrypt", + Plugin: "pipelinesteps", + ConfigKeys: []string{"module", "tenant_id"}, + }, + "step.token_revoke": { + Type: "step.token_revoke", + Plugin: "pipelinesteps", + ConfigKeys: []string{"blacklist_module", "token_source"}, + }, + "step.sandbox_exec": { + Type: "step.sandbox_exec", + Plugin: "pipelinesteps", + ConfigKeys: []string{"image", "command", "security_profile", "memory_limit", "cpu_limit", "timeout", "network", "env", "mounts", "fail_on_error"}, + }, + "step.ui_scaffold": { + Type: "step.ui_scaffold", + Plugin: "pipelinesteps", + ConfigKeys: []string{"title", "theme", "auth", "filename"}, + }, + "step.ui_scaffold_analyze": { + Type: "step.ui_scaffold_analyze", + Plugin: "pipelinesteps", + ConfigKeys: []string{"title", "theme"}, + }, + "step.graphql": { + Type: "step.graphql", + Plugin: "pipelinesteps", + ConfigKeys: []string{"url", "query", "variables", "headers", "data_path", "fragments", "batch", "persisted_query", "introspection", "pagination", "fail_on_graphql_errors", "timeout", "retry_on_network_error", "auth"}, + }, + + // actors plugin steps + "step.actor_send": { + Type: "step.actor_send", + Plugin: "actors", + ConfigKeys: []string{"pool", "message", "identity"}, + }, + "step.actor_ask": { + Type: "step.actor_ask", + Plugin: "actors", + ConfigKeys: []string{"pool", "message", "timeout", "identity"}, + }, // http plugin steps "step.rate_limit": { @@ -827,6 +904,31 @@ func KnownStepTypes() map[string]StepTypeInfo { Plugin: "cicd", ConfigKeys: []string{"config", "output"}, }, + "step.git_clone": { + Type: "step.git_clone", + Plugin: "cicd", + ConfigKeys: []string{"repository", "directory", "branch", "token", "ssh_key", "depth"}, + }, + "step.git_commit": { + Type: "step.git_commit", + Plugin: "cicd", + ConfigKeys: []string{"directory", "message", "author_name", "author_email", "add_all", "add_files"}, + }, + "step.git_push": { + Type: "step.git_push", + Plugin: "cicd", + ConfigKeys: []string{"directory", "remote", "branch", "force", "tags", "token"}, + }, + "step.git_tag": { + Type: "step.git_tag", + Plugin: "cicd", + ConfigKeys: []string{"directory", "tag", "message", "push", "token"}, + }, + "step.git_checkout": { + Type: "step.git_checkout", + Plugin: "cicd", + ConfigKeys: []string{"directory", "branch", "create"}, + }, // auth-related steps (from pipelinesteps but auth-aware) "step.auth_required": { @@ -1141,6 +1243,186 @@ func KnownStepTypes() map[string]StepTypeInfo { Plugin: "platform", ConfigKeys: []string{"app"}, }, + + // platform plugin steps (platform template) + "step.platform_template": { + Type: "step.platform_template", + Plugin: "platform", + ConfigKeys: []string{"template_name", "template_version", "parameters"}, + }, + + // platform plugin steps (kubernetes) + "step.k8s_plan": { + Type: "step.k8s_plan", + Plugin: "platform", + ConfigKeys: []string{"cluster"}, + }, + "step.k8s_apply": { + Type: "step.k8s_apply", + Plugin: "platform", + ConfigKeys: []string{"cluster"}, + }, + "step.k8s_status": { + Type: "step.k8s_status", + Plugin: "platform", + ConfigKeys: []string{"cluster"}, + }, + "step.k8s_destroy": { + Type: "step.k8s_destroy", + Plugin: "platform", + ConfigKeys: []string{"cluster"}, + }, + + // platform plugin steps (scaling) + "step.scaling_plan": { + Type: "step.scaling_plan", + Plugin: "platform", + ConfigKeys: []string{"scaling"}, + }, + "step.scaling_apply": { + Type: "step.scaling_apply", + Plugin: "platform", + ConfigKeys: []string{"scaling"}, + }, + "step.scaling_status": { + Type: "step.scaling_status", + Plugin: "platform", + ConfigKeys: []string{"scaling"}, + }, + "step.scaling_destroy": { + Type: "step.scaling_destroy", + Plugin: "platform", + ConfigKeys: []string{"scaling"}, + }, + + // platform plugin steps (iac) + "step.iac_plan": { + Type: "step.iac_plan", + Plugin: "platform", + ConfigKeys: []string{"platform", "resource_id", "state_store"}, + }, + "step.iac_apply": { + Type: "step.iac_apply", + Plugin: "platform", + ConfigKeys: []string{"platform", "resource_id", "state_store"}, + }, + "step.iac_status": { + Type: "step.iac_status", + Plugin: "platform", + ConfigKeys: []string{"platform", "resource_id", "state_store"}, + }, + "step.iac_destroy": { + Type: "step.iac_destroy", + Plugin: "platform", + ConfigKeys: []string{"platform", "resource_id", "state_store"}, + }, + "step.iac_drift_detect": { + Type: "step.iac_drift_detect", + Plugin: "platform", + ConfigKeys: []string{"platform", "resource_id", "state_store"}, + }, + + // platform plugin steps (dns) + "step.dns_plan": { + Type: "step.dns_plan", + Plugin: "platform", + ConfigKeys: []string{"zone"}, + }, + "step.dns_apply": { + Type: "step.dns_apply", + Plugin: "platform", + ConfigKeys: []string{"zone"}, + }, + "step.dns_status": { + Type: "step.dns_status", + Plugin: "platform", + ConfigKeys: []string{"zone"}, + }, + + // platform plugin steps (networking) + "step.network_plan": { + Type: "step.network_plan", + Plugin: "platform", + ConfigKeys: []string{"network"}, + }, + "step.network_apply": { + Type: "step.network_apply", + Plugin: "platform", + ConfigKeys: []string{"network"}, + }, + "step.network_status": { + Type: "step.network_status", + Plugin: "platform", + ConfigKeys: []string{"network"}, + }, + + // platform plugin steps (api gateway) + "step.apigw_plan": { + Type: "step.apigw_plan", + Plugin: "platform", + ConfigKeys: []string{"gateway"}, + }, + "step.apigw_apply": { + Type: "step.apigw_apply", + Plugin: "platform", + ConfigKeys: []string{"gateway"}, + }, + "step.apigw_status": { + Type: "step.apigw_status", + Plugin: "platform", + ConfigKeys: []string{"gateway"}, + }, + "step.apigw_destroy": { + Type: "step.apigw_destroy", + Plugin: "platform", + ConfigKeys: []string{"gateway"}, + }, + + // platform plugin steps (ecs) + "step.ecs_plan": { + Type: "step.ecs_plan", + Plugin: "platform", + ConfigKeys: []string{"service"}, + }, + "step.ecs_apply": { + Type: "step.ecs_apply", + Plugin: "platform", + ConfigKeys: []string{"service"}, + }, + "step.ecs_status": { + Type: "step.ecs_status", + Plugin: "platform", + ConfigKeys: []string{"service"}, + }, + "step.ecs_destroy": { + Type: "step.ecs_destroy", + Plugin: "platform", + ConfigKeys: []string{"service"}, + }, + + // platform plugin steps (app container) + "step.app_deploy": { + Type: "step.app_deploy", + Plugin: "platform", + ConfigKeys: []string{"app"}, + }, + "step.app_status": { + Type: "step.app_status", + Plugin: "platform", + ConfigKeys: []string{"app"}, + }, + "step.app_rollback": { + Type: "step.app_rollback", + Plugin: "platform", + ConfigKeys: []string{"app"}, + }, + + // secrets plugin steps + "step.secret_rotate": { + Type: "step.secret_rotate", + Plugin: "secrets", + ConfigKeys: []string{"provider", "key", "notify_module"}, + }, } // Include any step types registered dynamically (e.g. from external plugins). for _, t := range schema.KnownModuleTypes() { diff --git a/cmd/wfctl/type_registry_test.go b/cmd/wfctl/type_registry_test.go index 4e2d551a..c9cad621 100644 --- a/cmd/wfctl/type_registry_test.go +++ b/cmd/wfctl/type_registry_test.go @@ -3,6 +3,11 @@ package main import ( "strings" "testing" + + "github.com/GoCodeAlone/workflow/capability" + "github.com/GoCodeAlone/workflow/plugin" + "github.com/GoCodeAlone/workflow/plugins/all" + "github.com/GoCodeAlone/workflow/schema" ) func TestKnownModuleTypesPopulated(t *testing.T) { @@ -77,14 +82,67 @@ func TestKnownStepTypesPopulated(t *testing.T) { t.Fatal("expected known step types to be non-empty") } expected := []string{ + // pipelinesteps plugin "step.validate", "step.transform", "step.json_response", + "step.raw_response", + "step.json_parse", "step.db_query", "step.publish", "step.http_call", "step.cache_get", + "step.auth_validate", + "step.authz_check", + "step.hash", + "step.regex_match", + "step.parallel", + "step.field_reencrypt", + "step.token_revoke", + "step.sandbox_exec", + "step.ui_scaffold", + "step.ui_scaffold_analyze", + // http plugin "step.rate_limit", + // actors plugin + "step.actor_send", + "step.actor_ask", + // secrets plugin + "step.secret_rotate", + // platform plugin + "step.platform_template", + "step.scaling_plan", + "step.scaling_apply", + "step.scaling_status", + "step.scaling_destroy", + "step.iac_plan", + "step.iac_apply", + "step.iac_status", + "step.iac_destroy", + "step.iac_drift_detect", + "step.dns_plan", + "step.dns_apply", + "step.dns_status", + "step.network_plan", + "step.network_apply", + "step.network_status", + "step.apigw_plan", + "step.apigw_apply", + "step.apigw_status", + "step.apigw_destroy", + "step.ecs_plan", + "step.ecs_apply", + "step.ecs_status", + "step.ecs_destroy", + "step.app_deploy", + "step.app_status", + "step.app_rollback", + // cicd plugin + "step.git_clone", + "step.git_commit", + "step.git_push", + "step.git_tag", + "step.git_checkout", } for _, e := range expected { if _, ok := types[e]; !ok { @@ -134,8 +192,34 @@ func TestModuleTypeCount(t *testing.T) { func TestStepTypeCount(t *testing.T) { types := KnownStepTypes() - // We should have a substantial number of step types - if len(types) < 20 { - t.Errorf("expected at least 20 step types, got %d", len(types)) + // We should have a substantial number of step types — all built-in plugin steps. + // This threshold guards against accidental removal; update it when new steps are added. + if len(types) < 120 { + t.Errorf("expected at least 120 step types, got %d — some step types may have been dropped", len(types)) + } +} + +// TestKnownStepTypesCoverAllPlugins ensures KnownStepTypes() is in sync with all step +// types registered by the built-in plugins. Any step type registered by a DefaultPlugin +// that is not listed in KnownStepTypes() will cause this test to fail, preventing silent +// omissions from being introduced in the future. +func TestKnownStepTypesCoverAllPlugins(t *testing.T) { + // Collect all step types registered by DefaultPlugins via the PluginLoader. + capReg := capability.NewRegistry() + schemaReg := schema.NewModuleSchemaRegistry() + loader := plugin.NewPluginLoader(capReg, schemaReg) + for _, p := range all.DefaultPlugins() { + if err := loader.LoadPlugin(p); err != nil { + t.Fatalf("LoadPlugin(%q) error: %v", p.Name(), err) + } + } + + pluginSteps := loader.StepFactories() + known := KnownStepTypes() + + for stepType := range pluginSteps { + if _, ok := known[stepType]; !ok { + t.Errorf("step type %q is registered by a built-in plugin but missing from KnownStepTypes()", stepType) + } } }