v0.1.0 28f9d6e

convert.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package workflow
4
5import (
6	"bnema.dev/ermite-actions/template"
7)
8
9// converter turns the token tree of a workflow into the model. It is a port
10// of the reference's converter, which reports problems by throwing an error
11// that the caller of each conversion step catches and records; here panics of
12// type *template.TokenError play the part of those exceptions.
13type converter struct {
14	ctx    *template.Context
15	schema *template.Schema
16	w      *Workflow
17	jobIDs *idBuilder
18}
19
20// handle runs fn and records a *template.TokenError panic as an error
21// located at the token it names, like handleTemplateTokenErrors.
22func (c *converter) handle(root template.Token, fn func()) {
23	defer func() {
24		if r := recover(); r != nil {
25			te, ok := r.(*template.TokenError)
26			if !ok {
27				panic(r)
28			}
29			if te.Token != nil {
30				c.ctx.ErrorAt(te.Token, te.Message)
31			} else {
32				c.ctx.ErrorAt(root, te.Message)
33			}
34		}
35	}()
36	fn()
37}
38
39func ck[T any](v T, err error) T {
40	if err != nil {
41		panic(err)
42	}
43	return v
44}
45
46func asString(t template.Token, desc string) *template.String {
47	return ck(template.AssertString(t, desc))
48}
49
50func asBool(t template.Token, desc string) *template.Boolean {
51	return ck(template.AssertBoolean(t, desc))
52}
53
54func asSeq(t template.Token, desc string) *template.Sequence {
55	return ck(template.AssertSequence(t, desc))
56}
57
58func asMap(t template.Token, desc string) *template.Mapping {
59	return ck(template.AssertMapping(t, desc))
60}
61
62func asScalar(t template.Token, desc string) template.Token {
63	return ck(template.AssertScalar(t, desc))
64}
65
66func origin(t template.Token) template.Origin {
67	return template.Origin{File: t.File(), Pos: t.Pos()}
68}
69
70// convertRoot converts the root mapping.
71func (c *converter) convertRoot(root template.Token) {
72	defer func() {
73		if r := recover(); r != nil {
74			te, ok := r.(*template.TokenError)
75			if !ok {
76				panic(r)
77			}
78			if te.Token != nil {
79				c.ctx.ErrorAt(te.Token, te.Message)
80			} else {
81				c.ctx.ErrorAt(root, te.Message)
82			}
83		}
84	}()
85	m := asMap(root, "root")
86	for _, p := range m.Pairs {
87		key := asString(p.Key, "root key")
88		switch key.Value {
89		case "on":
90			c.handle(root, func() { c.w.On = c.convertOn(p.Value) })
91		case "jobs":
92			c.w.Jobs = nil
93			c.handle(root, func() { c.w.Jobs = c.convertJobs(p.Value) })
94		case "concurrency":
95			c.handle(root, func() { c.convertConcurrency(p.Value) })
96			c.w.Concurrency = p.Value
97		case "env":
98			if em, ok := p.Value.(*template.Mapping); ok {
99				c.w.Env = em
100			}
101		case "name":
102			if s, ok := p.Value.(*template.String); ok {
103				c.w.Name = s.Value
104			}
105		case "run-name":
106			c.w.RunName = p.Value
107		case "defaults":
108			c.w.Defaults = convertDefaults(p.Value)
109		case "permissions":
110			c.w.Permissions = convertPermissions(p.Value)
111		}
112	}
113}
114
115func convertDefaults(t template.Token) *Defaults {
116	m, ok := t.(*template.Mapping)
117	if !ok {
118		return nil
119	}
120	d := &Defaults{}
121	if run, ok := m.Find("run").(*template.Mapping); ok {
122		d.Run = &RunDefaults{Shell: run.Find("shell"), WorkingDirectory: run.Find("working-directory")}
123	}
124	return d
125}
126
127func convertPermissions(t template.Token) *Permissions {
128	switch t := t.(type) {
129	case *template.String:
130		return &Permissions{All: t.Value}
131	case *template.Mapping:
132		p := &Permissions{}
133		for _, kv := range t.Pairs {
134			p.Scopes = append(p.Scopes, PermissionScope{Scope: kv.Key.String(), Level: kv.Value.String()})
135		}
136		return p
137	}
138	return nil
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 1

Tags 3

↑↓ moveenter openesc close