Skip to content

Commit ad7f843

Browse files
authored
Capture container logs on failed CI runs. (#1285)
1 parent 8653507 commit ad7f843

File tree

5 files changed

+60
-5
lines changed

5 files changed

+60
-5
lines changed

.github/workflows/common.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ jobs:
8989
- name: Archive test logs
9090
if: always()
9191
run: |
92+
if [ -d "${APP_ROOT}/containers" ] && [ -n "${LOG_ROOT}" ]; then
93+
rsync -a --include='*/' --include='*.log' --exclude='*' \
94+
"${APP_ROOT}/containers/" \
95+
"${LOG_ROOT}/containers/"
96+
fi
9297
if [ -n "${LOG_ROOT}" ] && [ -d "${LOG_ROOT}" ] ; then
9398
echo "Collecting logs from ${LOG_ROOT}..."
9499
tar czf container-logs.tar.gz -C "$(dirname "${LOG_ROOT}")" "$(basename "${LOG_ROOT}")"

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ integration: init-block
187187
echo "Starting CLI integration tests" && \
188188
{ \
189189
exit_code=0; \
190+
CLITEST_LOG_ROOT=$(LOG_ROOT) && export CLITEST_LOG_ROOT ; \
190191
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLINetwork || exit_code=1 ; \
191192
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunLifecycle || exit_code=1 ; \
192193
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIExecCommand || exit_code=1 ; \

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ let package = Package(
7979
.product(name: "ContainerizationExtras", package: "containerization"),
8080
.product(name: "ContainerizationOS", package: "containerization"),
8181
"ContainerBuild",
82+
"ContainerLog",
8283
"ContainerResource",
8384
],
8485
path: "Tests/CLITests"

Tests/CLITests/Subcommands/Build/CLIBuilderTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ extension TestCLIBuildBase {
9393
}
9494

9595
@Test func testBuildAddFromSpecialDirs() throws {
96-
let tempDir = URL(filePath: "/tmp/container/.clitests" + testUUID)
96+
let tempDir = URL(filePath: "/tmp/container/.clitests/\(testSuite)/\(testName)")
9797
try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
9898

9999
defer {

Tests/CLITests/Utilities/CLITest.swift

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
import AsyncHTTPClient
18+
import ContainerLog
1819
import ContainerResource
1920
import Containerization
2021
import ContainerizationOS
2122
import Foundation
23+
import Logging
24+
import Synchronization
25+
import SystemPackage
2226
import Testing
2327

2428
class CLITest {
29+
private static let commandSeq = Mutex<Int>(0)
2530
struct Image: Codable {
2631
let reference: String
2732
}
@@ -46,14 +51,35 @@ class CLITest {
4651
let status: NetworkStatus?
4752
}
4853

49-
init() throws {}
50-
51-
let testUUID = UUID().uuidString
54+
let testName: String
55+
let testSuite: String
56+
var log: Logger
57+
58+
init() throws {
59+
let name = Test.current.map { $0.name.hasSuffix("()") ? String($0.name.dropLast(2)) : $0.name } ?? UUID().uuidString
60+
let suite = "\(type(of: self))"
61+
self.testName = name
62+
self.testSuite = suite
63+
let logger = Logger(label: "com.apple.container.test") { label in
64+
if let logRootString = ProcessInfo.processInfo.environment["CLITEST_LOG_ROOT"],
65+
!logRootString.isEmpty
66+
{
67+
let logPath = FilePath(logRootString).appending("clitests").appending(suite).appending(name + ".log")
68+
if let handler = try? FileLogHandler(label: label, category: "clitests", path: logPath) {
69+
return handler
70+
}
71+
}
72+
return StderrLogHandler()
73+
}
74+
self.log = logger
75+
self.log[metadataKey: "testID"] = "\(name)"
76+
self.log[metadataKey: "suite"] = "\(suite)"
77+
}
5278

5379
var testDir: URL! {
5480
let tempDir = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
5581
.appendingPathComponent(".clitests")
56-
.appendingPathComponent(testUUID)
82+
.appendingPathComponent(testName)
5783
try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
5884
return tempDir
5985
}
@@ -110,6 +136,18 @@ class CLITest {
110136
}
111137

112138
func run(arguments: [String], stdin: Data? = nil, currentDirectory: URL? = nil) throws -> (outputData: Data, output: String, error: String, status: Int32) {
139+
let seq = CLITest.commandSeq.withLock { counter in
140+
defer { counter += 1 }
141+
return counter
142+
}
143+
log.info(
144+
"command start",
145+
metadata: [
146+
"seq": "\(seq)",
147+
"args": "\(arguments.joined(separator: " "))",
148+
]
149+
)
150+
113151
let process = Process()
114152
process.executableURL = try executablePath
115153
process.arguments = arguments
@@ -142,6 +180,16 @@ class CLITest {
142180
let output = String(data: outputData, encoding: .utf8) ?? ""
143181
let error = String(data: errorData, encoding: .utf8) ?? ""
144182

183+
log.info(
184+
"command end",
185+
metadata: [
186+
"seq": "\(seq)",
187+
"status": "\(process.terminationStatus)",
188+
"stdout": "\(String(output.prefix(64)).debugDescription)",
189+
"stderr": "\(String(error.prefix(64)).debugDescription)",
190+
]
191+
)
192+
145193
return (outputData: outputData, output: output, error: error, status: process.terminationStatus)
146194
}
147195

0 commit comments

Comments
 (0)