ajhahn.de
← eeco
Go 95 lines
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
}