Skip to content

Commit e5a5b48

Browse files
committed
Add NodeStateCallback to core execution and improve workflow types
- Add NodeStateCallback to ExecutionState and RunOpts for tracking node start/finish events during graph execution - Add IntOrString type for YAML fields that accept integers or expressions (timeout-minutes, max-parallel) - Refactor ActfileSchema to use getter/setter instead of exported variable - Switch to SetGlobalNormalizationFunc for flag normalization
1 parent 292f8b5 commit e5a5b48

6 files changed

Lines changed: 61 additions & 9 deletions

File tree

cmd/cmd_validate.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,16 @@ import (
1414
"go.yaml.in/yaml/v4"
1515
)
1616

17-
// ActfileSchema holds the embedded JSON schema bytes, set from main.
18-
var ActfileSchema []byte
17+
// holds the embedded JSON schema bytes, set via SetActfileSchema.
18+
var actfileSchema []byte
19+
20+
func GetActfileSchema() []byte {
21+
return actfileSchema
22+
}
23+
24+
func SetActfileSchema(schema []byte) {
25+
actfileSchema = schema
26+
}
1927

2028
var cmdValidate = &cobra.Command{
2129
Use: "validate [graph-file]",
@@ -44,12 +52,12 @@ var cmdValidate = &cobra.Command{
4452
}
4553

4654
func ValidateSchema(data any) error {
47-
if len(ActfileSchema) == 0 {
55+
if len(actfileSchema) == 0 {
4856
return fmt.Errorf("actfile schema not loaded")
4957
}
5058

5159
var schemaObj any
52-
if err := json.Unmarshal(ActfileSchema, &schemaObj); err != nil {
60+
if err := json.Unmarshal(actfileSchema, &schemaObj); err != nil {
5361
return fmt.Errorf("failed to parse schema JSON: %w", err)
5462
}
5563

@@ -170,7 +178,7 @@ var cmdSchema = &cobra.Command{
170178
Long: `Prints the JSON schema used to validate Actionforge graph (.act) files.`,
171179
Args: cobra.NoArgs,
172180
Run: func(cmd *cobra.Command, args []string) {
173-
fmt.Println(string(ActfileSchema))
181+
fmt.Println(string(actfileSchema))
174182
},
175183
}
176184

core/context.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ type ExecutionState struct {
170170

171171
DebugCallback DebugCallback `json:"-"`
172172

173+
// NodeStateCallback is invoked when a node starts or finishes execution.
174+
// The started parameter is true when the node begins executing, false when done.
175+
NodeStateCallback func(nodeID, nodeName string, started bool) `json:"-"`
176+
173177
// PendingConcurrencyLocks tracks concurrency locks that are held during
174178
// ExecuteImpl. The key is node id → *sync.Mutex. Released when the
175179
// node calls Execute to dispatch downstream node, or as a fallback when
@@ -252,6 +256,7 @@ func (c *ExecutionState) PushNewExecutionState(parentNode NodeBaseInterface) *Ex
252256

253257
Visited: visited,
254258
DebugCallback: c.DebugCallback,
259+
NodeStateCallback: c.NodeStateCallback,
255260
PendingConcurrencyLocks: &sync.Map{},
256261
}
257262

core/executions.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,18 @@ func (n *Executions) Execute(outputPort OutputId, ec *ExecutionState, err error)
9999
}()
100100
}
101101

102+
nodeID := dest.DstNode.GetId()
103+
nodeName := dest.DstNode.GetName()
104+
if ec.NodeStateCallback != nil {
105+
ec.NodeStateCallback(nodeID, nodeName, true)
106+
}
107+
102108
err = dest.DstNode.ExecuteImpl(ec, dest.Port, err)
109+
110+
if ec.NodeStateCallback != nil {
111+
ec.NodeStateCallback(nodeID, nodeName, false)
112+
}
113+
103114
if err != nil {
104115
return err
105116
}

core/graph.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ type RunOpts struct {
3333
Args []string
3434
LocalGhServer bool
3535
VS *ValidationState
36+
37+
// NodeStateCallback is called when a node starts or finishes execution.
38+
NodeStateCallback func(nodeID, nodeName string, started bool)
3639
}
3740

3841
type ActionGraph struct {
@@ -566,6 +569,8 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
566569
needsTracker.toSimpleMap(),
567570
)
568571

572+
c.NodeStateCallback = opts.NodeStateCallback
573+
569574
if isBaseNode {
570575
c.PushNodeVisit(entryNode, true)
571576
}

github/workflow.yml/types.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ type Job struct {
7878
Env map[string]string `yaml:"env,omitempty"`
7979
Defaults Defaults `yaml:"defaults,omitempty"`
8080
Steps []Step `yaml:"steps,omitempty"`
81-
TimeoutMinutes int `yaml:"timeout-minutes,omitempty"`
81+
TimeoutMinutes IntOrString `yaml:"timeout-minutes,omitempty"`
8282
ContinueOnError BoolOrString `yaml:"continue-on-error,omitempty"`
8383
Strategy *Strategy `yaml:"strategy,omitempty"`
8484
Container Container `yaml:"container,omitempty"`
@@ -101,7 +101,7 @@ type Step struct {
101101
With map[string]interface{} `yaml:"with,omitempty"`
102102
Env map[string]string `yaml:"env,omitempty"`
103103
ContinueOnError BoolOrString `yaml:"continue-on-error,omitempty"`
104-
TimeoutMinutes int `yaml:"timeout-minutes,omitempty"`
104+
TimeoutMinutes IntOrString `yaml:"timeout-minutes,omitempty"`
105105
}
106106

107107
// ----------------------------------------------------------------------------
@@ -250,6 +250,29 @@ func (b *BoolOrString) UnmarshalYAML(value *yaml.Node) error {
250250
return nil
251251
}
252252

253+
// IntOrString handles fields that can be an integer or an expression string.
254+
// Example: timeout-minutes: ${{ inputs.timeout }}
255+
type IntOrString struct {
256+
Value int
257+
Expression string
258+
IsInt bool
259+
}
260+
261+
func (i *IntOrString) UnmarshalYAML(value *yaml.Node) error {
262+
if value.Kind == yaml.ScalarNode {
263+
var intVal int
264+
if err := value.Decode(&intVal); err == nil {
265+
i.Value = intVal
266+
i.IsInt = true
267+
return nil
268+
}
269+
i.Expression = value.Value
270+
i.IsInt = false
271+
return nil
272+
}
273+
return nil
274+
}
275+
253276
// Permissions handles:
254277
// - String: "read-all", "write-all"
255278
// - Map: { contents: read, ... }
@@ -287,7 +310,7 @@ func (e *Environment) UnmarshalYAML(value *yaml.Node) error {
287310
type Strategy struct {
288311
Matrix Matrix `yaml:"matrix"`
289312
FailFast interface{} `yaml:"fail-fast,omitempty"`
290-
MaxParallel int `yaml:"max-parallel,omitempty"`
313+
MaxParallel IntOrString `yaml:"max-parallel,omitempty"`
291314
}
292315

293316
// Matrix can be an expression string or a map of configs

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ func main() {
2828
return
2929
}
3030

31-
cmd.ActfileSchema = actfileSchema
31+
cmd.SetActfileSchema(actfileSchema)
3232
cmd.Execute()
3333
}

0 commit comments

Comments
 (0)