GitRoot
Craft your forge, Build your project, Grow your community freely
1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5package exec
6
7import (
8 "context"
9 _ "embed"
10 "os"
11 "path/filepath"
12 "strconv"
13 "strings"
14 "testing"
15
16 "github.com/go-git/go-git/v5/plumbing"
17 "gitroot.dev/libs/golang/plugin/model"
18 "gitroot.dev/server/configuration"
19 "gitroot.dev/server/repository"
20 "gitroot.dev/server/user"
21)
22
23const pluginCacheDir = "pluginCache"
24
25///go:embed resources/GitRoot.priv
26// var gitrootPriv []byte
27
28///go:embed resources/GitRoot.pub
29// var gitrootPub []byte
30
31type fakeConf struct {
32 kind string
33 dataDir string
34}
35
36func (f *fakeConf) GetExecConf() configuration.ExecConf {
37 dc := configuration.ExecDefaultConf
38 switch f.kind {
39 case "bareMetal":
40 dc.BareMetal.Enabled = true
41 case "bwrap":
42 dc.Bwrap.Enabled = true
43 case "container":
44 dc.Container.Enabled = true
45 dc.Container.Bin = "podman"
46 case "ssh":
47 dc.Ssh.Enabled = true
48 u := os.Getenv("SSH_USER")
49 a := os.Getenv("SSH_ADDR")
50 p := os.Getenv("SSH_PORT")
51 port, _ := strconv.Atoi(p)
52 dc.Ssh.Hosts = []configuration.Host{
53 {User: u, Address: a, Port: port, PublicKey: "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCBMx22obajd8AZ75MtevWOR62MHAI3Lf2pMt5rDjgdPJUy2v3ncdSdod8qbsJ7Ie0I3OMlq+21Lr51PLNJj5Ok="},
54 }
55 }
56 return dc
57}
58
59func (f *fakeConf) PathCacheProject(repoName string, pluginName string) string {
60 return filepath.Join(f.dataDir, pluginCacheDir)
61}
62
63type testDate struct {
64 executor string
65 build string
66 status *model.ExecStatus
67 env []string
68}
69
70var testData = []*testDate{
71 {executor: "bareMetal", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
72 {executor: "bwrap", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
73 //TODO no container on gitroot.dev instance
74 //{executor: "container", build: "docker.io/bash:5.3.3", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
75 //{executor: "container", build: "./Containerfile", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
76 // WARN if you uncomment that be sure to set env look line 47 of this file
77 //{executor: "ssh", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
78}
79
80func TestExec(t *testing.T) {
81 for _, tt := range testData {
82 dataDir := t.TempDir()
83 conf := configuration.NewConfiguration(dataDir)
84 userManager, _ := user.NewManager(conf)
85 // os.WriteFile(filepath.Join(conf.GetDirPathData(), fmt.Sprintf("%s.pub", conf.RootCommiterPseudo())), gitrootPub, os.ModeExclusive)
86 // os.WriteFile(filepath.Join(conf.GetDirPathData(), fmt.Sprintf("%s.priv", conf.RootCommiterPseudo())), gitrootPriv, os.ModeExclusive)
87 userManager, _ = user.NewManager(conf)
88 m := NewManager(t.Context(), &fakeConf{kind: tt.executor, dataDir: dataDir}, userManager)
89 repoManager := repository.NewManager(conf, userManager)
90 err := repoManager.CreateUserRepo(context.TODO(), repository.RepoConf{Name: "exec", DefaultBranch: "main"}, []user.SimpleUser{}, []byte(""))
91 if err != nil {
92 t.Fatal(err)
93 }
94 repo, err := repoManager.Open(t.Context(), "exec")
95 if err != nil {
96 t.Fatal(err)
97 }
98 repoWrite, _ := repo.WillWrite(plumbing.NewBranchReferenceName("main"))
99 repoWrite.Write("Containerfile", []byte("from docker.io/bash:5.3.3"))
100 repoWrite.CommitAll("add containerfile", userManager.RootCommiter())
101 repoWrite.Accept()
102 repoWrite.Storer().Commit()
103
104 cmds := []model.Cmd{
105 {Cmd: "echo", Args: []string{"$MISE_CACHE_DIR"}},
106 {Cmd: "echo", Args: []string{"$MISE_CACHE_DIR > arti"}},
107 {Cmd: "ls", Args: []string{"-a", "."}},
108 {Cmd: "dd", Args: []string{"if=/dev/zero", "of=test_write.tmp", "bs=1M", "count=500", "conv=fdatasync", "oflag=dsync"}},
109 {Cmd: "rm", Args: []string{"test_write.tmp"}},
110 }
111
112 if tt.executor == "bareMetal" {
113 cmds = append(cmds, model.Cmd{Cmd: "echo", Args: []string{"Hello world > $PWD$MISE_CACHE_DIR/myFileCache"}})
114 } else {
115 cmds = append(cmds, model.Cmd{Cmd: "echo", Args: []string{"Hello world > $MISE_CACHE_DIR/myFileCache"}})
116 }
117
118 status, err := m.Exec(repo, "main", "pluginName", model.Exec{
119 Build: tt.build,
120 ReportStats: true,
121 Cmds: cmds,
122 Env: tt.env,
123 Artifacts: []string{"arti"},
124 Cache: []model.Cache{{Key: "keyCacheDir", Path: "/cache/mise", ReadOnly: false}},
125 })
126 if err != nil {
127 t.Fatal(err)
128 }
129
130 tt.status = status
131
132 if len(tt.status.CmdsLogs) < 6 {
133 t.Fatal("All step should have logs")
134 }
135 hasContainer := tt.executor != "bareMetal" && tt.executor != "ssh"
136 firstIndex := 0
137 if hasContainer {
138 firstIndex++
139 }
140 hasBuild := strings.HasPrefix(tt.build, "./")
141 if hasBuild {
142 firstIndex++
143 }
144 contentLog, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.CmdsLogs[firstIndex]))
145 if err != nil {
146 t.Fatal(err)
147 }
148 if !strings.Contains(string(contentLog), "/cache/mise") {
149 t.Fatal(string(contentLog))
150 }
151
152 contentLog, err = os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.CmdsLogs[firstIndex+2]))
153 if err != nil {
154 t.Fatal(err)
155 }
156 if !strings.Contains(string(contentLog), ".gitroot") {
157 t.Fatal(string(contentLog))
158 }
159
160 if len(tt.status.Artifacts) < 1 {
161 t.Fatal("should have artifacts")
162 }
163 contentArtifact, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.Artifacts[0]))
164 if err != nil {
165 t.Fatal(err)
166 }
167 if !strings.Contains(string(contentArtifact), "/cache/mise") {
168 t.Fatal(contentArtifact)
169 }
170
171 contentCache, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, "cache", "keyCacheDir", "myFileCache"))
172 if err != nil {
173 t.Fatal(err)
174 }
175 if !strings.Contains(string(contentCache), "Hello world") {
176 t.Fatal(contentCache)
177 }
178 }
179
180 t.Log("|executor| cmd |status|memory|cpu|nb thread|read|write|")
181 t.Log("|--------|------|------|------|---|---------|----|-----|")
182 for _, tt := range testData {
183 for i, stat := range tt.status.CmdsStats {
184 t.Logf("|%s|%s|%d|%d|%d|%d|%d|%d|", tt.executor, tt.status.CmdsExec[i], tt.status.CmdsStatus[i], stat.MaxMemoryBytes, stat.TotalCPUTimeMs, stat.MaxThreads, stat.ReadBytesTotal, stat.WriteBytesTotal)
185 }
186 }
187
188 //uncomment to see stats
189 //t.FailNow()
190}