gitlink-cli/shortcuts/workflow/state.go

82 lines
1.9 KiB
Go

package workflow
import (
"crypto/md5"
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
"github.com/gitlink-org/gitlink-cli/internal/config"
)
// LoadState reads the persisted workflow state from disk.
func LoadState(name string) (*WorkflowState, error) {
path := statePath(name)
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return &WorkflowState{
Workflow: name,
Snapshots: make(map[string]string),
}, nil
}
return nil, err
}
var s WorkflowState
if err := json.Unmarshal(data, &s); err != nil {
return nil, fmt.Errorf("parse state file %s: %w", path, err)
}
if s.Snapshots == nil {
s.Snapshots = make(map[string]string)
}
return &s, nil
}
// Save persists the workflow state to disk.
func (s *WorkflowState) Save() error {
s.LastRun = time.Now().Format(time.RFC3339)
path := statePath(s.Workflow)
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, 0700); err != nil {
return err
}
data, err := json.MarshalIndent(s, "", " ")
if err != nil {
return err
}
return os.WriteFile(path, data, 0600)
}
// Diff compares current step results against stored snapshots.
// Returns the names of steps whose data changed since the last run.
func (s *WorkflowState) Diff(results []StepResult) []string {
changed := []string{}
for _, sr := range results {
if !sr.OK || sr.Data == nil {
continue
}
hash := hashData(sr.Data)
if prev, ok := s.Snapshots[sr.Step]; ok && prev != hash {
changed = append(changed, sr.Step)
}
s.Snapshots[sr.Step] = hash
}
return changed
}
// hashData computes an MD5 hash of the JSON-encoded data.
func hashData(data interface{}) string {
b, err := json.Marshal(data)
if err != nil {
return ""
}
return fmt.Sprintf("%x", md5.Sum(b))
}
// statePath returns the file path for a workflow's state file.
func statePath(name string) string {
return filepath.Join(config.ConfigDir(), fmt.Sprintf("workflow-%s-state.json", name))
}