forked from chroe/gitlink-cli
82 lines
1.9 KiB
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))
|
|
}
|