Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion Sources/ContainerCommands/BuildCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,33 @@ extension Application {
buildFilePath = resolvedPath
}

let buildFileData = try Data(contentsOf: URL(filePath: buildFilePath))
let buildFileData: Data
// Dockerfile should be read from stdin
if file == "-" {
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent("Dockerfile-\(UUID().uuidString)")
defer {
try? FileManager.default.removeItem(at: tempFile)
}

guard FileManager.default.createFile(atPath: tempFile.path(), contents: nil) else {
throw ContainerizationError(.internalError, message: "unable to create temporary file")
}

guard let fileHandle = try? FileHandle(forWritingTo: tempFile) else {
throw ContainerizationError(.internalError, message: "unable to open temporary file for writing")
}

let bufferSize = 4096
while true {
let chunk = FileHandle.standardInput.readData(ofLength: bufferSize)
if chunk.isEmpty { break }
fileHandle.write(chunk)
}
try fileHandle.close()
buildFileData = try Data(contentsOf: URL(filePath: tempFile.path()))
} else {
buildFileData = try Data(contentsOf: URL(filePath: buildFilePath))
}

let systemHealth = try await ClientHealthCheck.ping(timeout: .seconds(10))
let exportPath = systemHealth.appRoot
Expand Down
36 changes: 36 additions & 0 deletions Tests/CLITests/Subcommands/Build/CLIBuildBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,42 @@ class TestCLIBuildBase: CLITest {
return response.output
}

@discardableResult
func buildWithStdin(
tags: [String],
tempContext: URL,
dockerfileContents: String,
buildArgs: [String] = [],
otherArgs: [String] = []
) throws -> String {
let contextDir: URL = tempContext.appendingPathComponent("context")
let contextDirPath = contextDir.absoluteURL.path
var args = [
"build",
"-f",
"-",
]
for tag in tags {
args.append("-t")
args.append(tag)
}
for arg in buildArgs {
args.append("--build-arg")
args.append(arg)
}
args.append(contextDirPath)

args.append(contentsOf: otherArgs)

let stdinData = Data(dockerfileContents.utf8)
let response = try run(arguments: args, stdin: stdinData)
if response.status != 0 {
throw CLIError.executionFailed("build failed: stdout=\(response.output) stderr=\(response.error)")
}

return response.output
}

enum FileSystemEntry {
case file(
_ path: String,
Expand Down
15 changes: 15 additions & 0 deletions Tests/CLITests/Subcommands/Build/CLIBuilderTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -456,5 +456,20 @@ extension TestCLIBuildBase {
#expect(try self.inspectImage(tag2) == tag2, "expected to have successfully built \(tag2)")
#expect(try self.inspectImage(tag3) == tag3, "expected to have successfully built \(tag3)")
}

@Test func testBuildWithDockerfileFromStdin() throws {
let tempDir: URL = try createTempDir()
let dockerfile =
"""
FROM scratch

ADD emptyFile /
"""
let context: [FileSystemEntry] = [.file("emptyFile", content: .zeroFilled(size: 1))]
try createContext(tempDir: tempDir, dockerfile: "", context: context)
let imageName = "registry.local/stdin-file:\(UUID().uuidString)"
try buildWithStdin(tags: [imageName], tempContext: tempDir, dockerfileContents: dockerfile)
#expect(try self.inspectImage(imageName) == imageName, "expected to have successfully built \(imageName)")
}
}
}