detached 76038ad

style(executor): error strings and unused code for the linter

commit
76038ad1df833eccb3746ccc549db7bd39c37fb1
author
brice · · 2026-10-13 16:36 UTC
parent
622eabb
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
10 files · +20 -36
M internal/executor/actions_docker.go +2 -2
@@ -88,7 +88,7 @@ func (j *job) buildImage(s *stepRun, tag, dockerfile, workDir string, cancel <-c
8888 return err
8989 }
9090 if code != 0 {
91- return fmt.Errorf("Docker build failed with exit code %d", code)
91+ return ghErrorf("Docker build failed with exit code %d", code)
9292 }
9393 return nil
9494 }
@@ -127,7 +127,7 @@ func (j *job) runDocker(s *stepRun, def *actionDef, in *inputSet, env *ordered,
127127 }
128128 if build != "" {
129129 if _, err := os.Stat(build); err != nil {
130- return fail(fmt.Errorf("File not found: '%s'", build))
130+ return fail(ghErrorf("File not found: '%s'", build))
131131 }
132132 j.out(num, "##[group]Building docker image")
133133 j.out(num, fmt.Sprintf("Dockerfile for action: '%s'.", build))
@@ -88,7 +88,7 @@ func (j *job) buildImage(s *stepRun, tag, dockerfile, workDir string, cancel <-c
88 return err88 return err
89 }89 }
90 if code != 0 {90 if code != 0 {
91- return fmt.Errorf("Docker build failed with exit code %d", code)91+ return ghErrorf("Docker build failed with exit code %d", code)
92 }92 }
93 return nil93 return nil
94 }94 }
@@ -127,7 +127,7 @@ func (j *job) runDocker(s *stepRun, def *actionDef, in *inputSet, env *ordered,
127 }127 }
128 if build != "" {128 if build != "" {
129 if _, err := os.Stat(build); err != nil {129 if _, err := os.Stat(build); err != nil {
130- return fail(fmt.Errorf("File not found: '%s'", build))130+ return fail(ghErrorf("File not found: '%s'", build))
131 }131 }
132 j.out(num, "##[group]Building docker image")132 j.out(num, "##[group]Building docker image")
133 j.out(num, fmt.Sprintf("Dockerfile for action: '%s'.", build))133 j.out(num, fmt.Sprintf("Dockerfile for action: '%s'.", build))
M internal/executor/actions_docker_cmd.go +1 -2
@@ -5,7 +5,6 @@ package executor
55 import (
66 "crypto/sha256"
77 "encoding/hex"
8- "fmt"
98 "os"
109 "os/exec"
1110 "regexp"
@@ -188,7 +187,7 @@ func (j *job) runDockerCmd(s *stepRun, c dockerCmd, dir string, env []string, ca
188187 }
189188
190189 // errCancelled is a docker command stopped by the cancellation of the step.
191-var errCancelled = fmt.Errorf("The operation was canceled.")
190+var errCancelled = ghErrorf("The operation was canceled.")
192191
193192 func dirOrCwd(d string) string {
194193 if st, err := os.Stat(d); err == nil && st.IsDir() {
@@ -5,7 +5,6 @@ package executor
5 import (5 import (
6 "crypto/sha256"6 "crypto/sha256"
7 "encoding/hex"7 "encoding/hex"
8- "fmt"
9 "os"8 "os"
10 "os/exec"9 "os/exec"
11 "regexp"10 "regexp"
@@ -188,7 +187,7 @@ func (j *job) runDockerCmd(s *stepRun, c dockerCmd, dir string, env []string, ca
188 }187 }
189 188
190 // errCancelled is a docker command stopped by the cancellation of the step.189 // errCancelled is a docker command stopped by the cancellation of the step.
191-var errCancelled = fmt.Errorf("The operation was canceled.")190+var errCancelled = ghErrorf("The operation was canceled.")
192 191
193 func dirOrCwd(d string) string {192 func dirOrCwd(d string) string {
194 if st, err := os.Stat(d); err == nil && st.IsDir() {193 if st, err := os.Stat(d); err == nil && st.IsDir() {
M internal/executor/actions_download.go +2 -2
@@ -118,7 +118,7 @@ func (j *job) fetch(ctx context.Context, url, token, file string) error {
118118 return errAccessDenied{url}
119119 case resp.StatusCode < 200 || resp.StatusCode > 299:
120120 // HttpResponseMessage.EnsureSuccessStatusCode.
121- return fmt.Errorf("Response status code does not indicate success: %d (%s).", resp.StatusCode, http.StatusText(resp.StatusCode))
121+ return ghErrorf("Response status code does not indicate success: %d (%s).", resp.StatusCode, http.StatusText(resp.StatusCode))
122122 }
123123 f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
124124 if err != nil {
@@ -174,7 +174,7 @@ func (j *job) downloadAction(ctx context.Context, num int, ra ResolvedAction, na
174174 err = extractTarball(af, staging, owner)
175175 _ = af.Close()
176176 if err != nil {
177- return fmt.Errorf("Can't extract the archive of action %s@%s: %w", name, ref, err)
177+ return ghErrorf("Can't extract the archive of action %s@%s: %v", name, ref, err)
178178 }
179179 ents, err := os.ReadDir(staging)
180180 if err != nil {
@@ -118,7 +118,7 @@ func (j *job) fetch(ctx context.Context, url, token, file string) error {
118 return errAccessDenied{url}118 return errAccessDenied{url}
119 case resp.StatusCode < 200 || resp.StatusCode > 299:119 case resp.StatusCode < 200 || resp.StatusCode > 299:
120 // HttpResponseMessage.EnsureSuccessStatusCode.120 // HttpResponseMessage.EnsureSuccessStatusCode.
121- return fmt.Errorf("Response status code does not indicate success: %d (%s).", resp.StatusCode, http.StatusText(resp.StatusCode))121+ return ghErrorf("Response status code does not indicate success: %d (%s).", resp.StatusCode, http.StatusText(resp.StatusCode))
122 }122 }
123 f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)123 f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
124 if err != nil {124 if err != nil {
@@ -174,7 +174,7 @@ func (j *job) downloadAction(ctx context.Context, num int, ra ResolvedAction, na
174 err = extractTarball(af, staging, owner)174 err = extractTarball(af, staging, owner)
175 _ = af.Close()175 _ = af.Close()
176 if err != nil {176 if err != nil {
177- return fmt.Errorf("Can't extract the archive of action %s@%s: %w", name, ref, err)177+ return ghErrorf("Can't extract the archive of action %s@%s: %v", name, ref, err)
178 }178 }
179 ents, err := os.ReadDir(staging)179 ents, err := os.ReadDir(staging)
180 if err != nil {180 if err != nil {
M internal/executor/actions_load.go +1 -1
@@ -74,7 +74,7 @@ func (j *job) loadDef(num int, dir string) (*actionDef, error) {
7474 case action.RunsDocker:
7575 d.image = act.Runs.Image
7676 case action.RunsPlugin:
77- return nil, fmt.Errorf("Unsupported action type: Plugin")
77+ return nil, ghErrorf("Unsupported action type: Plugin")
7878 }
7979 return d, nil
8080 }
@@ -74,7 +74,7 @@ func (j *job) loadDef(num int, dir string) (*actionDef, error) {
74 case action.RunsDocker:74 case action.RunsDocker:
75 d.image = act.Runs.Image75 d.image = act.Runs.Image
76 case action.RunsPlugin:76 case action.RunsPlugin:
77- return nil, fmt.Errorf("Unsupported action type: Plugin")77+ return nil, ghErrorf("Unsupported action type: Plugin")
78 }78 }
79 return d, nil79 return d, nil
80 }80 }
M internal/executor/actions_node.go +2 -2
@@ -164,11 +164,11 @@ func (j *job) runNode(s *stepRun, def *actionDef, in *inputSet, env *ordered, ca
164164 }
165165 fail := func(err error) stepResult { j.errorf(num, "%s", err.Error()); return resFailed }
166166 if target == "" {
167- return fail(fmt.Errorf("Value cannot be null or empty. (Parameter 'target')"))
167+ return fail(ghErrorf("Value cannot be null or empty. (Parameter 'target')"))
168168 }
169169 script := filepath.Join(def.dir, filepath.FromSlash(target))
170170 if st, err := os.Stat(script); err != nil || st.IsDir() {
171- return fail(fmt.Errorf("File not found: '%s'", script))
171+ return fail(ghErrorf("File not found: '%s'", script))
172172 }
173173 j.mu.Lock()
174174 wd := j.state.GitHub("workspace")
@@ -164,11 +164,11 @@ func (j *job) runNode(s *stepRun, def *actionDef, in *inputSet, env *ordered, ca
164 }164 }
165 fail := func(err error) stepResult { j.errorf(num, "%s", err.Error()); return resFailed }165 fail := func(err error) stepResult { j.errorf(num, "%s", err.Error()); return resFailed }
166 if target == "" {166 if target == "" {
167- return fail(fmt.Errorf("Value cannot be null or empty. (Parameter 'target')"))167+ return fail(ghErrorf("Value cannot be null or empty. (Parameter 'target')"))
168 }168 }
169 script := filepath.Join(def.dir, filepath.FromSlash(target))169 script := filepath.Join(def.dir, filepath.FromSlash(target))
170 if st, err := os.Stat(script); err != nil || st.IsDir() {170 if st, err := os.Stat(script); err != nil || st.IsDir() {
171- return fail(fmt.Errorf("File not found: '%s'", script))171+ return fail(ghErrorf("File not found: '%s'", script))
172 }172 }
173 j.mu.Lock()173 j.mu.Lock()
174 wd := j.state.GitHub("workspace")174 wd := j.state.GitHub("workspace")
M internal/executor/actions_prepare.go +4 -5
@@ -4,7 +4,6 @@ package executor
44
55 import (
66 "context"
7- "fmt"
87 "strings"
98
109 "bnema.dev/ermite-actions/action"
@@ -59,7 +58,7 @@ func lookupKey(n *node) (string, error) {
5958 // it runs.
6059 func (j *job) prepare(ctx context.Context, num int, nodes []*node, depth int, parent *node) error {
6160 if depth > compositeMaxDepth {
62- return fmt.Errorf("Composite action depth exceeded max depth %d", compositeMaxDepth)
61+ return ghErrorf("Composite action depth exceeded max depth %d", compositeMaxDepth)
6362 }
6463 st := j.actions
6564 var repoNodes []*node
@@ -67,7 +66,7 @@ func (j *job) prepare(ctx context.Context, num int, nodes []*node, depth int, pa
6766 switch n.uses.Kind {
6867 case action.UsesDocker:
6968 if n.uses.Image == "" {
70- return fmt.Errorf("Value cannot be null or empty. (Parameter 'Image')")
69+ return ghErrorf("Value cannot be null or empty. (Parameter 'Image')")
7170 }
7271 if _, ok := st.pullUsers[n.uses.Image]; !ok {
7372 st.pullOrder = append(st.pullOrder, n.uses.Image)
@@ -94,7 +93,7 @@ func (j *job) prepare(ctx context.Context, num int, nodes []*node, depth int, pa
9493 }
9594 ra, ok := infos[key]
9695 if !ok {
97- return fmt.Errorf("Missing download info for %s", key)
96+ return ghErrorf("Missing download info for %s", key)
9897 }
9998 dir := j.actionDirOfRepo(n)
10099 // The directory of the archive is the one of the repository itself: the
@@ -180,7 +179,7 @@ func (j *job) prepareRepositoryAction(ctx context.Context, num int, n *node, dep
180179 dir := j.actionDir(n)
181180 def, err := j.loadDef(num, dir)
182181 if err == errNoAction {
183- return fmt.Errorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' for action '%s'.", refString(n.uses))
182+ return ghErrorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' for action '%s'.", refString(n.uses))
184183 }
185184 if err != nil {
186185 return err
@@ -4,7 +4,6 @@ package executor
4 4
5 import (5 import (
6 "context"6 "context"
7- "fmt"
8 "strings"7 "strings"
9 8
10 "bnema.dev/ermite-actions/action"9 "bnema.dev/ermite-actions/action"
@@ -59,7 +58,7 @@ func lookupKey(n *node) (string, error) {
59 // it runs.58 // it runs.
60 func (j *job) prepare(ctx context.Context, num int, nodes []*node, depth int, parent *node) error {59 func (j *job) prepare(ctx context.Context, num int, nodes []*node, depth int, parent *node) error {
61 if depth > compositeMaxDepth {60 if depth > compositeMaxDepth {
62- return fmt.Errorf("Composite action depth exceeded max depth %d", compositeMaxDepth)61+ return ghErrorf("Composite action depth exceeded max depth %d", compositeMaxDepth)
63 }62 }
64 st := j.actions63 st := j.actions
65 var repoNodes []*node64 var repoNodes []*node
@@ -67,7 +66,7 @@ func (j *job) prepare(ctx context.Context, num int, nodes []*node, depth int, pa
67 switch n.uses.Kind {66 switch n.uses.Kind {
68 case action.UsesDocker:67 case action.UsesDocker:
69 if n.uses.Image == "" {68 if n.uses.Image == "" {
70- return fmt.Errorf("Value cannot be null or empty. (Parameter 'Image')")69+ return ghErrorf("Value cannot be null or empty. (Parameter 'Image')")
71 }70 }
72 if _, ok := st.pullUsers[n.uses.Image]; !ok {71 if _, ok := st.pullUsers[n.uses.Image]; !ok {
73 st.pullOrder = append(st.pullOrder, n.uses.Image)72 st.pullOrder = append(st.pullOrder, n.uses.Image)
@@ -94,7 +93,7 @@ func (j *job) prepare(ctx context.Context, num int, nodes []*node, depth int, pa
94 }93 }
95 ra, ok := infos[key]94 ra, ok := infos[key]
96 if !ok {95 if !ok {
97- return fmt.Errorf("Missing download info for %s", key)96+ return ghErrorf("Missing download info for %s", key)
98 }97 }
99 dir := j.actionDirOfRepo(n)98 dir := j.actionDirOfRepo(n)
100 // The directory of the archive is the one of the repository itself: the99 // The directory of the archive is the one of the repository itself: the
@@ -180,7 +179,7 @@ func (j *job) prepareRepositoryAction(ctx context.Context, num int, n *node, dep
180 dir := j.actionDir(n)179 dir := j.actionDir(n)
181 def, err := j.loadDef(num, dir)180 def, err := j.loadDef(num, dir)
182 if err == errNoAction {181 if err == errNoAction {
183- return fmt.Errorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' for action '%s'.", refString(n.uses))182+ return ghErrorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' for action '%s'.", refString(n.uses))
184 }183 }
185 if err != nil {184 if err != nil {
186 return err185 return err
M internal/executor/actions_run.go +3 -3
@@ -33,7 +33,7 @@ func (j *job) definition(s *stepRun) (*actionDef, error) {
3333 dir := j.actionDir(n)
3434 def, err := j.loadDef(s.num, dir)
3535 if err == errNoAction {
36- return nil, fmt.Errorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '%s'. Did you forget to run actions/checkout before running your local action?", filepath.Clean(dir))
36+ return nil, ghErrorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '%s'. Did you forget to run actions/checkout before running your local action?", filepath.Clean(dir))
3737 }
3838 if err != nil {
3939 return nil, err
@@ -42,7 +42,7 @@ func (j *job) definition(s *stepRun) (*actionDef, error) {
4242 return def, nil
4343 }
4444 if n.def == nil {
45- return nil, fmt.Errorf("The action '%s' was not prepared", refString(n.uses))
45+ return nil, ghErrorf("The action '%s' was not prepared", refString(n.uses))
4646 }
4747 return n.def, nil
4848 }
@@ -198,7 +198,7 @@ func (j *job) runAction(s *stepRun, cancel <-chan struct{}) stepResult {
198198 case kindComposite:
199199 return j.runComposite(s, def, in, env)
200200 }
201- return fail(fmt.Errorf("Unsupported action type: %v", def.kind))
201+ return fail(ghErrorf("Unsupported action type: %v", def.kind))
202202 }
203203
204204 // hasPostOf is HasPost of the loaded definition of the node.
@@ -33,7 +33,7 @@ func (j *job) definition(s *stepRun) (*actionDef, error) {
33 dir := j.actionDir(n)33 dir := j.actionDir(n)
34 def, err := j.loadDef(s.num, dir)34 def, err := j.loadDef(s.num, dir)
35 if err == errNoAction {35 if err == errNoAction {
36- return nil, fmt.Errorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '%s'. Did you forget to run actions/checkout before running your local action?", filepath.Clean(dir))36+ return nil, ghErrorf("Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '%s'. Did you forget to run actions/checkout before running your local action?", filepath.Clean(dir))
37 }37 }
38 if err != nil {38 if err != nil {
39 return nil, err39 return nil, err
@@ -42,7 +42,7 @@ func (j *job) definition(s *stepRun) (*actionDef, error) {
42 return def, nil42 return def, nil
43 }43 }
44 if n.def == nil {44 if n.def == nil {
45- return nil, fmt.Errorf("The action '%s' was not prepared", refString(n.uses))45+ return nil, ghErrorf("The action '%s' was not prepared", refString(n.uses))
46 }46 }
47 return n.def, nil47 return n.def, nil
48 }48 }
@@ -198,7 +198,7 @@ func (j *job) runAction(s *stepRun, cancel <-chan struct{}) stepResult {
198 case kindComposite:198 case kindComposite:
199 return j.runComposite(s, def, in, env)199 return j.runComposite(s, def, in, env)
200 }200 }
201- return fail(fmt.Errorf("Unsupported action type: %v", def.kind))201+ return fail(ghErrorf("Unsupported action type: %v", def.kind))
202 }202 }
203 203
204 // hasPostOf is HasPost of the loaded definition of the node.204 // hasPostOf is HasPost of the loaded definition of the node.
M internal/executor/actions_test.go +1 -1
@@ -374,7 +374,7 @@ runs:
374374
375375 func TestCompositePostOfInnerStepsRunsInOuterPost(t *testing.T) {
376376 tree := files{
377- "c/action.yml": "name: c\nruns:\n using: composite\n steps:\n - uses: ./n1\n - uses: ./n2\n",
377+ "c/action.yml": "name: c\nruns:\n using: composite\n steps:\n - uses: ./n1\n - uses: ./n2\n",
378378 "n1/action.yml": "name: n1\nruns:\n using: node20\n main: m.sh\n post: p.sh\n", "n1/m.sh": "echo main1\n", "n1/p.sh": "echo post1\n",
379379 "n2/action.yml": "name: n2\nruns:\n using: node20\n main: m.sh\n post: p.sh\n", "n2/m.sh": "echo main2\n", "n2/p.sh": "echo post2\n",
380380 }
@@ -374,7 +374,7 @@ runs:
374 374
375 func TestCompositePostOfInnerStepsRunsInOuterPost(t *testing.T) {375 func TestCompositePostOfInnerStepsRunsInOuterPost(t *testing.T) {
376 tree := files{376 tree := files{
377- "c/action.yml": "name: c\nruns:\n using: composite\n steps:\n - uses: ./n1\n - uses: ./n2\n",377+ "c/action.yml": "name: c\nruns:\n using: composite\n steps:\n - uses: ./n1\n - uses: ./n2\n",
378 "n1/action.yml": "name: n1\nruns:\n using: node20\n main: m.sh\n post: p.sh\n", "n1/m.sh": "echo main1\n", "n1/p.sh": "echo post1\n",378 "n1/action.yml": "name: n1\nruns:\n using: node20\n main: m.sh\n post: p.sh\n", "n1/m.sh": "echo main1\n", "n1/p.sh": "echo post1\n",
379 "n2/action.yml": "name: n2\nruns:\n using: node20\n main: m.sh\n post: p.sh\n", "n2/m.sh": "echo main2\n", "n2/p.sh": "echo post2\n",379 "n2/action.yml": "name: n2\nruns:\n using: node20\n main: m.sh\n post: p.sh\n", "n2/m.sh": "echo main2\n", "n2/p.sh": "echo post2\n",
380 }380 }
M internal/executor/actions_types.go +0 -4
@@ -133,12 +133,8 @@ type node struct {
133133 // postRegistered is Root.StepsWithPostRegistered / EmbeddedStepsWithPostRegistered.
134134 postRegistered bool
135135 postCond string
136- // preScope is the random steps context of the pre stage of a composite.
137- preScope string
138136 }
139137
140-func (n *node) isRun() bool { return n.st.Run != nil }
141-
142138 // setState keeps a value of the intra-action state: keys compare without case
143139 // (the runner's dictionary is OrdinalIgnoreCase) and keep the first spelling.
144140 func (n *node) setState(k, v string) {
@@ -133,12 +133,8 @@ type node struct {
133 // postRegistered is Root.StepsWithPostRegistered / EmbeddedStepsWithPostRegistered.133 // postRegistered is Root.StepsWithPostRegistered / EmbeddedStepsWithPostRegistered.
134 postRegistered bool134 postRegistered bool
135 postCond string135 postCond string
136- // preScope is the random steps context of the pre stage of a composite.
137- preScope string
138 }136 }
139 137
140-func (n *node) isRun() bool { return n.st.Run != nil }
141-
142 // setState keeps a value of the intra-action state: keys compare without case138 // setState keeps a value of the intra-action state: keys compare without case
143 // (the runner's dictionary is OrdinalIgnoreCase) and keep the first spelling.139 // (the runner's dictionary is OrdinalIgnoreCase) and keep the first spelling.
144 func (n *node) setState(k, v string) {140 func (n *node) setState(k, v string) {
M internal/executor/step.go +4 -14
@@ -37,26 +37,16 @@ type stepRun struct {
3737 // fork for an embedded one.
3838 fc *FileCommands
3939 // actEnv, when set, is the environment of the process of an action step
40- // (env context, STATE_ and INPUT_); rawEnv replaces the whole environment
41- // of the process (docker, whose -e options read it).
42- actEnv, rawEnv *ordered
43- rawEnvList []string
44- quietExit bool
40+ // (env context, STATE_ and INPUT_); rawEnvList replaces the whole
41+ // environment of the process (docker, whose -e options read it).
42+ actEnv *ordered
43+ rawEnvList []string
4544 // displayName is the name of the step in the log; def the action it runs.
4645 displayName string
4746 def *actionDef
4847 nodeVersion string
4948 }
5049
51-// depth is the number of composite actions around the step.
52-func (s *stepRun) depth() int {
53- d := 0
54- for p := s.parent; p != nil; p = p.parent {
55- d++
56- }
57- return d
58-}
59-
6050 // procEnv is the environment of the process the step starts.
6151 func (s *stepRun) procEnv() []string {
6252 if s.rawEnvList != nil {
@@ -37,26 +37,16 @@ type stepRun struct {
37 // fork for an embedded one.37 // fork for an embedded one.
38 fc *FileCommands38 fc *FileCommands
39 // actEnv, when set, is the environment of the process of an action step39 // actEnv, when set, is the environment of the process of an action step
40- // (env context, STATE_ and INPUT_); rawEnv replaces the whole environment40+ // (env context, STATE_ and INPUT_); rawEnvList replaces the whole
41- // of the process (docker, whose -e options read it).41+ // environment of the process (docker, whose -e options read it).
42- actEnv, rawEnv *ordered42+ actEnv *ordered
43- rawEnvList []string43+ rawEnvList []string
44- quietExit bool
45 // displayName is the name of the step in the log; def the action it runs.44 // displayName is the name of the step in the log; def the action it runs.
46 displayName string45 displayName string
47 def *actionDef46 def *actionDef
48 nodeVersion string47 nodeVersion string
49 }48 }
50 49
51-// depth is the number of composite actions around the step.
52-func (s *stepRun) depth() int {
53- d := 0
54- for p := s.parent; p != nil; p = p.parent {
55- d++
56- }
57- return d
58-}
59-
60 // procEnv is the environment of the process the step starts.50 // procEnv is the environment of the process the step starts.
61 func (s *stepRun) procEnv() []string {51 func (s *stepRun) procEnv() []string {
62 if s.rawEnvList != nil {52 if s.rawEnvList != nil {

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