fix/exec-umask 2cd66dc

actions_cache_test.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package executor
4
5import (
6	"strings"
7	"testing"
8)
9
10const (
11	cacheTestURL   = "http://127.0.0.1:9089/"
12	cacheTestToken = "eyJqIjoiMSJ9.c2lnbmF0dXJl"
13)
14
15// cacheTestContainerURL is the relay on the docker bridge, for the steps that
16// run in a container.
17const cacheTestContainerURL = "http://172.17.0.1:9089/"
18
19func withCache(c *Config) {
20	c.CacheURL, c.CacheToken, c.ContainerCacheURL = cacheTestURL, cacheTestToken, cacheTestContainerURL
21}
22
23const envAction = `name: e
24description: d
25runs:
26  using: node20
27  main: main.sh
28`
29
30const envScript = "echo \"mode=[${ACTIONS_CACHE_MODE-unset}] url=[$ACTIONS_CACHE_URL] rurl=[$ACTIONS_RUNTIME_URL] token=[$ACTIONS_RUNTIME_TOKEN] v2=[${ACTIONS_CACHE_SERVICE_V2-unset}] results=[${ACTIONS_RESULTS_URL-unset}]\"\n"
31
32// NodeScriptActionHandler.cs:55-72: a node action gets the runtime variables. The
33// URL is the base the toolkit appends "_apis/artifactcache/…" to (it ends with a
34// slash), ACTIONS_CACHE_SERVICE_V2 is never set, and the token is masked in the
35// log.
36func TestNodeActionGetsCacheEnv(t *testing.T) {
37	rec, res := actionRun(t, files{".github/actions/e/action.yml": envAction, ".github/actions/e/main.sh": envScript},
38		"      - uses: ./.github/actions/e\n", withCache)
39	if res.Conclusion != ConclusionSuccess {
40		t.Fatalf("%+v\n%s", res, logOf(rec.steps[1]))
41	}
42	got := logOf(rec.step(t, "Run ./.github/actions/e"))
43	want := "mode=[unset] url=[" + cacheTestURL + "] rurl=[" + cacheTestURL + "] token=[***] v2=[unset] results=[unset]"
44	if !strings.Contains(got, want) {
45		t.Errorf("want %q in\n%s", want, got)
46	}
47	if strings.Contains(got, cacheTestToken) {
48		t.Errorf("the token is in the log:\n%s", got)
49	}
50}
51
52// A run: step is not an action handler: it gets none of it (the official runner
53// sets these for actions only), and a job without a cache service sets nothing.
54func TestCacheEnvOnlyForActions(t *testing.T) {
55	rec, res := actionRun(t, files{}, "      - run: echo \"[$ACTIONS_CACHE_URL][$ACTIONS_RUNTIME_TOKEN]\"\n", withCache)
56	if res.Conclusion != ConclusionSuccess {
57		t.Fatalf("%+v", res)
58	}
59	if got := logOf(rec.step(t, "Run echo \"[$ACTIONS_CACHE_URL][$ACTIONS_RUNTIME_TOKEN]\"")); !strings.HasSuffix(strings.TrimRight(got, "\n"), "\n[][]") {
60		t.Errorf("run step env:\n%s", got)
61	}
62	rec, res = actionRun(t, files{".github/actions/e/action.yml": envAction, ".github/actions/e/main.sh": envScript},
63		"      - uses: ./.github/actions/e\n", nil)
64	if res.Conclusion != ConclusionSuccess {
65		t.Fatalf("%+v", res)
66	}
67	if got := logOf(rec.step(t, "Run ./.github/actions/e")); !strings.Contains(got, "mode=[unset] url=[] rurl=[] token=[]") {
68		t.Errorf("no cache service, no variables:\n%s", got)
69	}
70}
71
72// ContainerActionHandler.cs:219-236: a docker action gets them too, passed by
73// name to "docker run" like the other variables.
74func TestDockerActionGetsCacheEnv(t *testing.T) {
75	d, lf := fakeDocker(t, 0)
76	rec, res := actionRun(t, files{}, "      - uses: docker://alpine:3.20\n", func(c *Config) {
77		c.Actions.DockerPath = d
78		withCache(c)
79	})
80	if res.Conclusion != ConclusionSuccess {
81		t.Fatalf("%+v %v", res, rec.names())
82	}
83	cs := calls(t, lf)
84	if len(cs) != 2 {
85		t.Fatalf("calls %v", cs)
86	}
87	args := "\n" + strings.Join(cs[1], "\n") + "\n"
88	for _, n := range []string{"ACTIONS_CACHE_URL", "ACTIONS_RUNTIME_URL", "ACTIONS_RUNTIME_TOKEN"} {
89		if !strings.Contains(args, "\n-e\n"+n+"\n") {
90			t.Errorf("docker run lacks -e %s:\n%s", n, args)
91		}
92	}
93	if strings.Contains(args, "ACTIONS_CACHE_SERVICE_V2") || strings.Contains(args, cacheTestToken) {
94		t.Errorf("service v2 or the token value on the command line:\n%s", args)
95	}
96	if run := logOf(rec.steps[2]); strings.Contains(run, cacheTestToken) {
97		t.Errorf("the token is in the log:\n%s", run)
98	}
99}
100
101// ACTIONS_CACHE_MODE (NodeScriptActionHandler.cs:71-84): set from the cache
102// mode of the job, for node and docker actions, next to the URL only.
103func TestCacheModeEnv(t *testing.T) {
104	for _, mode := range []string{"write", "read"} {
105		rec, res := actionRun(t, files{".github/actions/e/action.yml": envAction, ".github/actions/e/main.sh": envScript},
106			"      - uses: ./.github/actions/e\n", func(c *Config) { withCache(c); c.CacheMode = mode })
107		if res.Conclusion != ConclusionSuccess {
108			t.Fatalf("%+v", res)
109		}
110		if got := logOf(rec.step(t, "Run ./.github/actions/e")); !strings.Contains(got, "mode=["+mode+"] url=[") {
111			t.Errorf("mode %s:\n%s", mode, got)
112		}
113	}
114	// A mode without a cache service sets nothing.
115	rec, res := actionRun(t, files{".github/actions/e/action.yml": envAction, ".github/actions/e/main.sh": envScript},
116		"      - uses: ./.github/actions/e\n", func(c *Config) { c.CacheMode = "write" })
117	if res.Conclusion != ConclusionSuccess {
118		t.Fatalf("%+v", res)
119	}
120	if got := logOf(rec.step(t, "Run ./.github/actions/e")); !strings.Contains(got, "mode=[unset]") {
121		t.Errorf("no cache service:\n%s", got)
122	}
123}
124
125func TestDockerActionGetsCacheMode(t *testing.T) {
126	d, lf := fakeDocker(t, 0)
127	_, res := actionRun(t, files{}, "      - uses: docker://alpine:3.20\n", func(c *Config) {
128		c.Actions.DockerPath = d
129		withCache(c)
130		c.CacheMode = "read"
131	})
132	if res.Conclusion != ConclusionSuccess {
133		t.Fatalf("%+v", res)
134	}
135	cs := calls(t, lf)
136	if len(cs) != 2 || !strings.Contains("\n"+strings.Join(cs[1], "\n")+"\n", "\n-e\nACTIONS_CACHE_MODE\n") {
137		t.Errorf("docker run lacks -e ACTIONS_CACHE_MODE: %v", cs)
138	}
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