github.com/buildtool/build-tools@v0.2.29-0.20240322150259-6a1d0a553c23/pkg/build/build_test.go (about) 1 // MIT License 2 // 3 // Copyright (c) 2018 buildtool 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 // SOFTWARE. 22 23 package build 24 25 import ( 26 "bufio" 27 "errors" 28 "fmt" 29 "io" 30 "os" 31 "path/filepath" 32 "strings" 33 "testing" 34 35 "github.com/apex/log" 36 "github.com/docker/docker/api/types" 37 mocks "gitlab.com/unboundsoftware/apex-mocks" 38 39 "github.com/stretchr/testify/assert" 40 41 "github.com/buildtool/build-tools/pkg" 42 "github.com/buildtool/build-tools/pkg/args" 43 "github.com/buildtool/build-tools/pkg/docker" 44 ) 45 46 var name string 47 48 func TestMain(m *testing.M) { 49 buildToolsTempDir, osTempDir := setup() 50 setupSession = func(dir string) Session { 51 return &MockSession{} 52 } 53 code := m.Run() 54 teardown(buildToolsTempDir, osTempDir) 55 os.Exit(code) 56 } 57 58 func setup() (string, string) { 59 name, _ = os.MkdirTemp(os.TempDir(), "build-tools") 60 temp, _ := os.MkdirTemp(os.TempDir(), "build-tools-temp") 61 os.Clearenv() 62 _ = os.Setenv("TMPDIR", temp) 63 _ = os.Setenv("TEMP", temp) 64 65 return name, temp 66 } 67 68 func teardown(buildToolsTempDir, osTempDir string) { 69 _ = os.RemoveAll(buildToolsTempDir) 70 _ = os.RemoveAll(osTempDir) 71 } 72 73 func TestBuild_BrokenConfig(t *testing.T) { 74 yaml := `ci: [] ` 75 file := filepath.Join(name, ".buildtools.yaml") 76 _ = os.WriteFile(file, []byte(yaml), 0777) 77 defer func() { _ = os.Remove(file) }() 78 79 logMock := mocks.New() 80 log.SetHandler(logMock) 81 log.SetLevel(log.DebugLevel) 82 client := &docker.MockDocker{} 83 defer func() { _ = os.RemoveAll(name) }() 84 _ = write(name, "Dockerfile", "FROM scratch") 85 err := build(client, name, Args{ 86 Globals: args.Globals{}, 87 Dockerfile: "Dockerfile", 88 BuildArgs: nil, 89 NoLogin: false, 90 NoPull: false, 91 }) 92 93 absPath, _ := filepath.Abs(filepath.Join(name, ".buildtools.yaml")) 94 assert.EqualError(t, err, "yaml: unmarshal errors:\n line 1: cannot unmarshal !!seq into config.CIConfig") 95 logMock.Check(t, []string{fmt.Sprintf("debug: Parsing config from file: <green>'%s'</green>\n", absPath)}) 96 } 97 98 func TestBuild_NoRegistry(t *testing.T) { 99 defer func() { _ = os.RemoveAll(name) }() 100 _ = write(name, "Dockerfile", "FROM scratch") 101 102 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 103 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 104 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 105 106 logMock := mocks.New() 107 log.SetHandler(logMock) 108 log.SetLevel(log.DebugLevel) 109 tmpDockerClient := dockerClient 110 dockerClient = func() (client docker.Client, e error) { 111 return &docker.MockDocker{}, nil 112 } 113 defer func() { dockerClient = tmpDockerClient }() 114 115 err := DoBuild(name, Args{Dockerfile: "Dockerfile"}) 116 assert.NoError(t, err) 117 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 118 "debug: Using registry <green>No docker registry</green>\n", 119 "debug: Authenticating against registry <green>No docker registry</green>\n", 120 "debug: Authentication <yellow>not supported</yellow> for registry <green>No docker registry</green>\n", 121 "debug: Using build variables commit <green>abc123</green> on branch <green>feature1</green>\n", 122 "debug: performing docker build with options (auths removed):\ntags:\n - noregistry/reponame:abc123\n - noregistry/reponame:feature1\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: feature1\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - noregistry/reponame:feature1\n - noregistry/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 123 "info: Build successful"}) 124 } 125 126 func TestBuild_LoginError(t *testing.T) { 127 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 128 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 129 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 130 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 131 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 132 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 133 134 logMock := mocks.New() 135 log.SetHandler(logMock) 136 log.SetLevel(log.DebugLevel) 137 client := &docker.MockDocker{LoginError: fmt.Errorf("invalid username/password")} 138 defer func() { _ = os.RemoveAll(name) }() 139 _ = write(name, "Dockerfile", "FROM scratch") 140 err := build(client, name, Args{ 141 Globals: args.Globals{}, 142 Dockerfile: "Dockerfile", 143 BuildArgs: nil, 144 NoLogin: false, 145 NoPull: false, 146 }) 147 148 assert.EqualError(t, err, "invalid username/password") 149 logMock.Check(t, []string{ 150 "debug: Using CI <green>Gitlab</green>\n", 151 "debug: Using registry <green>Dockerhub</green>\n", 152 "debug: Authenticating against registry <green>Dockerhub</green>\n", 153 "error: Unable to login\n"}) 154 } 155 156 func TestBuild_NoCI(t *testing.T) { 157 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 158 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 159 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 160 161 logMock := mocks.New() 162 log.SetHandler(logMock) 163 log.SetLevel(log.DebugLevel) 164 client := &docker.MockDocker{BuildError: []error{fmt.Errorf("build error")}} 165 166 _ = write(name, "Dockerfile", "FROM scratch") 167 err := build(client, name, Args{ 168 Globals: args.Globals{}, 169 Dockerfile: "Dockerfile", 170 BuildArgs: nil, 171 NoLogin: false, 172 NoPull: false, 173 }) 174 175 assert.EqualError(t, err, "commit and/or branch information is <red>missing</red> (perhaps you're not in a Git repository or forgot to set environment variables?)") 176 logMock.Check(t, []string{ 177 "debug: Using CI <green>none</green>\n", 178 "debug: Using registry <green>Dockerhub</green>\n", 179 "debug: Authenticating against registry <green>Dockerhub</green>\n", 180 "debug: Logged in\n", 181 }) 182 } 183 184 func TestBuild_BuildError(t *testing.T) { 185 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 186 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 187 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 188 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 189 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 190 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 191 192 logMock := mocks.New() 193 log.SetHandler(logMock) 194 log.SetLevel(log.DebugLevel) 195 client := &docker.MockDocker{BuildError: []error{fmt.Errorf("build error")}} 196 defer func() { _ = os.RemoveAll(name) }() 197 _ = write(name, "Dockerfile", "FROM scratch") 198 err := build(client, name, Args{ 199 Globals: args.Globals{}, 200 Dockerfile: "Dockerfile", 201 BuildArgs: nil, 202 NoLogin: false, 203 NoPull: false, 204 }) 205 206 assert.EqualError(t, err, "build error") 207 logMock.Check(t, []string{ 208 "debug: Using CI <green>Gitlab</green>\n", 209 "debug: Using registry <green>Dockerhub</green>\n", 210 "debug: Authenticating against registry <green>Dockerhub</green>\n", 211 "debug: Logged in\n", 212 "debug: Using build variables commit <green>abc123</green> on branch <green>feature1</green>\n", 213 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:feature1\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: feature1\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:feature1\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 214 }) 215 } 216 217 func TestBuild_BuildResponseError(t *testing.T) { 218 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 219 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 220 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 221 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 222 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 223 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 224 225 logMock := mocks.New() 226 log.SetHandler(logMock) 227 log.SetLevel(log.DebugLevel) 228 client := &docker.MockDocker{ResponseError: fmt.Errorf("build error")} 229 defer func() { _ = os.RemoveAll(name) }() 230 _ = write(name, "Dockerfile", "FROM scratch") 231 err := build(client, name, Args{ 232 Globals: args.Globals{}, 233 Dockerfile: "Dockerfile", 234 BuildArgs: nil, 235 NoLogin: false, 236 NoPull: false, 237 }) 238 239 assert.EqualError(t, err, "code: 123, status: build error") 240 logMock.Check(t, []string{ 241 "debug: Using CI <green>Gitlab</green>\n", 242 "debug: Using registry <green>Dockerhub</green>\n", 243 "debug: Authenticating against registry <green>Dockerhub</green>\n", 244 "debug: Logged in\n", 245 "debug: Using build variables commit <green>abc123</green> on branch <green>feature1</green>\n", 246 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:feature1\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: feature1\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:feature1\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 247 }) 248 } 249 250 func TestBuild_ErrorOutput(t *testing.T) { 251 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 252 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 253 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 254 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 255 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 256 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 257 258 logMock := mocks.New() 259 log.SetHandler(logMock) 260 log.SetLevel(log.DebugLevel) 261 client := &docker.MockDocker{BrokenOutput: true} 262 defer func() { _ = os.RemoveAll(name) }() 263 _ = write(name, "Dockerfile", "FROM scratch") 264 err := build(client, name, Args{ 265 Globals: args.Globals{}, 266 Dockerfile: "Dockerfile", 267 BuildArgs: nil, 268 NoLogin: false, 269 NoPull: false, 270 }) 271 272 assert.EqualError(t, err, "code: 1, status: some message") 273 logMock.Check(t, []string{ 274 "debug: Using CI <green>Gitlab</green>\n", 275 "debug: Using registry <green>Dockerhub</green>\n", 276 "debug: Authenticating against registry <green>Dockerhub</green>\n", 277 "debug: Logged in\n", 278 "debug: Using build variables commit <green>abc123</green> on branch <green>feature1</green>\n", 279 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:feature1\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: feature1\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:feature1\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 280 }) 281 } 282 283 func TestBuild_ValidOutput(t *testing.T) { 284 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 285 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 286 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 287 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 288 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 289 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 290 291 f, err := os.Open("testdata/build_body.txt") 292 assert.NoError(t, err) 293 logMock := mocks.New() 294 log.SetHandler(logMock) 295 log.SetLevel(log.DebugLevel) 296 client := &docker.MockDocker{ResponseBody: bufio.NewReader(f)} 297 defer func() { _ = os.RemoveAll(name) }() 298 _ = write(name, "Dockerfile", "FROM scratch") 299 err = build(client, name, Args{ 300 Globals: args.Globals{}, 301 Dockerfile: "Dockerfile", 302 BuildArgs: nil, 303 NoLogin: false, 304 NoPull: false, 305 }) 306 307 assert.NoError(t, err) 308 logMock.Check(t, []string{ 309 "debug: Using CI <green>Gitlab</green>\n", 310 "debug: Using registry <green>Dockerhub</green>\n", 311 "debug: Authenticating against registry <green>Dockerhub</green>\n", 312 "debug: Logged in\n", 313 "debug: Using build variables commit <green>abc123</green> on branch <green>feature1</green>\n", 314 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:feature1\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: feature1\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:feature1\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 315 "info: 1: msg 1\n2: msg 2\n", 316 }) 317 } 318 319 func TestBuild_BrokenBuildResult(t *testing.T) { 320 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 321 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 322 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 323 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 324 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 325 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 326 327 logMock := mocks.New() 328 log.SetHandler(logMock) 329 log.SetLevel(log.DebugLevel) 330 client := &docker.MockDocker{ResponseBody: strings.NewReader(`{"id":"moby.image.id","aux":{"id":123}}`)} 331 defer func() { _ = os.RemoveAll(name) }() 332 _ = write(name, "Dockerfile", "FROM scratch") 333 err := build(client, name, Args{ 334 Globals: args.Globals{}, 335 Dockerfile: "Dockerfile", 336 BuildArgs: nil, 337 NoLogin: false, 338 NoPull: false, 339 }) 340 341 assert.NoError(t, err) 342 logMock.Check(t, []string{ 343 "debug: Using CI <green>Gitlab</green>\n", 344 "debug: Using registry <green>Dockerhub</green>\n", 345 "debug: Authenticating against registry <green>Dockerhub</green>\n", 346 "debug: Logged in\n", 347 "debug: Using build variables commit <green>abc123</green> on branch <green>feature1</green>\n", 348 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:feature1\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: feature1\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:feature1\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 349 "error: failed to parse aux message: json: cannot unmarshal number into Go struct field BuildResult.ID of type string", 350 "info: ", 351 }) 352 } 353 354 func TestBuild_WithBuildArgs(t *testing.T) { 355 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 356 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 357 defer pkg.SetEnv("CI_COMMIT_SHA", "sha")() 358 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 359 client := &docker.MockDocker{} 360 defer func() { _ = os.RemoveAll(name) }() 361 _ = write(name, "Dockerfile", "FROM scratch") 362 363 err := build(client, name, Args{ 364 Globals: args.Globals{}, 365 Dockerfile: "Dockerfile", 366 BuildArgs: []string{"buildargs1=1", "buildargs2=2"}, 367 NoLogin: false, 368 NoPull: false, 369 }) 370 assert.NoError(t, err) 371 372 assert.Equal(t, 5, len(client.BuildOptions[0].BuildArgs)) 373 assert.Equal(t, "1", *client.BuildOptions[0].BuildArgs["buildargs1"]) 374 assert.Equal(t, "2", *client.BuildOptions[0].BuildArgs["buildargs2"]) 375 } 376 377 func TestBuild_WithStrangeBuildArg(t *testing.T) { 378 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 379 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 380 defer pkg.SetEnv("CI_COMMIT_SHA", "sha")() 381 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 382 defer pkg.SetEnv("buildargs4", "env-value")() 383 logMock := mocks.New() 384 log.SetHandler(logMock) 385 log.SetLevel(log.DebugLevel) 386 client := &docker.MockDocker{} 387 defer func() { _ = os.RemoveAll(name) }() 388 _ = write(name, "Dockerfile", "FROM scratch") 389 390 err := build(client, name, Args{ 391 Globals: args.Globals{}, 392 Dockerfile: "Dockerfile", 393 BuildArgs: []string{"buildargs1=1=1", "buildargs2", "buildargs3=", "buildargs4"}, 394 NoLogin: false, 395 NoPull: false, 396 }) 397 assert.NoError(t, err) 398 399 assert.Equal(t, 5, len(client.BuildOptions[0].BuildArgs)) 400 assert.Equal(t, "1=1", *client.BuildOptions[0].BuildArgs["buildargs1"]) 401 assert.Equal(t, "env-value", *client.BuildOptions[0].BuildArgs["buildargs4"]) 402 logMock.Check(t, []string{ 403 "debug: Using CI <green>Gitlab</green>\n", 404 "debug: Using registry <green>Dockerhub</green>\n", 405 "debug: Authenticating against registry <green>Dockerhub</green>\n", 406 "debug: Logged in\n", 407 "debug: Using build variables commit <green>sha</green> on branch <green>master</green>\n", 408 "debug: ignoring build-arg buildargs2\n", 409 "debug: ignoring build-arg buildargs3\n", 410 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:sha\n - repo/reponame:master\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: sha\n buildargs1: 1=1\n buildargs4: env-value\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 411 "info: Build successful"}) 412 } 413 414 func TestBuild_WithPlatform(t *testing.T) { 415 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 416 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 417 defer pkg.SetEnv("CI_COMMIT_SHA", "sha")() 418 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 419 logMock := mocks.New() 420 log.SetHandler(logMock) 421 log.SetLevel(log.DebugLevel) 422 client := &docker.MockDocker{} 423 defer func() { _ = os.RemoveAll(name) }() 424 _ = write(name, "Dockerfile", "FROM scratch") 425 426 err := build(client, name, Args{ 427 Globals: args.Globals{}, 428 Dockerfile: "Dockerfile", 429 NoLogin: false, 430 NoPull: false, 431 Platform: "linux/amd64", 432 }) 433 assert.NoError(t, err) 434 435 logMock.Check(t, []string{ 436 "info: building for platform <green>linux/amd64</green>\n", 437 "debug: Using CI <green>Gitlab</green>\n", 438 "debug: Using registry <green>Dockerhub</green>\n", 439 "debug: Authenticating against registry <green>Dockerhub</green>\n", 440 "debug: Logged in\n", 441 "debug: Using build variables commit <green>sha</green> on branch <green>master</green>\n", 442 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:sha\n - repo/reponame:master\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: sha\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: linux/amd64\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 443 "info: Build successful"}) 444 } 445 446 func TestBuild_WithSkipLogin(t *testing.T) { 447 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 448 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 449 defer pkg.SetEnv("CI_COMMIT_SHA", "sha")() 450 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 451 logMock := mocks.New() 452 log.SetHandler(logMock) 453 log.SetLevel(log.DebugLevel) 454 defer func() { _ = os.RemoveAll(name) }() 455 _ = write(name, "Dockerfile", "FROM scratch") 456 457 client := &docker.MockDocker{} 458 err := build(client, name, Args{ 459 Globals: args.Globals{}, 460 Dockerfile: "Dockerfile", 461 BuildArgs: nil, 462 NoLogin: true, 463 NoPull: false, 464 }) 465 assert.NoError(t, err) 466 logMock.Check(t, []string{ 467 "debug: Using CI <green>Gitlab</green>\n", 468 "debug: Using registry <green>Dockerhub</green>\n", 469 "debug: Login <yellow>disabled</yellow>\n", 470 "debug: Using build variables commit <green>sha</green> on branch <green>master</green>\n", 471 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:sha\n - repo/reponame:master\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: sha\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 472 "info: Build successful"}) 473 } 474 475 func TestBuild_FeatureBranch(t *testing.T) { 476 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 477 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 478 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "feature1")() 479 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 480 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 481 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 482 483 logMock := mocks.New() 484 log.SetHandler(logMock) 485 log.SetLevel(log.DebugLevel) 486 defer func() { _ = os.RemoveAll(name) }() 487 _ = write(name, "Dockerfile", "FROM scratch") 488 489 client := &docker.MockDocker{} 490 err := build(client, name, Args{ 491 Globals: args.Globals{}, 492 Dockerfile: "Dockerfile", 493 BuildArgs: nil, 494 NoLogin: false, 495 NoPull: false, 496 }) 497 498 assert.NoError(t, err) 499 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 500 assert.Equal(t, 3, len(client.BuildOptions[0].BuildArgs)) 501 assert.Equal(t, "abc123", *client.BuildOptions[0].BuildArgs["CI_COMMIT"]) 502 assert.Equal(t, "feature1", *client.BuildOptions[0].BuildArgs["CI_BRANCH"]) 503 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 504 assert.Equal(t, true, client.BuildOptions[0].Remove) 505 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 506 assert.Equal(t, []string{"repo/reponame:abc123", "repo/reponame:feature1"}, client.BuildOptions[0].Tags) 507 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 508 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 509 assert.Equal(t, true, client.BuildOptions[0].Remove) 510 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 511 assert.Equal(t, []string{"repo/reponame:abc123", "repo/reponame:feature1"}, client.BuildOptions[0].Tags) 512 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 513 "debug: Using registry <green>Dockerhub</green>\n", 514 "debug: Authenticating against registry <green>Dockerhub</green>\n", 515 "debug: Logged in\n", 516 "debug: Using build variables commit <green>abc123</green> on branch <green>feature1</green>\n", 517 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:feature1\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: feature1\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:feature1\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 518 "info: Build successful"}) 519 } 520 521 func TestBuild_MasterBranch(t *testing.T) { 522 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 523 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 524 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 525 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 526 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 527 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 528 529 logMock := mocks.New() 530 log.SetHandler(logMock) 531 log.SetLevel(log.DebugLevel) 532 client := &docker.MockDocker{} 533 defer func() { _ = os.RemoveAll(name) }() 534 _ = write(name, "Dockerfile", "FROM scratch") 535 536 err := build(client, name, Args{ 537 Globals: args.Globals{}, 538 Dockerfile: "Dockerfile", 539 BuildArgs: nil, 540 NoLogin: false, 541 NoPull: false, 542 }) 543 544 assert.NoError(t, err) 545 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 546 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 547 assert.Equal(t, true, client.BuildOptions[0].Remove) 548 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 549 assert.Equal(t, []string{"repo/reponame:abc123", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[0].Tags) 550 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 551 "debug: Using registry <green>Dockerhub</green>\n", 552 "debug: Authenticating against registry <green>Dockerhub</green>\n", 553 "debug: Logged in\n", 554 "debug: Using build variables commit <green>abc123</green> on branch <green>master</green>\n", 555 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:master\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 556 "info: Build successful"}) 557 } 558 559 func TestBuild_MainBranch(t *testing.T) { 560 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 561 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 562 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "main")() 563 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 564 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 565 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 566 567 logMock := mocks.New() 568 log.SetHandler(logMock) 569 log.SetLevel(log.DebugLevel) 570 client := &docker.MockDocker{} 571 defer func() { _ = os.RemoveAll(name) }() 572 _ = write(name, "Dockerfile", "FROM scratch") 573 574 err := build(client, name, Args{ 575 Globals: args.Globals{}, 576 Dockerfile: "Dockerfile", 577 BuildArgs: nil, 578 NoLogin: false, 579 NoPull: false, 580 }) 581 582 assert.NoError(t, err) 583 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 584 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 585 assert.Equal(t, true, client.BuildOptions[0].Remove) 586 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 587 assert.Equal(t, []string{"repo/reponame:abc123", "repo/reponame:main", "repo/reponame:latest"}, client.BuildOptions[0].Tags) 588 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 589 "debug: Using registry <green>Dockerhub</green>\n", 590 "debug: Authenticating against registry <green>Dockerhub</green>\n", 591 "debug: Logged in\n", 592 "debug: Using build variables commit <green>abc123</green> on branch <green>main</green>\n", 593 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:main\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: main\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:main\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 594 "info: Build successful"}) 595 } 596 597 func TestBuild_WithImageName(t *testing.T) { 598 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 599 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 600 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "main")() 601 defer pkg.SetEnv("IMAGE_NAME", "other")() 602 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 603 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 604 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 605 606 logMock := mocks.New() 607 log.SetHandler(logMock) 608 log.SetLevel(log.DebugLevel) 609 defer func() { _ = os.RemoveAll(name) }() 610 _ = write(name, "Dockerfile", "FROM scratch") 611 612 client := &docker.MockDocker{} 613 err := build(client, name, Args{ 614 Globals: args.Globals{}, 615 Dockerfile: "Dockerfile", 616 BuildArgs: nil, 617 NoLogin: false, 618 NoPull: false, 619 }) 620 621 assert.NoError(t, err) 622 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 623 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 624 assert.Equal(t, true, client.BuildOptions[0].Remove) 625 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 626 assert.Equal(t, []string{"repo/other:abc123", "repo/other:main", "repo/other:latest"}, client.BuildOptions[0].Tags) 627 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 628 "debug: Using registry <green>Dockerhub</green>\n", 629 "debug: Authenticating against registry <green>Dockerhub</green>\n", 630 "debug: Logged in\n", 631 "debug: Using build variables commit <green>abc123</green> on branch <green>main</green>\n", 632 "info: Using other as BuildName\n", 633 "info: Using other as BuildName\n", 634 "info: Using other as BuildName\n", 635 "debug: performing docker build with options (auths removed):\ntags:\n - repo/other:abc123\n - repo/other:main\n - repo/other:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: main\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/other:main\n - repo/other:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 636 "info: Build successful"}) 637 } 638 639 func TestBuild_BadDockerHost(t *testing.T) { 640 defer pkg.SetEnv("DOCKER_HOST", "abc-123")() 641 logMock := mocks.New() 642 log.SetHandler(logMock) 643 log.SetLevel(log.DebugLevel) 644 err := DoBuild(name, Args{}) 645 assert.EqualError(t, err, "unable to parse docker host `abc-123`") 646 logMock.Check(t, []string{}) 647 } 648 649 func TestBuild_Unreadable_Dockerfile(t *testing.T) { 650 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 651 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 652 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 653 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 654 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 655 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 656 657 defer func() { _ = os.RemoveAll(name) }() 658 dockerfile := filepath.Join(name, "Dockerfile") 659 _ = os.MkdirAll(dockerfile, 0777) 660 661 logMock := mocks.New() 662 log.SetHandler(logMock) 663 log.SetLevel(log.DebugLevel) 664 client := &docker.MockDocker{} 665 666 err := build(client, name, Args{ 667 Globals: args.Globals{}, 668 Dockerfile: "Dockerfile", 669 BuildArgs: nil, 670 NoLogin: false, 671 NoPull: false, 672 }) 673 674 assert.EqualError(t, err, fmt.Sprintf("read %s: is a directory", dockerfile)) 675 logMock.Check(t, []string{ 676 "debug: Using CI <green>Gitlab</green>\n", 677 "debug: Using registry <green>Dockerhub</green>\n", 678 "debug: Authenticating against registry <green>Dockerhub</green>\n", 679 "debug: Logged in\n", 680 fmt.Sprintf("error: <red>read %s: is a directory</red>", dockerfile), 681 }) 682 } 683 684 func TestBuild_HandleCaching(t *testing.T) { 685 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 686 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 687 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 688 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 689 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 690 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 691 692 logMock := mocks.New() 693 log.SetHandler(logMock) 694 log.SetLevel(log.DebugLevel) 695 client := &docker.MockDocker{} 696 697 dockerfile := ` 698 FROM scratch as build 699 RUN echo apa > file 700 FROM scratch as test 701 RUN echo cepa > file2 702 FROM scratch 703 COPY --from=build file . 704 COPY --from=test file2 . 705 ` 706 defer func() { _ = os.RemoveAll(name) }() 707 _ = write(name, "Dockerfile", dockerfile) 708 709 err := build(client, name, Args{ 710 Globals: args.Globals{}, 711 Dockerfile: "Dockerfile", 712 BuildArgs: nil, 713 NoLogin: false, 714 NoPull: false, 715 }) 716 717 assert.NoError(t, err) 718 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 719 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 720 assert.Equal(t, true, client.BuildOptions[0].Remove) 721 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 722 assert.Equal(t, 3, len(client.BuildOptions)) 723 assert.Equal(t, []string{"repo/reponame:build"}, client.BuildOptions[0].Tags) 724 assert.Equal(t, []string{"repo/reponame:test"}, client.BuildOptions[1].Tags) 725 assert.Equal(t, []string{"repo/reponame:abc123", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[2].Tags) 726 assert.Equal(t, []string{"repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[0].CacheFrom) 727 assert.Equal(t, []string{"repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[1].CacheFrom) 728 assert.Equal(t, []string{"repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[2].CacheFrom) 729 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 730 "debug: Using registry <green>Dockerhub</green>\n", 731 "debug: Authenticating against registry <green>Dockerhub</green>\n", 732 "debug: Logged in\n", 733 "debug: Using build variables commit <green>abc123</green> on branch <green>master</green>\n", 734 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:build\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: build\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 735 "info: Build successful", 736 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:test\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: test\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 737 "info: Build successful", 738 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:master\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 739 "info: Build successful"}) 740 } 741 742 func TestBuild_BrokenStage(t *testing.T) { 743 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 744 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 745 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 746 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 747 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 748 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 749 750 logMock := mocks.New() 751 log.SetHandler(logMock) 752 log.SetLevel(log.DebugLevel) 753 client := &docker.MockDocker{BuildError: []error{nil, errors.New("build error")}} 754 dockerfile := ` 755 FROM scratch as build 756 RUN echo apa > file 757 FROM scratch as test 758 RUN echo cepa > file2 759 FROM scratch 760 COPY --from=build file . 761 COPY --from=test file2 . 762 ` 763 defer func() { _ = os.RemoveAll(name) }() 764 _ = write(name, "Dockerfile", dockerfile) 765 err := build(client, name, Args{ 766 Globals: args.Globals{}, 767 Dockerfile: "Dockerfile", 768 BuildArgs: nil, 769 NoLogin: false, 770 NoPull: false, 771 }) 772 773 assert.EqualError(t, err, "build error") 774 logMock.Check(t, []string{ 775 "debug: Using CI <green>Gitlab</green>\n", 776 "debug: Using registry <green>Dockerhub</green>\n", 777 "debug: Authenticating against registry <green>Dockerhub</green>\n", 778 "debug: Logged in\n", 779 "debug: Using build variables commit <green>abc123</green> on branch <green>master</green>\n", 780 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:build\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: build\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 781 "info: Build successful", 782 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:test\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: test\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 783 }) 784 } 785 786 func TestBuild_ExportStage(t *testing.T) { 787 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 788 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 789 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 790 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 791 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 792 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 793 794 logMock := mocks.New() 795 log.SetHandler(logMock) 796 log.SetLevel(log.DebugLevel) 797 client := &docker.MockDocker{} 798 dockerfile := ` 799 FROM scratch as build 800 RUN echo apa > file 801 FROM scratch as test 802 RUN echo cepa > file2 803 FROM scratch as export 804 COPY --from=build file . 805 COPY --from=test file2 . 806 FROM scratch 807 COPY --from=build file . 808 COPY --from=test file2 . 809 ` 810 defer func() { _ = os.RemoveAll(name) }() 811 _ = write(name, "Dockerfile", dockerfile) 812 err := build(client, name, Args{ 813 Globals: args.Globals{}, 814 Dockerfile: "Dockerfile", 815 BuildArgs: nil, 816 NoLogin: false, 817 NoPull: false, 818 }) 819 820 assert.NoError(t, err) 821 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 822 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 823 assert.Equal(t, true, client.BuildOptions[0].Remove) 824 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 825 assert.Equal(t, 4, len(client.BuildOptions)) 826 assert.Equal(t, []string{"repo/reponame:build"}, client.BuildOptions[0].Tags) 827 assert.Equal(t, []string{"repo/reponame:test"}, client.BuildOptions[1].Tags) 828 assert.Equal(t, []string{"repo/reponame:export"}, client.BuildOptions[2].Tags) 829 assert.Equal(t, []types.ImageBuildOutput{ 830 { 831 Type: "local", 832 Attrs: map[string]string{}, 833 }, 834 }, client.BuildOptions[2].Outputs) 835 assert.Equal(t, []string{"repo/reponame:abc123", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[3].Tags) 836 assert.Equal(t, []string{"repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[0].CacheFrom) 837 assert.Equal(t, []string{"repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[1].CacheFrom) 838 assert.Equal(t, []string{"repo/reponame:export", "repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[2].CacheFrom) 839 assert.Equal(t, []string{"repo/reponame:export", "repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[3].CacheFrom) 840 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 841 "debug: Using registry <green>Dockerhub</green>\n", 842 "debug: Authenticating against registry <green>Dockerhub</green>\n", 843 "debug: Logged in\n", 844 "debug: Using build variables commit <green>abc123</green> on branch <green>master</green>\n", 845 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:build\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: build\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 846 "info: Build successful", 847 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:test\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: test\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 848 "info: Build successful", 849 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:export\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:export\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: export\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs:\n - type: local\n attrs: {}\n\n", 850 "info: Build successful", 851 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:master\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:export\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 852 "info: Build successful"}) 853 } 854 855 func TestBuild_ExportAsLastStage(t *testing.T) { 856 defer pkg.SetEnv("CI_COMMIT_SHA", "abc123")() 857 defer pkg.SetEnv("CI_PROJECT_NAME", "reponame")() 858 defer pkg.SetEnv("CI_COMMIT_REF_NAME", "master")() 859 defer pkg.SetEnv("DOCKERHUB_NAMESPACE", "repo")() 860 defer pkg.SetEnv("DOCKERHUB_USERNAME", "user")() 861 defer pkg.SetEnv("DOCKERHUB_PASSWORD", "pass")() 862 863 logMock := mocks.New() 864 log.SetHandler(logMock) 865 log.SetLevel(log.DebugLevel) 866 client := &docker.MockDocker{} 867 dockerfile := ` 868 FROM scratch as build 869 RUN echo apa > file 870 FROM scratch as test 871 RUN echo cepa > file2 872 FROM scratch as export 873 COPY --from=build file . 874 COPY --from=test file2 . 875 ` 876 defer func() { _ = os.RemoveAll(name) }() 877 _ = write(name, "Dockerfile", dockerfile) 878 err := build(client, name, Args{ 879 Globals: args.Globals{}, 880 Dockerfile: "Dockerfile", 881 BuildArgs: nil, 882 NoLogin: false, 883 NoPull: false, 884 }) 885 886 assert.NoError(t, err) 887 assert.Equal(t, "Dockerfile", client.BuildOptions[0].Dockerfile) 888 assert.Equal(t, int64(-1), client.BuildOptions[0].MemorySwap) 889 assert.Equal(t, true, client.BuildOptions[0].Remove) 890 assert.Equal(t, int64(256*1024*1024), client.BuildOptions[0].ShmSize) 891 assert.Equal(t, 4, len(client.BuildOptions)) 892 assert.Equal(t, []string{"repo/reponame:build"}, client.BuildOptions[0].Tags) 893 assert.Equal(t, []string{"repo/reponame:test"}, client.BuildOptions[1].Tags) 894 assert.Equal(t, []string{"repo/reponame:export"}, client.BuildOptions[2].Tags) 895 assert.Equal(t, []types.ImageBuildOutput{ 896 { 897 Type: "local", 898 Attrs: map[string]string{}, 899 }, 900 }, client.BuildOptions[2].Outputs) 901 assert.Equal(t, []string{"repo/reponame:abc123", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[3].Tags) 902 assert.Equal(t, []string{"repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[0].CacheFrom) 903 assert.Equal(t, []string{"repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[1].CacheFrom) 904 assert.Equal(t, []string{"repo/reponame:export", "repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[2].CacheFrom) 905 assert.Equal(t, []string{"repo/reponame:export", "repo/reponame:test", "repo/reponame:build", "repo/reponame:master", "repo/reponame:latest"}, client.BuildOptions[3].CacheFrom) 906 logMock.Check(t, []string{"debug: Using CI <green>Gitlab</green>\n", 907 "debug: Using registry <green>Dockerhub</green>\n", 908 "debug: Authenticating against registry <green>Dockerhub</green>\n", 909 "debug: Logged in\n", 910 "debug: Using build variables commit <green>abc123</green> on branch <green>master</green>\n", 911 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:build\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: build\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 912 "info: Build successful", 913 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:test\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: test\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 914 "info: Build successful", 915 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:export\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:export\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: export\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs:\n - type: local\n attrs: {}\n\n", 916 "info: Build successful", 917 "debug: performing docker build with options (auths removed):\ntags:\n - repo/reponame:abc123\n - repo/reponame:master\n - repo/reponame:latest\nsuppressoutput: false\nremotecontext: client-session\nnocache: false\nremove: true\nforceremove: false\npullparent: true\nisolation: \"\"\ncpusetcpus: \"\"\ncpusetmems: \"\"\ncpushares: 0\ncpuquota: 0\ncpuperiod: 0\nmemory: 0\nmemoryswap: -1\ncgroupparent: \"\"\nnetworkmode: \"\"\nshmsize: 268435456\ndockerfile: Dockerfile\nulimits: []\nbuildargs:\n BUILDKIT_INLINE_CACHE: \"1\"\n CI_BRANCH: master\n CI_COMMIT: abc123\nauthconfigs: {}\ncontext: null\nlabels: {}\nsquash: false\ncachefrom:\n - repo/reponame:export\n - repo/reponame:test\n - repo/reponame:build\n - repo/reponame:master\n - repo/reponame:latest\nsecurityopt: []\nextrahosts: []\ntarget: \"\"\nsessionid: \"\"\nplatform: \"\"\nversion: \"2\"\nbuildid: \"\"\noutputs: []\n\n", 918 "info: Build successful"}) 919 } 920 921 type brokenReader struct{} 922 923 func (b brokenReader) Read([]byte) (n int, err error) { 924 return 0, errors.New("read error") 925 } 926 927 var _ io.Reader = &brokenReader{} 928 929 func write(dir, file, content string) error { 930 if err := os.MkdirAll(filepath.Dir(filepath.Join(dir, file)), 0777); err != nil { 931 return err 932 } 933 return os.WriteFile(filepath.Join(dir, file), []byte(fmt.Sprintln(strings.TrimSpace(content))), 0666) 934 }