Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions cmd/wfctl/ci_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,4 @@ func runPreDeploySteps(ctx context.Context, steps []string, verbose bool) error
}

// newCommandContext wraps exec.CommandContext so tests can replace it.
var newCommandContext = func(ctx context.Context, name string, args ...string) *exec.Cmd {
return exec.CommandContext(ctx, name, args...)
}
var newCommandContext = exec.CommandContext //nolint:gosec // G204: subprocess args come from validated user config
23 changes: 0 additions & 23 deletions cmd/wfctl/deploy_providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,29 +470,6 @@ func injectSecrets(ctx context.Context, cfg *config.WorkflowConfig, envName stri
return result, nil
}

// injectSecretsLegacy is the pre-multi-store implementation kept for callers
// that only have a SecretsConfig (not a full WorkflowConfig).
func injectSecretsLegacy(ctx context.Context, secretsCfg *config.SecretsConfig) (map[string]string, error) {
if secretsCfg == nil || len(secretsCfg.Entries) == 0 {
return nil, nil
}

provider, err := newSecretsProvider(secretsCfg.Provider)
if err != nil {
return nil, fmt.Errorf("secrets provider: %w", err)
}

result := make(map[string]string, len(secretsCfg.Entries))
for _, entry := range secretsCfg.Entries {
val, err := provider.Get(ctx, entry.Name)
if err != nil {
return nil, fmt.Errorf("fetch secret %q: %w", entry.Name, err)
}
result[entry.Name] = val
}
return result, nil
}

// cmp returns a if non-empty, otherwise b. Mirrors cmp.Or for strings.
func cmp(a, b string) string {
if a != "" {
Expand Down
31 changes: 15 additions & 16 deletions cmd/wfctl/dev_compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ const devComposeFileName = "docker-compose.dev.yml"

// moduleToDockerImage maps workflow module types to Docker images.
var moduleToDockerImage = map[string]string{
"database.postgres": "postgres:16",
"database.workflow": "postgres:16",
"nosql.redis": "redis:7-alpine",
"cache.redis": "redis:7-alpine",
"messaging.nats": "nats:latest",
"messaging.kafka": "confluentinc/cp-kafka:latest",
"database.postgres": "postgres:16",
"database.workflow": "postgres:16",
"nosql.redis": "redis:7-alpine",
"cache.redis": "redis:7-alpine",
"messaging.nats": "nats:latest",
"messaging.kafka": "confluentinc/cp-kafka:latest",
"messaging.rabbitmq": "rabbitmq:3-management-alpine",
}

Expand Down Expand Up @@ -58,13 +58,13 @@ var infraModuleVolumeMounts = map[string]string{

// devComposeService represents a docker-compose service entry for dev mode.
type devComposeService struct {
Image string `yaml:"image,omitempty"`
Build *devComposeBuild `yaml:"build,omitempty"`
Ports []string `yaml:"ports,omitempty"`
Environment map[string]string `yaml:"environment,omitempty"`
Volumes []string `yaml:"volumes,omitempty"`
DependsOn []string `yaml:"depends_on,omitempty"`
Restart string `yaml:"restart,omitempty"`
Image string `yaml:"image,omitempty"`
Build *devComposeBuild `yaml:"build,omitempty"`
Ports []string `yaml:"ports,omitempty"`
Environment map[string]string `yaml:"environment,omitempty"`
Volumes []string `yaml:"volumes,omitempty"`
DependsOn []string `yaml:"depends_on,omitempty"`
Restart string `yaml:"restart,omitempty"`
Healthcheck *devComposeHealthcheck `yaml:"healthcheck,omitempty"`
}

Expand Down Expand Up @@ -254,8 +254,7 @@ func moduleTypeToDNS(modType string) string {
parts := strings.Split(modType, ".")
last := parts[len(parts)-1]
// Shorten well-known names.
switch last {
case "workflow":
if last == "workflow" {
return "postgres"
}
return last
Expand All @@ -271,7 +270,7 @@ func runDevCompose(cfg *config.WorkflowConfig, cfgPath string, verbose bool) err
// Write to the same directory as the config file.
outDir := filepath.Dir(cfgPath)
outPath := filepath.Join(outDir, devComposeFileName)
if err := os.WriteFile(outPath, []byte(composeYAML), 0o644); err != nil {
if err := os.WriteFile(outPath, []byte(composeYAML), 0o600); err != nil {
return fmt.Errorf("write %s: %w", outPath, err)
}
fmt.Printf("Generated %s\n", outPath)
Expand Down
2 changes: 1 addition & 1 deletion cmd/wfctl/dev_k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func runDevK8s(cfg *config.WorkflowConfig, verbose bool) error {

// 5. Write manifests to a temp file and apply.
const manifestFile = "dev-manifests.yaml"
if err := os.WriteFile(manifestFile, []byte(manifests), 0o644); err != nil {
if err := os.WriteFile(manifestFile, []byte(manifests), 0o600); err != nil {
return fmt.Errorf("write manifests: %w", err)
}
defer os.Remove(manifestFile) //nolint:errcheck
Expand Down
4 changes: 2 additions & 2 deletions cmd/wfctl/dev_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func runDevProcess(cfg *config.WorkflowConfig, verbose bool) error {
return fmt.Errorf("generate infra compose: %w", err)
}
const infraComposeFile = "docker-compose.dev-infra.yml"
if err := os.WriteFile(infraComposeFile, []byte(composeYAML), 0o644); err != nil {
if err := os.WriteFile(infraComposeFile, []byte(composeYAML), 0o600); err != nil {
return fmt.Errorf("write infra compose: %w", err)
}

Expand Down Expand Up @@ -309,7 +309,7 @@ func collectWatchDirs(root string) []string {
seen := map[string]bool{}
_ = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
return nil //nolint:nilerr // intentionally skip unreadable paths in watch dirs
}
if !info.IsDir() {
return nil
Expand Down
46 changes: 3 additions & 43 deletions cmd/wfctl/infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/GoCodeAlone/workflow/interfaces"
"github.com/GoCodeAlone/workflow/platform"
"github.com/GoCodeAlone/workflow/secrets"
"gopkg.in/yaml.v3"
"os"
"path/filepath"
"strings"
)

func runInfra(args []string) error {
Expand Down Expand Up @@ -593,46 +593,6 @@ func extractSources(m map[string]any, singular, plural string) string {
return ""
}

// formatFirewallRules produces a compact summary of firewall rule config (legacy, single-line).
func formatFirewallRules(v any) string {
switch rules := v.(type) {
case []any:
if len(rules) == 0 {
return ""
}
// Summarise first rule.
first, ok := rules[0].(map[string]any)
if !ok {
return fmt.Sprintf("%d rule(s)", len(rules))
}
proto, _ := first["protocol"].(string)
ports, _ := first["ports"].(string)
src, _ := first["source"].(string)
dst, _ := first["destination"].(string)
var parts []string
if proto != "" {
parts = append(parts, strings.ToUpper(proto))
}
if ports != "" {
parts = append(parts, ports)
}
if src != "" {
parts = append(parts, "from "+src)
}
if dst != "" {
parts = append(parts, "to "+dst)
}
summary := strings.Join(parts, " ")
if len(rules) > 1 {
summary += fmt.Sprintf(" (+%d more)", len(rules)-1)
}
return summary
case string:
return rules
}
return ""
}

func actionSymbol(action string) string {
switch action {
case "create":
Expand Down
7 changes: 5 additions & 2 deletions cmd/wfctl/infra_bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ func runInfraBootstrap(args []string) error {
// backing infrastructure (e.g. a DO Spaces bucket) if it does not already exist.
func bootstrapStateBackend(ctx context.Context, cfgFile string) error {
iacStates, _, _, err := discoverInfraModules(cfgFile)
if err != nil || len(iacStates) == 0 {
return nil // no state module configured — nothing to bootstrap
if err != nil {
return fmt.Errorf("discover infra modules: %w", err)
}
if len(iacStates) == 0 {
return nil
}
Comment on lines 58 to 64

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bootstrapStateBackend currently returns nil when discoverInfraModules returns an error (if err != nil || len(iacStates) == 0). That suppresses potentially actionable failures (bad config path, YAML parse errors, etc.) and could leave state backends unbootstrapped without telling the user. Consider handling the cases separately: return the error when err != nil, and only return nil when there are simply no iac.state modules.

Copilot uses AI. Check for mistakes.
m := iacStates[0]
backend, _ := m.Config["backend"].(string)
Expand Down
2 changes: 1 addition & 1 deletion cmd/wfctl/infra_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func resolveSecretsProvider(cfg *SecretsConfig) (secrets.Provider, error) {
repo, _ := c["repo"].(string)
tokenVar, _ := c["token_env"].(string)
if tokenVar == "" {
tokenVar = "GITHUB_TOKEN"
tokenVar = "GITHUB_TOKEN" //nolint:gosec // G101: this is an env var name, not a credential
}
return secrets.NewGitHubSecretsProvider(repo, tokenVar)

Expand Down
6 changes: 3 additions & 3 deletions cmd/wfctl/migrate_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Options:
return nil
}

if err := os.WriteFile(dest, []byte(converted), 0o644); err != nil {
if err := os.WriteFile(dest, []byte(converted), 0o600); err != nil {
return fmt.Errorf("write %s: %w", dest, err)
}
fmt.Printf("Wrote %s: %d expression(s) converted, %d marked as TODO\n",
Expand Down Expand Up @@ -191,12 +191,12 @@ func convertGoTemplateExpr(inner string) (string, bool) {

// .steps.stepName.field → steps["stepName"]["field"]
if m := stepsDotRe.FindStringSubmatch(inner); m != nil {
return fmt.Sprintf(`steps["%s"]["%s"]`, m[1], m[2]), true
return fmt.Sprintf(`steps[%q][%q]`, m[1], m[2]), true
}

// index .steps "name" "field" → steps["name"]["field"]
if m := indexStepsRe.FindStringSubmatch(inner); m != nil {
return fmt.Sprintf(`steps["%s"]["%s"]`, m[1], m[2]), true
return fmt.Sprintf(`steps[%q][%q]`, m[1], m[2]), true
}

// eq .field "value" → field == "value"
Expand Down
20 changes: 5 additions & 15 deletions cmd/wfctl/plugin_deps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@ import (
"testing"
)

// makeManifestJSON returns a JSON-encoded RegistryManifest for use in test servers.
func makeManifestJSON(t *testing.T, m RegistryManifest) []byte {
t.Helper()
data, err := json.Marshal(m)
if err != nil {
t.Fatalf("marshal manifest: %v", err)
}
return data
}

// TestCompareSemverConstraints verifies semver comparison used in version constraint checks.
func TestCompareSemverConstraints(t *testing.T) {
tests := []struct {
Expand Down Expand Up @@ -47,10 +37,10 @@ func TestCompareSemverConstraints(t *testing.T) {
// TestCheckVersionConstraints verifies min/max version enforcement.
func TestCheckVersionConstraints(t *testing.T) {
tests := []struct {
name string
dep PluginDependency
version string
wantErr bool
name string
dep PluginDependency
version string
wantErr bool
errContains string
}{
{
Expand Down Expand Up @@ -287,7 +277,7 @@ func TestPluginInstall_ResolveDependencies(t *testing.T) {
Author: "test", Description: "bento stream processor",
Type: "external", Tier: "community", License: "MIT",
Downloads: []PluginDownload{
{OS: "linux", Arch: "amd64", URL: ""}, // filled below
{OS: "linux", Arch: "amd64", URL: ""}, // filled below
{OS: "darwin", Arch: "amd64", URL: ""},
{OS: "darwin", Arch: "arm64", URL: ""},
},
Expand Down
3 changes: 2 additions & 1 deletion cmd/wfctl/plugin_infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ func DetectPluginInfraNeeds(cfg *config.WorkflowConfig, manifests map[string]*co
var needs []config.InfraRequirement

addRequirements := func(reqs []config.InfraRequirement) {
for _, req := range reqs {
for i := range reqs {
req := reqs[i]
key := req.Type + ":" + req.Name
if seen[key] {
continue
Expand Down
15 changes: 10 additions & 5 deletions cmd/wfctl/secrets_detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,12 @@ func secretStateLabel(state SecretState) string {
func loadWorkflowConfigForSecrets(configFile string) (*config.WorkflowConfig, error) {
data, err := os.ReadFile(configFile)
if err != nil {
return &config.WorkflowConfig{
Secrets: &config.SecretsConfig{Provider: "env"},
}, nil
if os.IsNotExist(err) {
return &config.WorkflowConfig{ //nolint:nilerr // gracefully fall back when file is absent
Secrets: &config.SecretsConfig{Provider: "env"},
}, nil
}
return nil, fmt.Errorf("read config: %w", err)
}
var cfg config.WorkflowConfig
if err := yaml.Unmarshal(data, &cfg); err != nil {
Expand Down Expand Up @@ -420,8 +423,10 @@ func runSecretsSync(args []string) error {
func loadSecretsConfig(configFile string) (*config.SecretsConfig, error) {
data, err := os.ReadFile(configFile)
if err != nil {
// If the file doesn't exist, return a default env provider config.
return &config.SecretsConfig{Provider: "env"}, nil
if os.IsNotExist(err) {
return &config.SecretsConfig{Provider: "env"}, nil //nolint:nilerr // gracefully fall back when file is absent
}
return nil, fmt.Errorf("read config %q: %w", configFile, err)
}
var cfg config.WorkflowConfig
if err := yaml.Unmarshal(data, &cfg); err != nil {
Expand Down
13 changes: 7 additions & 6 deletions cmd/wfctl/security_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,12 @@ func runSecurityGenerateNetworkPolicies(args []string) error {
return nil
}

if err := os.MkdirAll(*outputDir, 0755); err != nil {
if err := os.MkdirAll(*outputDir, 0750); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}

for name, policy := range policies {
for name := range policies {
policy := policies[name]
outPath := filepath.Join(*outputDir, fmt.Sprintf("netpol-%s.yaml", name))
data, err := yaml.Marshal(policy)
if err != nil {
Expand All @@ -229,10 +230,10 @@ func runSecurityGenerateNetworkPolicies(args []string) error {

// k8sNetworkPolicy is a minimal Kubernetes NetworkPolicy for YAML generation.
type k8sNetworkPolicy struct {
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Metadata k8sMetadata `yaml:"metadata"`
Spec k8sNetworkPolicySpec `yaml:"spec"`
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
Metadata k8sMetadata `yaml:"metadata"`
Spec k8sNetworkPolicySpec `yaml:"spec"`
}

type k8sMetadata struct {
Expand Down
8 changes: 8 additions & 0 deletions cmd/wfctl/type_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,14 @@ func KnownModuleTypes() map[string]ModuleTypeInfo {
ConfigKeys: []string{"system", "workers", "handler"},
},

// mcp plugin
"mcp.registry": {
Type: "mcp.registry",
Plugin: "mcp",
Stateful: false,
ConfigKeys: []string{"log_on_init", "expose_admin_api", "audit_tool_calls"},
},

// scanner plugin
"security.scanner": {
Type: "security.scanner",
Expand Down
Loading
Loading