mirror of
https://codeberg.org/secana/Forji.git
synced 2026-06-16 05:13:55 -07:00
The integration seeder gave no visible progress and could hang indefinitely. A `docker compose exec` for the admin-user step wedged on a transient Docker-on-macOS flake, and `DockerExec.run` waited on it with no timeout, blocking the whole UI test suite forever with no output (stdout was block-buffered, so the existing phase prints never flushed). - Line-buffer the seeder's stdout so phase progress streams live instead of being dumped all at once when stdout is a pipe. - Add numbered "[n/7] <phase> (Ns elapsed)" headers for a clear progress and timing signal. - Add a 60s timeout to `docker compose exec` and retry the admin-user step, so a hung exec fails fast and recovers instead of wedging the suite. Reviewed-on: https://codeberg.org/secana/Forji/pulls/69
46 lines
1.6 KiB
Swift
46 lines
1.6 KiB
Swift
import Foundation
|
|
|
|
enum DockerExec {
|
|
static func run(
|
|
composeFile: String, service: String,
|
|
command: [String],
|
|
timeout: TimeInterval = 60,
|
|
) throws {
|
|
let process = Process()
|
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
|
|
process.arguments = [
|
|
"docker", "compose", "-f", composeFile,
|
|
"exec", "-T", "-u", "git", service,
|
|
] + command
|
|
|
|
let stderrPipe = Pipe()
|
|
process.standardError = stderrPipe
|
|
process.standardOutput = FileHandle.nullDevice
|
|
|
|
let didExit = DispatchSemaphore(value: 0)
|
|
process.terminationHandler = { _ in didExit.signal() }
|
|
|
|
try process.run()
|
|
|
|
// Enforce a timeout so a hung `docker compose exec` (a known Docker-on-macOS flake)
|
|
// fails fast and can be retried, instead of blocking the whole seed indefinitely.
|
|
if didExit.wait(timeout: .now() + timeout) == .timedOut {
|
|
process.terminate()
|
|
_ = didExit.wait(timeout: .now() + 5)
|
|
throw SeedError.dockerExecTimedOut(
|
|
command: command.joined(separator: " "),
|
|
seconds: timeout,
|
|
)
|
|
}
|
|
|
|
if process.terminationStatus != 0 {
|
|
let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
|
|
let stderr = String(data: stderrData, encoding: .utf8) ?? ""
|
|
throw SeedError.dockerExecFailed(
|
|
command: command.joined(separator: " "),
|
|
exitCode: process.terminationStatus,
|
|
stderr: stderr,
|
|
)
|
|
}
|
|
}
|
|
}
|