v0.4.3 bdc41cd

actions_inputs.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package executor
4
5import (
6	"context"
7	"fmt"
8	"strings"
9
10	"bnema.dev/ermite-actions/action"
11	"bnema.dev/ermite-actions/expr"
12	"bnema.dev/ermite-actions/template"
13	"bnema.dev/ermite-actions/workflow"
14)
15
16// inputSet is the inputs of a step: a dictionary with OrdinalIgnoreCase keys
17// that keeps insertion order (Dictionary<string,string>, which only keeps it
18// while nothing is removed).
19type inputSet struct {
20	keys []string
21	vals map[string]string
22}
23
24func newInputSet() *inputSet { return &inputSet{vals: map[string]string{}} }
25
26func (in *inputSet) lower(k string) string { return strings.ToLower(k) }
27
28func (in *inputSet) Set(k, v string) {
29	l := in.lower(k)
30	if _, ok := in.vals[l]; !ok {
31		in.keys = append(in.keys, k)
32	}
33	in.vals[l] = v
34}
35
36func (in *inputSet) Get(k string) (string, bool) { v, ok := in.vals[in.lower(k)]; return v, ok }
37func (in *inputSet) Has(k string) bool           { _, ok := in.vals[in.lower(k)]; return ok }
38func (in *inputSet) Len() int                    { return len(in.keys) }
39
40// evalInputs is EvaluateStepInputs: the "with" of the step (a mapping, or the
41// expression of a composite action's step that stands for one).
42func (j *job) evalInputs(st *workflow.Step, env *ordered) (*inputSet, error) {
43	in := newInputSet()
44	var tok template.Token
45	switch {
46	case st.With != nil:
47		tok = st.With
48	case st.WithExpression != nil:
49		tok = st.WithExpression
50	default:
51		return in, nil
52	}
53	v, err := template.Evaluate(context.Background(), tok, j.evalEnv(env))
54	if err != nil {
55		return nil, err
56	}
57	d, ok := v.(*expr.Dictionary)
58	if !ok {
59		return in, nil
60	}
61	for _, p := range d.Pairs() {
62		if k := p.Value.Kind(); k == expr.KindArray || k == expr.KindDictionary {
63			return nil, ghErrorf("Unexpected type '%s' encountered while reading 'inputs value'. The type 'StringToken' was expected.", k)
64		}
65		in.Set(p.Key, expr.CoerceString(p.Value))
66	}
67	return in, nil
68}
69
70// mergeInputs is the second half of ActionRunner.RunAsync's input handling
71// (ActionRunner.cs:121-186): deprecation warnings for the inputs the step
72// gives, the defaults of the others (evaluated in the context of the step), and
73// the warning about inputs the action does not declare. The missing required
74// inputs are not checked: the runner has no such check.
75func (j *job) mergeInputs(s *stepRun, def *actionDef, in *inputSet) error {
76	var act *action.Action
77	if def != nil {
78		act = def.act
79	}
80	user := append([]string(nil), in.keys...)
81	if act != nil {
82		for _, k := range user {
83			for i := range act.Inputs {
84				if act.Inputs[i].Name == k && act.Inputs[i].Deprecated() {
85					j.tagged(s.num, "##[warning]", fmt.Sprintf("Input '%s' has been deprecated with message: %s", k, act.Inputs[i].DeprecationMessage))
86				}
87			}
88		}
89	}
90	valid := newInputSet()
91	if def != nil && def.kind == kindDocker {
92		// A container action always accepts 'entryPoint' and 'args'.
93		valid.Set("entryPoint", "")
94		valid.Set("args", "")
95	}
96	if act != nil {
97		for i := range act.Inputs {
98			name := act.Inputs[i].Name
99			valid.Set(name, "")
100			if in.Has(name) {
101				continue
102			}
103			val, err := j.evalDefault(act.Inputs[i].Default, s.env)
104			if err != nil {
105				return err
106			}
107			in.Set(name, val)
108		}
109	}
110	// Only for actions with a manifest or a repository reference (not docker://).
111	if s.n.uses.Kind == action.UsesDocker {
112		return nil
113	}
114	var unexpected []string
115	for _, k := range user {
116		if !valid.Has(k) {
117			unexpected = append(unexpected, k)
118		}
119	}
120	if len(unexpected) > 0 {
121		j.tagged(s.num, "##[warning]", fmt.Sprintf("Unexpected input(s) '%s', valid inputs are ['%s']",
122			strings.Join(unexpected, "', '"), strings.Join(valid.keys, "', '")))
123	}
124	return nil
125}
126
127// evalDefault is EvaluateDefaultInput: the default is a string or an expression
128// over github, strategy, matrix, job, runner and hashFiles.
129func (j *job) evalDefault(tok template.Token, env *ordered) (string, error) {
130	if tok == nil {
131		return "", nil
132	}
133	return j.evalString(tok, env)
134}
135
136// inputEnvName is the variable of an input (Handler.AddInputsToEnvironment).
137func inputEnvName(key string) string {
138	return "INPUT_" + strings.ToUpper(strings.ReplaceAll(key, " ", "_"))
139}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close