chore: import upstream snapshot with attribution
container project - merge build / Invoke build (push) Successful in 1s
container project - merge build / Invoke build (push) Successful in 1s
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2026 Apple Inc. and the container project authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@Suite(.serialized)
|
||||
struct TestCLIBuilderLocalOutputSerial {
|
||||
@Test func testBuildLocalOutputHappyPath() async throws {
|
||||
try await ContainerFixture.with { f in
|
||||
try await f.withBuilder { f in
|
||||
// Comprehensive multi-stage build with context and build args.
|
||||
let dir = try f.createTempDir()
|
||||
let dockerfile =
|
||||
"""
|
||||
ARG MESSAGE=default
|
||||
FROM scratch AS builder
|
||||
ADD build.txt /build.txt
|
||||
ADD testfile.txt /hello.txt
|
||||
FROM scratch
|
||||
COPY --from=builder /build.txt /final.txt
|
||||
COPY --from=builder /hello.txt /app/hello.txt
|
||||
ADD message.txt /message.txt
|
||||
"""
|
||||
let context: [ContainerFixture.FileSystemEntry] = [
|
||||
.file("build.txt", content: .data("Building stage\n".data(using: .utf8)!)),
|
||||
.file("testfile.txt", content: .data("Hello from local build\n".data(using: .utf8)!)),
|
||||
.file("message.txt", content: .data("Hello from build args\n".data(using: .utf8)!)),
|
||||
]
|
||||
try f.createContext(dir: dir, dockerfile: dockerfile, context: context)
|
||||
let outputDir = dir.appending("comprehensive-local-output")
|
||||
let imageName = "local-comprehensive-test:\(UUID().uuidString)"
|
||||
let response = try f.buildWithPathsAndLocalOutput(
|
||||
tag: imageName, contextDir: dir, outputDir: outputDir,
|
||||
buildArgs: ["MESSAGE=Hello from build args"])
|
||||
#expect(response.contains(outputDir.string), "output should reference the export path")
|
||||
#expect(FileManager.default.fileExists(atPath: outputDir.string))
|
||||
let contents = try FileManager.default.contentsOfDirectory(atPath: outputDir.string)
|
||||
#expect(!contents.isEmpty, "output directory should contain files")
|
||||
|
||||
// Basic local output.
|
||||
let basicDir = try f.createTempDir()
|
||||
try f.createContext(
|
||||
dir: basicDir,
|
||||
dockerfile: "FROM scratch\nADD testfile.txt /hello.txt",
|
||||
context: [.file("testfile.txt", content: .data("Hello from basic build\n".data(using: .utf8)!))])
|
||||
let basicOutputDir = basicDir.appending("basic-local-output")
|
||||
let basicResponse = try f.buildWithPathsAndLocalOutput(
|
||||
tag: "local-basic-test:\(UUID().uuidString)", contextDir: basicDir, outputDir: basicOutputDir)
|
||||
#expect(basicResponse.contains(basicOutputDir.string))
|
||||
#expect(FileManager.default.fileExists(atPath: basicOutputDir.string))
|
||||
|
||||
// Build with context (COPY instruction).
|
||||
let ctxDir = try f.createTempDir()
|
||||
try f.createContext(
|
||||
dir: ctxDir,
|
||||
dockerfile: "FROM scratch\nCOPY testfile.txt /app/testfile.txt",
|
||||
context: [.file("testfile.txt", content: .data("Test content\n".data(using: .utf8)!))])
|
||||
let ctxOutputDir = ctxDir.appending("context-local-output")
|
||||
let ctxResponse = try f.buildWithPathsAndLocalOutput(
|
||||
tag: "local-context-test:\(UUID().uuidString)", contextDir: ctxDir, outputDir: ctxOutputDir)
|
||||
#expect(ctxResponse.contains(ctxOutputDir.string))
|
||||
#expect(FileManager.default.fileExists(atPath: ctxOutputDir.string))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test func testBuildLocalOutputEdgeCases() async throws {
|
||||
try await ContainerFixture.with { f in
|
||||
try await f.withBuilder { f in
|
||||
// Different paths for Dockerfile context and build context.
|
||||
let dockerfileDir = try f.createTempDir()
|
||||
try f.createContext(
|
||||
dir: dockerfileDir,
|
||||
dockerfile: "FROM scratch\nCOPY . /app",
|
||||
context: [.file("dockerfile-context.txt", content: .data("Dockerfile context\n".data(using: .utf8)!))])
|
||||
|
||||
let buildContextDir = try f.createTempDir()
|
||||
try f.createContext(
|
||||
dir: buildContextDir, dockerfile: "",
|
||||
context: [.file("build-context.txt", content: .data("Build context\n".data(using: .utf8)!))])
|
||||
|
||||
let outputDir = dockerfileDir.appending("diffpaths-local-output")
|
||||
let response = try f.buildWithPathsAndLocalOutput(
|
||||
tag: "local-diffpaths-test:\(UUID().uuidString)",
|
||||
contextDir: buildContextDir,
|
||||
dockerfilePath: dockerfileDir.appending("Dockerfile"),
|
||||
outputDir: outputDir)
|
||||
#expect(response.contains(outputDir.string))
|
||||
#expect(FileManager.default.fileExists(atPath: outputDir.string))
|
||||
|
||||
// Build into an existing output directory (should merge/overwrite).
|
||||
let existingDir = try f.createTempDir()
|
||||
try f.createContext(
|
||||
dir: existingDir,
|
||||
dockerfile: "FROM scratch\nADD newfile.txt /newfile.txt",
|
||||
context: [.file("newfile.txt", content: .data("New content\n".data(using: .utf8)!))])
|
||||
let existingOutputDir = existingDir.appending("existing-output")
|
||||
try FileManager.default.createDirectory(
|
||||
atPath: existingOutputDir.string, withIntermediateDirectories: true, attributes: nil)
|
||||
try "Existing content\n".data(using: .utf8)!
|
||||
.write(to: URL(filePath: existingOutputDir.appending("existing.txt").string), options: .atomic)
|
||||
let existingResponse = try f.buildWithPathsAndLocalOutput(
|
||||
tag: "local-existing-test:\(UUID().uuidString)",
|
||||
contextDir: existingDir, outputDir: existingOutputDir)
|
||||
#expect(existingResponse.contains(existingOutputDir.string))
|
||||
let contents = try FileManager.default.contentsOfDirectory(atPath: existingOutputDir.string)
|
||||
#expect(!contents.isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test func testBuildLocalOutputFailure() async throws {
|
||||
try await ContainerFixture.with { f in
|
||||
try await f.withBuilder { f in
|
||||
let dir = try f.createTempDir()
|
||||
try f.createContext(
|
||||
dir: dir,
|
||||
dockerfile: "FROM scratch\nADD test.txt /test.txt",
|
||||
context: [.file("test.txt", content: .data("test\n".data(using: .utf8)!))])
|
||||
|
||||
// An uncreateable path should cause the build to fail.
|
||||
let result = try f.run([
|
||||
"build",
|
||||
"-f", dir.appending("Dockerfile").string,
|
||||
"-t", "local-invalid-test:\(UUID().uuidString)",
|
||||
"--output", "type=local,dest=/nonexistent/invalid/path",
|
||||
dir.appending("context").string,
|
||||
])
|
||||
#expect(result.status != 0, "build with invalid output path should fail")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user