actions_scope.go RawHistoryFolder
1// SPDX-License-Identifier: MIT
2
3package executor
4
5import (
6 "strings"
7
8 "bnema.dev/ermite-actions/expr"
9)
10
11// stepsScope is a steps context: the one of the job, or the one of a composite
12// action's steps (StepsContext.GetScope in the runner), plus the inputs the
13// steps of that composite see.
14type stepsScope struct {
15 order []string
16 steps map[string]*stepCtx
17 inputs *expr.Dictionary // nil: the job's inputs context
18}
19
20func newScope() *stepsScope { return &stepsScope{steps: map[string]*stepCtx{}} }
21
22// PushScope makes the steps context called name (created empty on first use)
23// the current one, with inputs as the inputs context, and returns the function
24// that restores the previous one. A composite action's pre, main and post
25// stages share one scope (the same name), as the runner's do.
26func (s *State) PushScope(name string, inputs *expr.Dictionary) (restore func()) {
27 prev := s.cur
28 sc, ok := s.scopes[name]
29 if name == "" {
30 sc, ok = s.root, true
31 }
32 if !ok {
33 sc = newScope()
34 s.scopes[name] = sc
35 }
36 if inputs != nil {
37 sc.inputs = inputs
38 }
39 s.cur = sc
40 return func() { s.cur = prev }
41}
42
43// StepsValue returns the steps context in use (outputs of a composite action
44// are evaluated against it).
45func (s *State) StepsValue() *expr.Dictionary { return s.stepsValue() }
46
47// GitHubKeys saves the current value of the given keys of the github context,
48// to put them back with the returned function: a key that was absent is
49// removed again. Composite actions' steps get their own copy of the context in
50// the runner (a shallow copy, CompositeActionHandler.cs:152-161); the
51// executor keeps one and restores it.
52func (s *State) GitHubKeys(keys ...string) (restore func()) {
53 type saved struct {
54 key string
55 val expr.Value
56 has bool
57 }
58 var old []saved
59 for _, k := range keys {
60 v, has := s.github.Get(k)
61 old = append(old, saved{k, v, has})
62 }
63 return func() {
64 for _, o := range old {
65 if o.has {
66 s.github = setKey(s.github, o.key, o.val)
67 } else {
68 s.github = removeKey(s.github, o.key)
69 }
70 }
71 }
72}
73
74// removeKey returns d without key (compared case-insensitively).
75func removeKey(d *expr.Dictionary, key string) *expr.Dictionary {
76 out := expr.NewDictionary()
77 for _, p := range d.Pairs() {
78 if !strings.EqualFold(p.Key, key) {
79 out.Add(p.Key, p.Value)
80 }
81 }
82 return out
83}