containers_exec.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package executor
4
5import (
6	"strings"
7)
8
9// ContainerStepHost (Runner.Worker/Handlers/StepHost.cs:102-296): the steps of
10// a job that has a container run through `docker exec` in it.
11
12// stepContainer is the job container when steps run in it, else nil.
13func (j *job) stepContainer() *container {
14	if j.cs == nil {
15		return nil
16	}
17	return j.cs.job
18}
19
20// containerReady is the check of ActionRunner.cs:157-164: the container must
21// exist. After a failed "Initialize containers" an always() step gets the
22// runner's error.
23func (j *job) containerReady() error {
24	if c := j.stepContainer(); c != nil && c.id == "" {
25		return ghErrorf("Value cannot be null. (Parameter 'ContainerId')")
26	}
27	return nil
28}
29
30// hostEnv is the environment a handler gives its process: the step's
31// variables then the runtime variables of the contexts (GITHUB_*, RUNNER_*)
32// (ScriptHandler.cs:289-299). It is what `docker exec -e` passes on.
33func (j *job) handlerEnv(stepEnv *ordered) *ordered {
34	env := stepEnv.Clone()
35	j.mu.Lock()
36	rt := j.state.RuntimeEnv()
37	j.mu.Unlock()
38	for _, k := range rt.Keys() {
39		v, _ := rt.Get(k)
40		env.Set(k, v)
41	}
42	return env
43}
44
45// execArgv is ContainerStepHost.ExecuteAsync (StepHost.cs:196-246):
46// `docker exec -i --workdir <dir> -e NAME... [-e PATH="<prepend>:<PATH>"] <id>
47// <command> <arguments>`. The values of the variables are not on the command
48// line: -e NAME takes them from the environment of docker, which gets them
49// with the host paths translated to the container's (:248-254). args are the
50// arguments of the command, already split.
51func (j *job) execArgv(c *container, workDir string, env *ordered, command string, args []string) (argv []string, procEnv []string) {
52	argv = []string{j.dockerPath(), "exec", "-i", "--workdir", workDir}
53	cenv := newOrdered()
54	for _, k := range env.Keys() {
55		v, _ := env.Get(k)
56		v = c.translateToContainer(v)
57		// PATH is set below. The docker process is started by its absolute
58		// path, so the variables of the container are not read by it, except
59		// those the CLI itself reads.
60		p := j.envForCLI(k, v, "")
61		argv = append(argv, p.args...)
62		for _, kv := range p.env {
63			cenv.Set(k, strings.TrimPrefix(kv, k+"="))
64		}
65	}
66	j.mu.Lock()
67	prepend := j.state.Prepend()
68	j.mu.Unlock()
69	if len(prepend) > 0 {
70		rev := make([]string, len(prepend))
71		for i, p := range prepend {
72			rev[len(prepend)-1-i] = p
73		}
74		full := strings.Join(rev, ":")
75		if c.runtimePath != "" {
76			full += ":" + c.runtimePath
77		}
78		// The value is in the environment of docker.exec only: the process is
79		// started by absolute path and `docker exec` runs nothing of its own.
80		argv = append(argv, "-e", "PATH")
81		cenv.Set("PATH", full)
82	}
83	argv = append(argv, c.id, command)
84	argv = append(argv, args...)
85	return argv, j.dockerProcessEnv(cenv)
86}
87
88// containerScript prepares a run step for the container: the script is
89// written on the host in RUNNER_TEMP, which the container has at /__w/_temp,
90// and the shell gets that path.
91func (j *job) containerScript(c *container, plan ShellPlan, in scriptIn, s *stepRun) (argv []string, err error) {
92	sc, err := WriteScriptFor(j.tempRoot, plan, in.script, c.translateToContainer)
93	if err != nil {
94		return nil, err
95	}
96	j.mu.Lock()
97	workspace := j.state.GitHub("workspace")
98	j.mu.Unlock()
99	wd := c.resolvePath(ResolveWorkingDirectory(workspace, in.workingDirectory))
100	var rest []string
101	if len(sc.Argv) > 1 {
102		rest = sc.Argv[1:]
103	}
104	henv := j.handlerEnv(s.actEnv)
105	if err := checkEnvNames(henv.Keys()); err != nil {
106		return nil, err
107	}
108	a, penv := j.execArgv(c, wd, henv, plan.CommandPath, rest)
109	s.rawEnvList = penv
110	return a, nil
111}

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