package workflow import ( "fmt" "os" "path/filepath" "github.com/ajhahnde/eeco/internal/config" ) // DisabledMarker is the sentinel file name that marks a user-scaffolded // workflow as disabled. Its presence inside the workflow's directory // makes ScriptRun report the workflow as blocked rather than execute // it. The marker is empty; its existence is the entire signal. Stored // in-tree with the workflow it gates, so removing the workflow removes // the marker, and no separate ledger file enters the frozen surface. const DisabledMarker = "disabled" // workflowDir returns the absolute path of a user-scaffolded workflow's // directory inside the workspace. func workflowDir(cfg *config.Config, name string) string { return filepath.Join(cfg.Workspace, "workflows", name) } // WorkflowExists reports whether a user-scaffolded workflow directory // exists for name. Used by the CLI surface to refuse a toggle that // names a workflow eeco does not know about. func WorkflowExists(cfg *config.Config, name string) bool { if cfg == nil { return false } info, err := os.Stat(workflowDir(cfg, name)) return err == nil && info.IsDir() } // IsDisabled reports whether a user-scaffolded workflow is currently // disabled. A missing workflow is not "disabled" — it is absent; callers // that need to distinguish use WorkflowExists first. func IsDisabled(cfg *config.Config, name string) bool { if cfg == nil { return false } _, err := os.Stat(filepath.Join(workflowDir(cfg, name), DisabledMarker)) return err == nil } // Disable marks a user-scaffolded workflow as disabled by creating an // empty sentinel marker file inside its directory. The workflow stays // on disk; ScriptRun reports it as blocked until Enable removes the // marker. A workflow already disabled is a clean no-op. func Disable(cfg *config.Config, name string) error { dir, err := toggleDir(cfg, name) if err != nil { return err } marker := filepath.Join(dir, DisabledMarker) if _, err := os.Stat(marker); err == nil { return nil } return os.WriteFile(marker, nil, 0o644) } // Enable removes the disabled marker from a user-scaffolded workflow's // directory. The workflow becomes runnable again. A workflow that is // not disabled is a clean no-op. func Enable(cfg *config.Config, name string) error { dir, err := toggleDir(cfg, name) if err != nil { return err } marker := filepath.Join(dir, DisabledMarker) if err := os.Remove(marker); err != nil && !os.IsNotExist(err) { return err } return nil } // toggleDir validates inputs shared by Enable / Disable and returns the // workflow's on-disk directory. Refuses nil config, a bad workflow // name, and a workflow whose directory does not exist. func toggleDir(cfg *config.Config, name string) (string, error) { if cfg == nil { return "", fmt.Errorf("nil config") } if !workflowNameRE.MatchString(name) { return "", fmt.Errorf("workflow name %q: must be lower-kebab-case (a-z, 0-9, '-')", name) } dir := workflowDir(cfg, name) info, err := os.Stat(dir) if err != nil || !info.IsDir() { return "", fmt.Errorf("workflow %q not found at %s", name, dir) } return dir, nil }