Skip to content

Commit f9ccdc3

Browse files
Merge pull request #13 from actionforge/bugfix/github-environment
Fix logic for determining when to simulate GitHub Actions environment
2 parents 3b541fa + 3295aad commit f9ccdc3

File tree

109 files changed

+209
-178
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+209
-178
lines changed

cmd/cmd_validate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func validateGraph(filePath string) error {
5454
return err
5555
}
5656

57-
_, errs := core.LoadGraph(graphYaml, nil, "", true)
57+
_, errs := core.LoadGraph(graphYaml, nil, "", true, core.RunOpts{})
5858

5959
if len(errs) > 0 {
6060
fmt.Printf("\n❌ Validation failed with %d error(s):\n", len(errs))

core/base.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ func LogDebugInfoForGh(t GetNameIdInterface) {
287287
)
288288
}
289289

290-
type nodeFactoryFunc func(ctx any, parent NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (NodeBaseInterface, []error)
290+
type nodeFactoryFunc func(ctx any, parent NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts RunOpts) (NodeBaseInterface, []error)
291291

292292
var registries = make(map[string]NodeTypeDefinitionFull)
293293

@@ -585,13 +585,13 @@ func RegisterNodeFactory(nodeDefStr string, fn nodeFactoryFunc) error {
585585
return nil
586586
}
587587

588-
func NewGhActionNode(nodeType string, parent NodeBaseInterface, parentId string, validate bool) (NodeBaseInterface, []error) {
588+
func NewGhActionNode(nodeType string, parent NodeBaseInterface, parentId string, validate bool, opts RunOpts) (NodeBaseInterface, []error) {
589589
factoryEntry, exists := registries["core/gh-action@v1"]
590590
if !exists {
591591
return nil, []error{CreateErr(nil, nil, "node type '%v' not registered", nodeType)}
592592
}
593593

594-
node, errs := factoryEntry.FactoryFn(nodeType, parent, parentId, nil, validate)
594+
node, errs := factoryEntry.FactoryFn(nodeType, parent, parentId, nil, validate, opts)
595595
if len(errs) > 0 {
596596
return nil, errs
597597
}
@@ -601,7 +601,7 @@ func NewGhActionNode(nodeType string, parent NodeBaseInterface, parentId string,
601601
return node, nil
602602
}
603603

604-
func NewNodeInstance(nodeType string, parent NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (NodeBaseInterface, []error) {
604+
func NewNodeInstance(nodeType string, parent NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts RunOpts) (NodeBaseInterface, []error) {
605605
var (
606606
node NodeBaseInterface
607607
errs []error
@@ -616,7 +616,7 @@ func NewNodeInstance(nodeType string, parent NodeBaseInterface, parentId string,
616616
factoryEntry, exists := registries[nodeType]
617617
if exists {
618618
// Pass 'validate' to the factory function
619-
node, errs = factoryEntry.FactoryFn(nil, parent, parentId, nodeDef, validate)
619+
node, errs = factoryEntry.FactoryFn(nil, parent, parentId, nodeDef, validate, opts)
620620
if len(errs) > 0 {
621621
// If the factory failed to produce a node (or found errors), return them.
622622
return nil, errs

core/graph.go

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
276276
return CreateErr(nil, err, "failed to load yaml")
277277
}
278278

279-
ag, errs := LoadGraph(graphYaml, nil, "", false)
279+
ag, errs := LoadGraph(graphYaml, nil, "", false, opts)
280280
if len(errs) > 0 {
281281
return CreateErr(nil, errs[0], "failed to load graph")
282282
}
@@ -287,8 +287,23 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
287287
}
288288

289289
entryNode, isBaseNode := entry.(NodeBaseInterface)
290-
isGitHubAction := os.Getenv("GITHUB_ACTIONS") == "true"
291-
isGitHubWorkflow := isBaseNode && entryNode.GetNodeTypeId() == "core/gh-start@v1"
290+
291+
// isGitHubActions: Determines if this run should behave as a GitHub Action.
292+
// True when either running on actual GitHub Actions (system env), or an external
293+
// caller (e.g., web app) explicitly requests GitHub Actions behavior via OverrideEnv.
294+
// This affects input variable handling, context loading, and other GitHub-specific behavior.
295+
//
296+
// **Important** we haven't loaded the config file yet, so we can only look at overriden envs,
297+
// .env (already set in os.GetEnv) or shell.
298+
isGitHubActions := opts.OverrideEnv["GITHUB_ACTIONS"] == "true" || os.Getenv("GITHUB_ACTIONS") == "true" || entryNode.GetNodeTypeId() == "core/gh-start@v1"
299+
300+
// mimickGitHubEnv: Determines if we need to set up a simulated GitHub environment. The easiest
301+
// approach for now is to just check a bunch of env vars. The user may have set one or the other
302+
// (through .env or shell) but unlikely all of them but they are by a real GitHub Actions runner.
303+
mimickGitHubEnv := isGitHubActions && os.Getenv("GITHUB_RUN_ID") == "" &&
304+
os.Getenv("RUNNER_TEMP") == "" &&
305+
os.Getenv("GITHUB_API_URL") == "" &&
306+
os.Getenv("GITHUB_RETENTION_DAYS") == ""
292307

293308
// Initialize trackers with their respective categories
294309
envTracker := newValueMap[string]("env")
@@ -301,19 +316,21 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
301316
if opts.ConfigFile != "" {
302317
if _, err := os.Stat(opts.ConfigFile); err == nil {
303318
localConfig, err := utils.LoadConfig(opts.ConfigFile)
304-
if err == nil {
305-
configName := filepath.Base(opts.ConfigFile)
306-
envTracker.set(localConfig.Env, configName, true, false)
307-
inputTracker.set(localConfig.Inputs, configName, true, false)
308-
secretTracker.set(localConfig.Secrets, configName, true, true)
319+
if err != nil {
320+
return CreateErr(nil, err, "failed to load config file")
309321
}
322+
323+
configName := filepath.Base(opts.ConfigFile)
324+
envTracker.set(localConfig.Env, configName, true, false)
325+
inputTracker.set(localConfig.Inputs, configName, true, false)
326+
secretTracker.set(localConfig.Secrets, configName, true, true)
310327
}
311328
}
312329

313330
rawEnv := utils.GetAllEnvMapCopy()
314331

315332
// normalize all inputs/secrets with ACT_* iif we're in GitHub
316-
if isGitHubWorkflow {
333+
if isGitHubActions {
317334
prefixedRawEnv := make(map[string]utils.EnvKV)
318335
for k, v := range rawEnv {
319336
prefixedKey := k
@@ -366,15 +383,15 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
366383
secretTracker.setSingle(key, v.Value, fmt.Sprintf("%s (%s)", source, k), true, true)
367384

368385
// GitHub specifics
369-
case isGitHubWorkflow && k == "ACT_INPUT_MATRIX":
386+
case isGitHubActions && k == "ACT_INPUT_MATRIX":
370387
if m, err := decodeJsonFromEnvValue[any](v.Value); err == nil {
371388
matrixTracker.set(m, source, true, true)
372389
}
373-
case isGitHubWorkflow && k == "ACT_INPUT_NEEDS":
390+
case isGitHubActions && k == "ACT_INPUT_NEEDS":
374391
if m, err := decodeJsonFromEnvValue[any](v.Value); err == nil {
375392
needsTracker.set(m, source, true, true)
376393
}
377-
case isGitHubWorkflow && k == "ACT_INPUT_TOKEN":
394+
case isGitHubActions && k == "ACT_INPUT_TOKEN":
378395
secretTracker.setSingle("GITHUB_TOKEN", v.Value, source, true, true)
379396

380397
default:
@@ -413,7 +430,7 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
413430
}()
414431
}
415432

416-
if !isGitHubAction && isGitHubWorkflow {
433+
if mimickGitHubEnv {
417434
// If we are running a github actions workflow, then mimic a GitHub Actions environment
418435
// But only do is if we are NOT already in GitHub Actions
419436
err = SetupGitHubActionsEnv(finalEnv)
@@ -439,7 +456,7 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
439456
// construct the `github` context
440457
var ghContext map[string]any
441458
var errGh error
442-
if isGitHubWorkflow {
459+
if isGitHubActions {
443460
ghContext, errGh = LoadGitHubContext(finalEnv, finalInputs, finalSecrets)
444461
if errGh != nil {
445462
return CreateErr(nil, errGh, "failed to load github context")
@@ -450,7 +467,7 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
450467
ctx,
451468
&ag,
452469
graphName,
453-
isGitHubWorkflow,
470+
isGitHubActions,
454471
debugCb,
455472
finalEnv,
456473
finalInputs,
@@ -467,7 +484,7 @@ func RunGraph(ctx context.Context, graphName string, graphContent []byte, opts R
467484
return entry.ExecuteEntry(c, nil, opts.Args)
468485
}
469486

470-
func LoadGraph(graphYaml map[string]any, parent NodeBaseInterface, parentId string, validate bool) (ActionGraph, []error) {
487+
func LoadGraph(graphYaml map[string]any, parent NodeBaseInterface, parentId string, validate bool, opts RunOpts) (ActionGraph, []error) {
471488

472489
var (
473490
collectedErrors []error
@@ -492,7 +509,7 @@ func LoadGraph(graphYaml map[string]any, parent NodeBaseInterface, parentId stri
492509
collectedErrors = append(collectedErrors, err)
493510
}
494511

495-
err = LoadNodes(&ag, parent, parentId, graphYaml, validate, &collectedErrors)
512+
err = LoadNodes(&ag, parent, parentId, graphYaml, validate, &collectedErrors, opts)
496513
if err != nil && !validate {
497514
return ActionGraph{}, []error{err}
498515
}
@@ -570,14 +587,14 @@ func anyToPortDefinition[T any](o any) (T, error) {
570587
return ret, err
571588
}
572589

573-
func LoadNodes(ag *ActionGraph, parent NodeBaseInterface, parentId string, nodesYaml map[string]any, validate bool, errs *[]error) error {
590+
func LoadNodes(ag *ActionGraph, parent NodeBaseInterface, parentId string, nodesYaml map[string]any, validate bool, errs *[]error, opts RunOpts) error {
574591
nodesList, err := utils.GetTypedPropertyByPath[[]any](nodesYaml, "nodes")
575592
if err != nil {
576593
return collectOrReturn(err, validate, errs)
577594
}
578595

579596
for _, nodeData := range nodesList {
580-
n, id, err := LoadNode(parent, parentId, nodeData, validate, errs)
597+
n, id, err := LoadNode(parent, parentId, nodeData, validate, errs, opts)
581598
if err != nil {
582599
return err
583600
}
@@ -592,7 +609,7 @@ func LoadNodes(ag *ActionGraph, parent NodeBaseInterface, parentId string, nodes
592609
return nil
593610
}
594611

595-
func LoadNode(parent NodeBaseInterface, parentId string, nodeData any, validate bool, errs *[]error) (NodeBaseInterface, string, error) {
612+
func LoadNode(parent NodeBaseInterface, parentId string, nodeData any, validate bool, errs *[]error, opts RunOpts) (NodeBaseInterface, string, error) {
596613
nodeI, ok := nodeData.(map[string]any)
597614
if !ok {
598615
err := CreateErr(nil, nil, "node is not a map")
@@ -635,9 +652,9 @@ func LoadNode(parent NodeBaseInterface, parentId string, nodeData any, validate
635652
fullPath = parentId + "/" + id
636653
}
637654
if strings.HasPrefix(nodeType, "github.com/") {
638-
n, factoryErrs = NewGhActionNode(nodeType, parent, fullPath, validate)
655+
n, factoryErrs = NewGhActionNode(nodeType, parent, fullPath, validate, opts)
639656
} else {
640-
n, factoryErrs = NewNodeInstance(nodeType, parent, fullPath, nodeI, validate)
657+
n, factoryErrs = NewNodeInstance(nodeType, parent, fullPath, nodeI, validate, opts)
641658
}
642659

643660
if len(factoryErrs) > 0 {

nodes/affirm@v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (n *AffirmNode) OutputValueById(c *core.ExecutionState, outputId core.Outpu
2626
}
2727

2828
func init() {
29-
err := core.RegisterNodeFactory(affirmDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (core.NodeBaseInterface, []error) {
29+
err := core.RegisterNodeFactory(affirmDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts core.RunOpts) (core.NodeBaseInterface, []error) {
3030
return &AffirmNode{}, nil
3131
})
3232
if err != nil {

nodes/array-add@v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (n *ArrayAddNode) ExecuteImpl(c *core.ExecutionState, inputId core.InputId,
6060
}
6161

6262
func init() {
63-
err := core.RegisterNodeFactory(arrayAddNodeDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (core.NodeBaseInterface, []error) {
63+
err := core.RegisterNodeFactory(arrayAddNodeDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts core.RunOpts) (core.NodeBaseInterface, []error) {
6464
return &ArrayAddNode{}, nil
6565
})
6666
if err != nil {

nodes/array-append@v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (n *ArrayAppendNode) ExecuteImpl(c *core.ExecutionState, inputId core.Input
5151
}
5252

5353
func init() {
54-
err := core.RegisterNodeFactory(arrayAppendNodeDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (core.NodeBaseInterface, []error) {
54+
err := core.RegisterNodeFactory(arrayAppendNodeDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts core.RunOpts) (core.NodeBaseInterface, []error) {
5555
return &ArrayAppendNode{}, nil
5656
})
5757
if err != nil {

nodes/array-get@v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (n *ArrayGet) OutputValueById(c *core.ExecutionState, outputId core.OutputI
5151
}
5252

5353
func init() {
54-
err := core.RegisterNodeFactory(arrayGetDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (core.NodeBaseInterface, []error) {
54+
err := core.RegisterNodeFactory(arrayGetDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts core.RunOpts) (core.NodeBaseInterface, []error) {
5555
return &ArrayGet{}, nil
5656
})
5757
if err != nil {

nodes/bool@v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func init() {
8484
}
8585

8686
for _, op := range ops {
87-
err := core.RegisterNodeFactory(op.definition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (core.NodeBaseInterface, []error) {
87+
err := core.RegisterNodeFactory(op.definition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts core.RunOpts) (core.NodeBaseInterface, []error) {
8888
return &BoolNode{
8989
op: op.op,
9090
opStr: op.opStr,

nodes/branch@v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (n *BranchNode) ExecuteImpl(c *core.ExecutionState, inputId core.InputId, p
3838
}
3939

4040
func init() {
41-
err := core.RegisterNodeFactory(ifDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (core.NodeBaseInterface, []error) {
41+
err := core.RegisterNodeFactory(ifDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts core.RunOpts) (core.NodeBaseInterface, []error) {
4242
return &BranchNode{}, nil
4343
})
4444
if err != nil {

nodes/comment@v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type CommentNode struct {
1515
}
1616

1717
func init() {
18-
err := core.RegisterNodeFactory(commentNodeDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool) (core.NodeBaseInterface, []error) {
18+
err := core.RegisterNodeFactory(commentNodeDefinition, func(ctx any, parent core.NodeBaseInterface, parentId string, nodeDef map[string]any, validate bool, opts core.RunOpts) (core.NodeBaseInterface, []error) {
1919
return &CommentNode{}, nil
2020
})
2121
if err != nil {

0 commit comments

Comments
 (0)