mirror of
https://github.com/apple/container.git
synced 2026-09-22 15:45:38 +00:00
- Closes #1308. - Applies dependency and code changes similar to apple/containerization#578. - Upgrades hawkeye to latest version. - Update StderrLogHandler not to create a (non-Sendable) Swift time formatter object for every log message.
This commit is contained in:
+37
-10
@@ -24,8 +24,35 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-swift.git",
|
||||
"state" : {
|
||||
"revision" : "a56a157218877ef3e9625f7e1f7b2cb7e46ead1b",
|
||||
"version" : "1.26.1"
|
||||
"revision" : "ac715c584bb1e2e5cdfb7684ccb46fab8dafc641",
|
||||
"version" : "1.27.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-swift-2",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-swift-2.git",
|
||||
"state" : {
|
||||
"revision" : "f28854bc760a116e053fdfc4a48a9428c34625c0",
|
||||
"version" : "2.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-swift-nio-transport",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-swift-nio-transport.git",
|
||||
"state" : {
|
||||
"revision" : "f37e0c2d293cea668b11e10e1fb1c24cb40781ff",
|
||||
"version" : "2.4.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "grpc-swift-protobuf",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/grpc/grpc-swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "19153231a03c2fda1f4ea60da1b92a2cb9c011d8",
|
||||
"version" : "2.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -42,8 +69,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-argument-parser.git",
|
||||
"state" : {
|
||||
"revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3",
|
||||
"version" : "1.6.1"
|
||||
"revision" : "c5d11a805e765f52ba34ec7284bd4fcd6ba68615",
|
||||
"version" : "1.7.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -141,8 +168,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-log.git",
|
||||
"state" : {
|
||||
"revision" : "ce592ae52f982c847a4efc0dd881cc9eb32d29f2",
|
||||
"version" : "1.6.4"
|
||||
"revision" : "bbd81b6725ae874c69e9b8c8804d462356b55523",
|
||||
"version" : "1.10.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -204,8 +231,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-protobuf.git",
|
||||
"state" : {
|
||||
"revision" : "102a647b573f60f73afdce5613a51d71349fe507",
|
||||
"version" : "1.30.0"
|
||||
"revision" : "86970144a0b86068c81ff48ee29b3f97cae0b879",
|
||||
"version" : "1.36.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -222,8 +249,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-system.git",
|
||||
"state" : {
|
||||
"revision" : "890830fff1a577dc83134890c7984020c5f6b43b",
|
||||
"version" : "1.6.2"
|
||||
"revision" : "7c6ad0fc39d0763e0b699210e4124afd5041c5df",
|
||||
"version" : "1.6.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+15
-5
@@ -52,9 +52,11 @@ let package = Package(
|
||||
.package(url: "https://github.com/apple/swift-collections.git", from: "1.2.0"),
|
||||
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
|
||||
.package(url: "https://github.com/apple/swift-nio.git", from: "2.80.0"),
|
||||
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.29.0"),
|
||||
.package(url: "https://github.com/apple/swift-system.git", from: "1.4.0"),
|
||||
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.26.0"),
|
||||
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.36.0"),
|
||||
.package(url: "https://github.com/apple/swift-system.git", from: "1.6.4"),
|
||||
.package(url: "https://github.com/grpc/grpc-swift-2.git", from: "2.3.0"),
|
||||
.package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", from: "2.4.4"),
|
||||
.package(url: "https://github.com/grpc/grpc-swift-protobuf.git", from: "2.2.0"),
|
||||
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.20.1"),
|
||||
.package(url: "https://github.com/swiftlang/swift-docc-plugin.git", from: "1.1.0"),
|
||||
.package(url: "https://github.com/mattt/swift-toml.git", from: "2.0.0"),
|
||||
@@ -110,10 +112,14 @@ let package = Package(
|
||||
dependencies: [
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
.product(name: "NIO", package: "swift-nio"),
|
||||
.product(name: "NIOPosix", package: "swift-nio"),
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
.product(name: "ContainerizationArchive", package: "containerization"),
|
||||
.product(name: "ContainerizationOCI", package: "containerization"),
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "GRPCCore", package: "grpc-swift-2"),
|
||||
.product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"),
|
||||
.product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"),
|
||||
"ContainerAPIClient",
|
||||
]
|
||||
),
|
||||
@@ -139,7 +145,9 @@ let package = Package(
|
||||
.product(name: "ContainerizationExtras", package: "containerization"),
|
||||
.product(name: "ContainerizationOS", package: "containerization"),
|
||||
.product(name: "ContainerizationEXT4", package: "containerization"),
|
||||
.product(name: "GRPC", package: "grpc-swift"),
|
||||
.product(name: "GRPCCore", package: "grpc-swift-2"),
|
||||
.product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"),
|
||||
.product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"),
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
"ContainerAPIService",
|
||||
"ContainerAPIClient",
|
||||
@@ -308,7 +316,9 @@ let package = Package(
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
.product(name: "GRPC", package: "grpc-swift"),
|
||||
.product(name: "GRPCCore", package: "grpc-swift-2"),
|
||||
.product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"),
|
||||
.product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"),
|
||||
.product(name: "Containerization", package: "containerization"),
|
||||
"ContainerLog",
|
||||
"ContainerPlugin",
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@ $(PROTOC):
|
||||
.PHONY: protoc-gen-swift
|
||||
protoc-gen-swift:
|
||||
@$(SWIFT) build --product protoc-gen-swift
|
||||
@$(SWIFT) build --product protoc-gen-grpc-swift
|
||||
@$(SWIFT) build --product protoc-gen-grpc-swift-2
|
||||
|
||||
.PHONY: protos
|
||||
protos: $(PROTOC) protoc-gen-swift
|
||||
@@ -47,7 +47,7 @@ protos: $(PROTOC) protoc-gen-swift
|
||||
cd $(LOCAL_DIR) && git clone --branch $(BUILDER_SHIM_VERSION) --depth 1 $(BUILDER_SHIM_REPO); \
|
||||
fi
|
||||
@$(PROTOC) $(LOCAL_DIR)/container-builder-shim/pkg/api/Builder.proto \
|
||||
--plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift \
|
||||
--plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift-2 \
|
||||
--plugin=protoc-gen-swift=$(BUILD_BIN_DIR)/protoc-gen-swift \
|
||||
--proto_path=$(LOCAL_DIR)/container-builder-shim/pkg/api \
|
||||
--grpc-swift_out="Sources/ContainerBuild" \
|
||||
|
||||
@@ -24,10 +24,6 @@ public typealias ClientStream = Com_Apple_Container_Build_V1_ClientStream
|
||||
public typealias ServerStream = Com_Apple_Container_Build_V1_ServerStream
|
||||
public typealias ImageTransfer = Com_Apple_Container_Build_V1_ImageTransfer
|
||||
public typealias BuildTransfer = Com_Apple_Container_Build_V1_BuildTransfer
|
||||
public typealias BuilderClient = Com_Apple_Container_Build_V1_BuilderNIOClient
|
||||
public typealias BuilderClientAsync = Com_Apple_Container_Build_V1_BuilderAsyncClient
|
||||
public typealias BuilderClientProtocol = Com_Apple_Container_Build_V1_BuilderClientProtocol
|
||||
public typealias BuilderClientAsyncProtocol = Com_Apple_Container_Build_V1_BuilderAsyncClient
|
||||
|
||||
extension BuildTransfer {
|
||||
func stage() -> String? {
|
||||
|
||||
@@ -20,7 +20,7 @@ import ContainerizationArchive
|
||||
import ContainerizationOCI
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
import GRPC
|
||||
import GRPCCore
|
||||
|
||||
actor BuildFSSync: BuildPipelineHandler {
|
||||
let contextDir: URL
|
||||
|
||||
@@ -18,7 +18,7 @@ import ContainerAPIClient
|
||||
import Containerization
|
||||
import ContainerizationOCI
|
||||
import Foundation
|
||||
import GRPC
|
||||
import GRPCCore
|
||||
import Logging
|
||||
import TerminalProgress
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
import Foundation
|
||||
import GRPC
|
||||
import GRPCCore
|
||||
import NIO
|
||||
|
||||
protocol BuildPipelineHandler: Sendable {
|
||||
@@ -35,10 +35,10 @@ public actor BuildPipeline {
|
||||
]
|
||||
}
|
||||
|
||||
public func run(
|
||||
public func run<S: AsyncSequence & Sendable>(
|
||||
sender: AsyncStream<ClientStream>.Continuation,
|
||||
receiver: GRPCAsyncResponseStream<ServerStream>
|
||||
) async throws {
|
||||
receiver: S
|
||||
) async throws where S.Element == ServerStream {
|
||||
defer { sender.finish() }
|
||||
try await untilFirstError { group in
|
||||
for try await packet in receiver {
|
||||
|
||||
@@ -19,7 +19,7 @@ import Containerization
|
||||
import ContainerizationArchive
|
||||
import ContainerizationOCI
|
||||
import Foundation
|
||||
import GRPC
|
||||
import GRPCCore
|
||||
|
||||
struct BuildRemoteContentProxy: BuildPipelineHandler {
|
||||
let local: ContentStore
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import ContainerizationOS
|
||||
import Foundation
|
||||
import GRPC
|
||||
import GRPCCore
|
||||
import NIO
|
||||
|
||||
actor BuildStdio: BuildPipelineHandler {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,11 @@
|
||||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
#if canImport(FoundationEssentials)
|
||||
import FoundationEssentials
|
||||
#else
|
||||
import Foundation
|
||||
#endif
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
@@ -213,7 +217,7 @@ public struct Com_Apple_Container_Build_V1_ClientStream: @unchecked Sendable {
|
||||
|
||||
/// A unique ID for the build.
|
||||
public var buildID: String {
|
||||
get {return _storage._buildID}
|
||||
get {_storage._buildID}
|
||||
set {_uniqueStorage()._buildID = newValue}
|
||||
}
|
||||
|
||||
@@ -314,7 +318,7 @@ public struct Com_Apple_Container_Build_V1_RunComplete: Sendable {
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public struct Com_Apple_Container_Build_V1_BuildTransfer: @unchecked Sendable {
|
||||
public struct Com_Apple_Container_Build_V1_BuildTransfer: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
@@ -328,21 +332,21 @@ public struct Com_Apple_Container_Build_V1_BuildTransfer: @unchecked Sendable {
|
||||
|
||||
/// The absolute path to the source from the server perspective.
|
||||
public var source: String {
|
||||
get {return _source ?? String()}
|
||||
get {_source ?? String()}
|
||||
set {_source = newValue}
|
||||
}
|
||||
/// Returns true if `source` has been explicitly set.
|
||||
public var hasSource: Bool {return self._source != nil}
|
||||
public var hasSource: Bool {self._source != nil}
|
||||
/// Clears the value of `source`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearSource() {self._source = nil}
|
||||
|
||||
/// The absolute path for the destination from the server perspective.
|
||||
public var destination: String {
|
||||
get {return _destination ?? String()}
|
||||
get {_destination ?? String()}
|
||||
set {_destination = newValue}
|
||||
}
|
||||
/// Returns true if `destination` has been explicitly set.
|
||||
public var hasDestination: Bool {return self._destination != nil}
|
||||
public var hasDestination: Bool {self._destination != nil}
|
||||
/// Clears the value of `destination`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearDestination() {self._destination = nil}
|
||||
|
||||
@@ -366,7 +370,7 @@ public struct Com_Apple_Container_Build_V1_BuildTransfer: @unchecked Sendable {
|
||||
fileprivate var _destination: String? = nil
|
||||
}
|
||||
|
||||
public struct Com_Apple_Container_Build_V1_ImageTransfer: @unchecked Sendable {
|
||||
public struct Com_Apple_Container_Build_V1_ImageTransfer: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
@@ -383,11 +387,11 @@ public struct Com_Apple_Container_Build_V1_ImageTransfer: @unchecked Sendable {
|
||||
|
||||
/// The descriptor for the image content.
|
||||
public var descriptor: Com_Apple_Container_Build_V1_Descriptor {
|
||||
get {return _descriptor ?? Com_Apple_Container_Build_V1_Descriptor()}
|
||||
get {_descriptor ?? Com_Apple_Container_Build_V1_Descriptor()}
|
||||
set {_descriptor = newValue}
|
||||
}
|
||||
/// Returns true if `descriptor` has been explicitly set.
|
||||
public var hasDescriptor: Bool {return self._descriptor != nil}
|
||||
public var hasDescriptor: Bool {self._descriptor != nil}
|
||||
/// Clears the value of `descriptor`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearDescriptor() {self._descriptor = nil}
|
||||
|
||||
@@ -414,7 +418,7 @@ public struct Com_Apple_Container_Build_V1_ServerStream: @unchecked Sendable {
|
||||
|
||||
/// A unique ID for the build.
|
||||
public var buildID: String {
|
||||
get {return _storage._buildID}
|
||||
get {_storage._buildID}
|
||||
set {_uniqueStorage()._buildID = newValue}
|
||||
}
|
||||
|
||||
@@ -481,7 +485,7 @@ public struct Com_Apple_Container_Build_V1_ServerStream: @unchecked Sendable {
|
||||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
public struct Com_Apple_Container_Build_V1_IO: @unchecked Sendable {
|
||||
public struct Com_Apple_Container_Build_V1_IO: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
@@ -551,11 +555,11 @@ public struct Com_Apple_Container_Build_V1_Descriptor: Sendable {
|
||||
public var annotations: Dictionary<String,String> = [:]
|
||||
|
||||
public var platform: Com_Apple_Container_Build_V1_Platform {
|
||||
get {return _platform ?? Com_Apple_Container_Build_V1_Platform()}
|
||||
get {_platform ?? Com_Apple_Container_Build_V1_Platform()}
|
||||
set {_platform = newValue}
|
||||
}
|
||||
/// Returns true if `platform` has been explicitly set.
|
||||
public var hasPlatform: Bool {return self._platform != nil}
|
||||
public var hasPlatform: Bool {self._platform != nil}
|
||||
/// Clears the value of `platform`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearPlatform() {self._platform = nil}
|
||||
|
||||
@@ -571,25 +575,15 @@ public struct Com_Apple_Container_Build_V1_Descriptor: Sendable {
|
||||
fileprivate let _protobuf_package = "com.apple.container.build.v1"
|
||||
|
||||
extension Com_Apple_Container_Build_V1_TransferDirection: SwiftProtobuf._ProtoNameProviding {
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "INTO"),
|
||||
1: .same(proto: "OUTOF"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0INTO\0\u{1}OUTOF\0")
|
||||
}
|
||||
|
||||
extension Com_Apple_Container_Build_V1_Stdio: SwiftProtobuf._ProtoNameProviding {
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "STDIN"),
|
||||
1: .same(proto: "STDOUT"),
|
||||
2: .same(proto: "STDERR"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0STDIN\0\u{1}STDOUT\0\u{1}STDERR\0")
|
||||
}
|
||||
|
||||
extension Com_Apple_Container_Build_V1_BuildErrorType: SwiftProtobuf._ProtoNameProviding {
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
0: .same(proto: "BUILD_FAILED"),
|
||||
1: .same(proto: "INTERNAL"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0BUILD_FAILED\0\u{1}INTERNAL\0")
|
||||
}
|
||||
|
||||
extension Com_Apple_Container_Build_V1_InfoRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
@@ -632,13 +626,7 @@ extension Com_Apple_Container_Build_V1_InfoResponse: SwiftProtobuf.Message, Swif
|
||||
|
||||
extension Com_Apple_Container_Build_V1_CreateBuildRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".CreateBuildRequest"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "stage_name"),
|
||||
2: .same(proto: "tag"),
|
||||
3: .same(proto: "metadata"),
|
||||
4: .standard(proto: "build_args"),
|
||||
5: .same(proto: "debug"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}stage_name\0\u{1}tag\0\u{1}metadata\0\u{3}build_args\0\u{1}debug\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -688,10 +676,7 @@ extension Com_Apple_Container_Build_V1_CreateBuildRequest: SwiftProtobuf.Message
|
||||
|
||||
extension Com_Apple_Container_Build_V1_CreateBuildResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".CreateBuildResponse"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "build_id"),
|
||||
2: .same(proto: "metadata"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}build_id\0\u{1}metadata\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -726,13 +711,7 @@ extension Com_Apple_Container_Build_V1_CreateBuildResponse: SwiftProtobuf.Messag
|
||||
|
||||
extension Com_Apple_Container_Build_V1_ClientStream: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".ClientStream"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "build_id"),
|
||||
2: .same(proto: "signal"),
|
||||
3: .same(proto: "command"),
|
||||
4: .standard(proto: "build_transfer"),
|
||||
5: .standard(proto: "image_transfer"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}build_id\0\u{1}signal\0\u{1}command\0\u{3}build_transfer\0\u{3}image_transfer\0")
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _buildID: String = String()
|
||||
@@ -876,9 +855,7 @@ extension Com_Apple_Container_Build_V1_ClientStream: SwiftProtobuf.Message, Swif
|
||||
|
||||
extension Com_Apple_Container_Build_V1_Signal: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".Signal"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "signal"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}signal\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -908,10 +885,7 @@ extension Com_Apple_Container_Build_V1_Signal: SwiftProtobuf.Message, SwiftProto
|
||||
|
||||
extension Com_Apple_Container_Build_V1_Run: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".Run"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "id"),
|
||||
2: .same(proto: "command"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}command\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -946,9 +920,7 @@ extension Com_Apple_Container_Build_V1_Run: SwiftProtobuf.Message, SwiftProtobuf
|
||||
|
||||
extension Com_Apple_Container_Build_V1_RunComplete: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".RunComplete"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "id"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -978,16 +950,7 @@ extension Com_Apple_Container_Build_V1_RunComplete: SwiftProtobuf.Message, Swift
|
||||
|
||||
extension Com_Apple_Container_Build_V1_BuildTransfer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".BuildTransfer"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "id"),
|
||||
2: .same(proto: "direction"),
|
||||
3: .same(proto: "source"),
|
||||
4: .same(proto: "destination"),
|
||||
5: .same(proto: "data"),
|
||||
6: .same(proto: "complete"),
|
||||
7: .standard(proto: "is_directory"),
|
||||
8: .same(proto: "metadata"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}direction\0\u{1}source\0\u{1}destination\0\u{1}data\0\u{1}complete\0\u{3}is_directory\0\u{1}metadata\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -1056,15 +1019,7 @@ extension Com_Apple_Container_Build_V1_BuildTransfer: SwiftProtobuf.Message, Swi
|
||||
|
||||
extension Com_Apple_Container_Build_V1_ImageTransfer: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".ImageTransfer"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "id"),
|
||||
2: .same(proto: "direction"),
|
||||
3: .same(proto: "tag"),
|
||||
4: .same(proto: "descriptor"),
|
||||
5: .same(proto: "data"),
|
||||
6: .same(proto: "complete"),
|
||||
7: .same(proto: "metadata"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}direction\0\u{1}tag\0\u{1}descriptor\0\u{1}data\0\u{1}complete\0\u{1}metadata\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -1128,14 +1083,7 @@ extension Com_Apple_Container_Build_V1_ImageTransfer: SwiftProtobuf.Message, Swi
|
||||
|
||||
extension Com_Apple_Container_Build_V1_ServerStream: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".ServerStream"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "build_id"),
|
||||
2: .same(proto: "io"),
|
||||
3: .standard(proto: "build_error"),
|
||||
4: .standard(proto: "command_complete"),
|
||||
5: .standard(proto: "build_transfer"),
|
||||
6: .standard(proto: "image_transfer"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}build_id\0\u{1}io\0\u{3}build_error\0\u{3}command_complete\0\u{3}build_transfer\0\u{3}image_transfer\0")
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _buildID: String = String()
|
||||
@@ -1296,10 +1244,7 @@ extension Com_Apple_Container_Build_V1_ServerStream: SwiftProtobuf.Message, Swif
|
||||
|
||||
extension Com_Apple_Container_Build_V1_IO: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".IO"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "type"),
|
||||
2: .same(proto: "data"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}type\0\u{1}data\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -1334,10 +1279,7 @@ extension Com_Apple_Container_Build_V1_IO: SwiftProtobuf.Message, SwiftProtobuf.
|
||||
|
||||
extension Com_Apple_Container_Build_V1_BuildError: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".BuildError"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "type"),
|
||||
2: .same(proto: "message"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}type\0\u{1}message\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -1372,13 +1314,7 @@ extension Com_Apple_Container_Build_V1_BuildError: SwiftProtobuf.Message, SwiftP
|
||||
|
||||
extension Com_Apple_Container_Build_V1_Platform: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".Platform"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .same(proto: "architecture"),
|
||||
2: .same(proto: "os"),
|
||||
3: .standard(proto: "os_version"),
|
||||
4: .standard(proto: "os_features"),
|
||||
5: .same(proto: "variant"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}architecture\0\u{1}os\0\u{3}os_version\0\u{3}os_features\0\u{1}variant\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
@@ -1428,14 +1364,7 @@ extension Com_Apple_Container_Build_V1_Platform: SwiftProtobuf.Message, SwiftPro
|
||||
|
||||
extension Com_Apple_Container_Build_V1_Descriptor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".Descriptor"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "media_type"),
|
||||
2: .same(proto: "digest"),
|
||||
3: .same(proto: "size"),
|
||||
4: .same(proto: "urls"),
|
||||
5: .same(proto: "annotations"),
|
||||
6: .same(proto: "platform"),
|
||||
]
|
||||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}media_type\0\u{1}digest\0\u{1}size\0\u{1}urls\0\u{1}annotations\0\u{1}platform\0")
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
|
||||
@@ -19,58 +19,72 @@ import Containerization
|
||||
import ContainerizationOCI
|
||||
import ContainerizationOS
|
||||
import Foundation
|
||||
import GRPC
|
||||
import GRPCCore
|
||||
import GRPCNIOTransportHTTP2
|
||||
import Logging
|
||||
import NIO
|
||||
import NIOCore
|
||||
import NIOHPACK
|
||||
import NIOHTTP2
|
||||
import NIOPosix
|
||||
|
||||
public struct Builder: Sendable {
|
||||
public static let builderContainerId = "buildkit"
|
||||
|
||||
let client: BuilderClientProtocol
|
||||
let clientAsync: BuilderClientAsyncProtocol
|
||||
let client: Com_Apple_Container_Build_V1_Builder.Client<HTTP2ClientTransport.WrappedChannel>
|
||||
let grpcClient: GRPCClient<HTTP2ClientTransport.WrappedChannel>
|
||||
let group: EventLoopGroup
|
||||
let builderShimSocket: FileHandle
|
||||
let channel: GRPCChannel
|
||||
let clientTask: Task<Void, any Swift.Error>
|
||||
let logger: Logger
|
||||
|
||||
public init(socket: FileHandle, group: EventLoopGroup) throws {
|
||||
public init(socket: FileHandle, group: EventLoopGroup, logger: Logger) throws {
|
||||
try socket.setSendBufSize(4 << 20)
|
||||
try socket.setRecvBufSize(2 << 20)
|
||||
var config = ClientConnection.Configuration.default(
|
||||
target: .connectedSocket(socket.fileDescriptor),
|
||||
eventLoopGroup: group
|
||||
)
|
||||
config.connectionIdleTimeout = TimeAmount(.seconds(600))
|
||||
config.connectionKeepalive = .init(
|
||||
interval: TimeAmount(.seconds(600)),
|
||||
timeout: TimeAmount(.seconds(500)),
|
||||
permitWithoutCalls: true
|
||||
)
|
||||
config.connectionBackoff = .init(
|
||||
initialBackoff: TimeInterval(1),
|
||||
maximumBackoff: TimeInterval(10)
|
||||
)
|
||||
config.callStartBehavior = .fastFailure
|
||||
config.httpMaxFrameSize = 8 << 10
|
||||
config.maximumReceiveMessageLength = 512 << 20
|
||||
config.httpTargetWindowSize = 16 << 10
|
||||
|
||||
let channel = ClientConnection(configuration: config)
|
||||
self.channel = channel
|
||||
self.clientAsync = BuilderClientAsync(channel: channel)
|
||||
self.client = BuilderClient(channel: channel)
|
||||
let channel = try ClientBootstrap(group: group)
|
||||
.channelInitializer { channel in
|
||||
channel.eventLoop.makeCompletedFuture(withResultOf: {
|
||||
try channel.pipeline.syncOperations.addHandler(HTTP2ConnectBufferingHandler())
|
||||
})
|
||||
}
|
||||
.withConnectedSocket(socket.fileDescriptor)
|
||||
.wait()
|
||||
|
||||
let transport = HTTP2ClientTransport.WrappedChannel.wrapping(
|
||||
channel: channel
|
||||
)
|
||||
|
||||
let grpcClient = GRPCClient(transport: transport)
|
||||
self.grpcClient = grpcClient
|
||||
self.client = Com_Apple_Container_Build_V1_Builder.Client(wrapping: grpcClient)
|
||||
self.group = group
|
||||
self.builderShimSocket = socket
|
||||
}
|
||||
self.logger = logger
|
||||
|
||||
public func info() throws -> InfoResponse {
|
||||
let resp = self.client.info(InfoRequest(), callOptions: CallOptions())
|
||||
return try resp.response.wait()
|
||||
// Start the client connection loop in a background task
|
||||
self.clientTask = Task {
|
||||
do {
|
||||
try await grpcClient.runConnections()
|
||||
} catch is CancellationError {
|
||||
// Expected during graceful shutdown - re-throw
|
||||
throw CancellationError()
|
||||
} catch let error as RPCError where error.code == .unavailable {
|
||||
// Connection closed - this is expected when the container stops
|
||||
logger.debug("gRPC connection closed: \(error)")
|
||||
throw error
|
||||
} catch {
|
||||
// Log unexpected connection errors
|
||||
logger.error("gRPC client connection error: \(error)")
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func info() async throws -> InfoResponse {
|
||||
let opts = CallOptions(timeLimit: .timeout(.seconds(30)))
|
||||
return try await self.clientAsync.info(InfoRequest(), callOptions: opts)
|
||||
var opts = CallOptions.defaults
|
||||
opts.timeout = .seconds(30)
|
||||
return try await self.client.info(InfoRequest(), options: opts)
|
||||
}
|
||||
|
||||
// TODO
|
||||
@@ -119,12 +133,23 @@ public struct Builder: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
let respStream = self.clientAsync.performBuild(reqStream, callOptions: try CallOptions(config))
|
||||
let pipeline = try await BuildPipeline(config)
|
||||
do {
|
||||
try await pipeline.run(sender: continuation, receiver: respStream)
|
||||
try await self.client.performBuild(
|
||||
metadata: try Self.buildMetadata(config),
|
||||
options: .defaults,
|
||||
requestProducer: { writer in
|
||||
for await message in reqStream {
|
||||
try await writer.write(message)
|
||||
}
|
||||
},
|
||||
onResponse: { response in
|
||||
try await pipeline.run(sender: continuation, receiver: response.messages)
|
||||
}
|
||||
)
|
||||
} catch Error.buildComplete {
|
||||
_ = channel.close()
|
||||
self.grpcClient.beginGracefulShutdown()
|
||||
self.clientTask.cancel()
|
||||
try await group.shutdownGracefully()
|
||||
return
|
||||
}
|
||||
@@ -295,6 +320,48 @@ public struct Builder: Sendable {
|
||||
self.pull = pull
|
||||
}
|
||||
}
|
||||
|
||||
static func buildMetadata(_ config: BuildConfig) throws -> Metadata {
|
||||
var metadata = Metadata()
|
||||
metadata.addString(config.buildID, forKey: "build-id")
|
||||
metadata.addString(URL(filePath: config.contextDir).path(percentEncoded: false), forKey: "context")
|
||||
metadata.addString(config.dockerfile.base64EncodedString(), forKey: "dockerfile")
|
||||
metadata.addString(config.terminal != nil ? "tty" : "plain", forKey: "progress")
|
||||
metadata.addString(config.target, forKey: "target")
|
||||
|
||||
if let hiddenDockerDir = config.hiddenDockerDir {
|
||||
metadata.addString(hiddenDockerDir, forKey: "hidden-docker-dir")
|
||||
}
|
||||
for tag in config.tags {
|
||||
metadata.addString(tag, forKey: "tag")
|
||||
}
|
||||
for platform in config.platforms {
|
||||
metadata.addString(platform.description, forKey: "platforms")
|
||||
}
|
||||
if config.noCache {
|
||||
metadata.addString("", forKey: "no-cache")
|
||||
}
|
||||
for label in config.labels {
|
||||
metadata.addString(label, forKey: "labels")
|
||||
}
|
||||
for buildArg in config.buildArgs {
|
||||
metadata.addString(buildArg, forKey: "build-args")
|
||||
}
|
||||
for (id, data) in config.secrets {
|
||||
metadata.addString(id + "=" + data.base64EncodedString(), forKey: "secrets")
|
||||
}
|
||||
for output in config.exports {
|
||||
metadata.addString(try output.stringValue, forKey: "outputs")
|
||||
}
|
||||
for cacheIn in config.cacheIn {
|
||||
metadata.addString(cacheIn, forKey: "cache-in")
|
||||
}
|
||||
for cacheOut in config.cacheOut {
|
||||
metadata.addString(cacheOut, forKey: "cache-out")
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
}
|
||||
|
||||
extension Builder {
|
||||
@@ -316,52 +383,6 @@ extension Builder {
|
||||
}
|
||||
}
|
||||
|
||||
extension CallOptions {
|
||||
public init(_ config: Builder.BuildConfig) throws {
|
||||
var headers: [(String, String)] = [
|
||||
("build-id", config.buildID),
|
||||
("context", URL(filePath: config.contextDir).path(percentEncoded: false)),
|
||||
("dockerfile", config.dockerfile.base64EncodedString()),
|
||||
("progress", config.terminal != nil ? "tty" : "plain"),
|
||||
("target", config.target),
|
||||
]
|
||||
if let hiddenDockerDir = config.hiddenDockerDir {
|
||||
headers.append(("hidden-docker-dir", hiddenDockerDir))
|
||||
}
|
||||
for tag in config.tags {
|
||||
headers.append(("tag", tag))
|
||||
}
|
||||
for platform in config.platforms {
|
||||
headers.append(("platforms", platform.description))
|
||||
}
|
||||
if config.noCache {
|
||||
headers.append(("no-cache", ""))
|
||||
}
|
||||
for label in config.labels {
|
||||
headers.append(("labels", label))
|
||||
}
|
||||
for buildArg in config.buildArgs {
|
||||
headers.append(("build-args", buildArg))
|
||||
}
|
||||
for (id, data) in config.secrets {
|
||||
headers.append(("secrets", id + "=" + data.base64EncodedString()))
|
||||
}
|
||||
for output in config.exports {
|
||||
headers.append(("outputs", try output.stringValue))
|
||||
}
|
||||
for cacheIn in config.cacheIn {
|
||||
headers.append(("cache-in", cacheIn))
|
||||
}
|
||||
for cacheOut in config.cacheOut {
|
||||
headers.append(("cache-out", cacheOut))
|
||||
}
|
||||
|
||||
self.init(
|
||||
customMetadata: HPACKHeaders(headers)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension FileHandle {
|
||||
@discardableResult
|
||||
func setSendBufSize(_ bytes: Int) throws -> Int {
|
||||
@@ -404,3 +425,49 @@ extension FileHandle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffers incoming bytes until the full gRPC HTTP/2 pipeline is configured, then replays them.
|
||||
///
|
||||
/// See the equivalent in Containerization/Vminitd.swift for a full explanation.
|
||||
private final class HTTP2ConnectBufferingHandler: ChannelDuplexHandler, RemovableChannelHandler {
|
||||
typealias InboundIn = ByteBuffer
|
||||
typealias InboundOut = ByteBuffer
|
||||
typealias OutboundIn = ByteBuffer
|
||||
typealias OutboundOut = ByteBuffer
|
||||
|
||||
private var removalScheduled = false
|
||||
private var bufferedReads: [NIOAny] = []
|
||||
|
||||
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
|
||||
bufferedReads.append(data)
|
||||
}
|
||||
|
||||
func channelReadComplete(context: ChannelHandlerContext) {}
|
||||
|
||||
func flush(context: ChannelHandlerContext) {
|
||||
if !removalScheduled {
|
||||
removalScheduled = true
|
||||
context.eventLoop.assumeIsolatedUnsafeUnchecked().execute {
|
||||
context.pipeline.syncOperations.removeHandler(self, promise: nil)
|
||||
}
|
||||
}
|
||||
context.flush()
|
||||
}
|
||||
|
||||
func removeHandler(context: ChannelHandlerContext, removalToken: ChannelHandlerContext.RemovalToken) {
|
||||
var didRead = false
|
||||
while !bufferedReads.isEmpty {
|
||||
context.fireChannelRead(bufferedReads.removeFirst())
|
||||
didRead = true
|
||||
}
|
||||
if didRead {
|
||||
context.fireChannelReadComplete()
|
||||
}
|
||||
context.leavePipeline(removalToken: removalToken)
|
||||
}
|
||||
|
||||
func channelInactive(context: ChannelHandlerContext) {
|
||||
bufferedReads.removeAll()
|
||||
context.fireChannelInactive()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ extension Application {
|
||||
let fh = try await client.dial(id: "buildkit", port: vsockPort)
|
||||
|
||||
let threadGroup: MultiThreadedEventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
|
||||
let b = try Builder(socket: fh, group: threadGroup)
|
||||
let b = try Builder(socket: fh, group: threadGroup, logger: log)
|
||||
|
||||
// If this call succeeds, then BuildKit is running.
|
||||
let _ = try await b.info()
|
||||
|
||||
@@ -46,31 +46,42 @@ public struct StderrLogHandler: LogHandler {
|
||||
let data: Data
|
||||
switch logLevel {
|
||||
case .debug, .trace:
|
||||
let timestamp = ISO8601DateFormatter().string(from: Date())
|
||||
let timestamp = isoTimestamp()
|
||||
if let metadata, !metadata.isEmpty {
|
||||
data =
|
||||
"\(timestamp) \(message.description): \(metadata.description)"
|
||||
"\(timestamp) \(message.description): \(metadata.description)\n"
|
||||
.data(using: .utf8) ?? Data()
|
||||
} else {
|
||||
data =
|
||||
"\(timestamp) \(message.description)"
|
||||
"\(timestamp) \(message.description)\n"
|
||||
.data(using: .utf8) ?? Data()
|
||||
}
|
||||
default:
|
||||
if let metadata, !metadata.isEmpty {
|
||||
data =
|
||||
"\(message.description): \(metadata.description)"
|
||||
"\(message.description): \(metadata.description)\n"
|
||||
.data(using: .utf8) ?? Data()
|
||||
} else {
|
||||
data =
|
||||
message.description
|
||||
"\(message.description)\n"
|
||||
.data(using: .utf8) ?? Data()
|
||||
}
|
||||
}
|
||||
|
||||
// Use a single write call for atomicity
|
||||
var output = data
|
||||
output.append("\n".data(using: .utf8)!)
|
||||
FileHandle.standardError.write(output)
|
||||
FileHandle.standardError.write(data)
|
||||
}
|
||||
|
||||
private func isoTimestamp() -> String {
|
||||
let date = Date()
|
||||
var time = time_t(date.timeIntervalSince1970)
|
||||
var ms = Int(date.timeIntervalSince1970 * 1000) % 1000
|
||||
if ms < 0 { ms += 1000 }
|
||||
var tm = tm()
|
||||
gmtime_r(&time, &tm)
|
||||
let buf = withUnsafeTemporaryAllocation(of: CChar.self, capacity: 32) { ptr -> String in
|
||||
strftime(ptr.baseAddress!, 32, "%Y-%m-%dT%H:%M:%S", &tm)
|
||||
return String(cString: ptr.baseAddress!)
|
||||
}
|
||||
return String(format: "%@.%03dZ", buf, ms)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@ if command -v .local/bin/hawkeye >/dev/null 2>&1; then
|
||||
echo "hawkeye already installed"
|
||||
else
|
||||
echo "Installing hawkeye"
|
||||
export VERSION=v6.1.0
|
||||
export VERSION=v6.5.1
|
||||
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/korandoru/hawkeye/releases/download/${VERSION}/hawkeye-installer.sh | CARGO_HOME=.local sh -s -- --no-modify-path
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user