mirror of
https://github.com/apple/container.git
synced 2026-09-26 00:51:12 -04:00
Centralize utilities for configuration loading and path parsing (#1448)
This creates a more centralized utility to find and load configuration files, as a follow up to (pr: https://github.com/apple/container/pull/1425).
This commit is contained in:
@@ -50,9 +50,7 @@ extension APIServer {
|
||||
var logRoot = LogRoot.path
|
||||
|
||||
func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let commandName = APIServer._commandName
|
||||
let logPath = logRoot.map { $0.appending("\(commandName).log") }
|
||||
let log = ServiceLogger.bootstrap(category: "APIServer", debug: debug, logPath: logPath)
|
||||
|
||||
@@ -149,9 +149,7 @@ extension Application {
|
||||
var pull: Bool = false
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
do {
|
||||
let timeout: Duration = .seconds(300)
|
||||
let progressConfig = try ProgressConfig(
|
||||
|
||||
@@ -55,9 +55,7 @@ extension Application {
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let progressConfig = try ProgressConfig(
|
||||
showTasks: true,
|
||||
showItems: true,
|
||||
|
||||
@@ -56,9 +56,7 @@ extension Application {
|
||||
var arguments: [String] = []
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let progressConfig = try ProgressConfig(
|
||||
showTasks: true,
|
||||
showItems: true,
|
||||
|
||||
@@ -63,9 +63,7 @@ extension Application {
|
||||
var arguments: [String] = []
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
var exitCode: Int32 = 127
|
||||
let id = Utility.createContainerID(name: self.managementFlags.name)
|
||||
|
||||
|
||||
@@ -109,9 +109,7 @@ extension Application {
|
||||
}
|
||||
|
||||
public mutating func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
try await DeleteImageImplementation.removeImage(options: options, containerSystemConfig: containerSystemConfig, log: log)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,7 @@ extension Application {
|
||||
}
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
var printable: [ImageDetail] = []
|
||||
var succeededImages: [String] = []
|
||||
var allErrors: [(String, Error)] = []
|
||||
|
||||
@@ -45,9 +45,7 @@ extension Application {
|
||||
public var logOptions: Flags.Logging
|
||||
|
||||
public mutating func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
try Self.validate(format: format, quiet: quiet, verbose: verbose)
|
||||
|
||||
var images = try await ClientImage.list().filter { img in
|
||||
|
||||
@@ -69,9 +69,7 @@ extension Application {
|
||||
}
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let p = try DefaultPlatform.resolve(platform: platform, os: os, arch: arch, log: log)
|
||||
|
||||
let scheme = try RequestScheme(registry.scheme)
|
||||
|
||||
@@ -57,9 +57,7 @@ extension Application {
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let p = try DefaultPlatform.resolve(platform: platform, os: os, arch: arch, log: log)
|
||||
|
||||
let scheme = try RequestScheme(registry.scheme)
|
||||
|
||||
@@ -62,9 +62,7 @@ extension Application {
|
||||
@Argument var references: [String]
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let p = try DefaultPlatform.resolve(platform: platform, os: os, arch: arch, log: log)
|
||||
|
||||
let progressConfig = try ProgressConfig(
|
||||
|
||||
@@ -36,9 +36,7 @@ extension Application {
|
||||
public var logOptions: Flags.Logging
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let existing = try await ClientImage.get(reference: source, containerSystemConfig: containerSystemConfig)
|
||||
let targetReference = try ClientImage.normalizeReference(target, containerSystemConfig: containerSystemConfig)
|
||||
try await existing.tag(new: targetReference)
|
||||
|
||||
@@ -47,9 +47,7 @@ extension Application {
|
||||
var server: String
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
var username = self.username
|
||||
var password = ""
|
||||
if passwordStdin {
|
||||
|
||||
@@ -53,9 +53,7 @@ extension Application {
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
if recommended {
|
||||
let url = containerSystemConfig.kernel.url
|
||||
let path: String = containerSystemConfig.kernel.binaryPath
|
||||
|
||||
@@ -42,9 +42,7 @@ extension Application {
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let output =
|
||||
switch format {
|
||||
case .json: try Output.renderJSON(containerSystemConfig)
|
||||
|
||||
@@ -72,12 +72,10 @@ extension Application {
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
// Copy the user's config into appRoot/config/ so that all plugins
|
||||
// and the apiserver subsequently read the same snapshot.
|
||||
SystemRuntimeOptions.copyConfigToAppRoot(appRoot: appRoot)
|
||||
let appRootPath = FilePath(appRoot.path(percentEncoded: false))
|
||||
try ConfigurationLoader.copyConfigurationToReadOnly(to: appRootPath)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load(
|
||||
configurationFile: ConfigurationLoader.configurationFile(in: appRootPath))
|
||||
|
||||
// Without the true path to the binary in the plist, `container-apiserver` won't launch properly.
|
||||
// Resolve the symlink to get the true binary path before writing the launchd plist.
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerizationError
|
||||
import Foundation
|
||||
import SystemPackage
|
||||
import TOML
|
||||
|
||||
public protocol Initable {
|
||||
init()
|
||||
}
|
||||
|
||||
public typealias LoadableConfiguration = Codable & Sendable & Initable
|
||||
|
||||
public enum ConfigurationLoader {
|
||||
private static let configFilename = "runtime-config.toml"
|
||||
private static let configDirectory = "config"
|
||||
private static let READ_ONLY: Int = 0o444
|
||||
private static let READ_AND_WRITE: Int = 0o644
|
||||
|
||||
/// Returns the canonical configuration file path under an appRoot base directory:
|
||||
/// `<base>/config/runtime-config.toml`.
|
||||
public static func configurationFile(in base: FilePath) -> FilePath {
|
||||
base.appending(configDirectory).appending(configFilename)
|
||||
}
|
||||
|
||||
/// Loads and decodes a TOML configuration file as type `T`.
|
||||
///
|
||||
/// - Parameter configurationFile: Absolute path to the configuration file.
|
||||
/// When `nil`, falls back to
|
||||
/// `configurationFile(in: PathUtils.BaseConfigPath.appRoot.basePath())`.
|
||||
/// - Returns: A decoded value of type `T`, or a default-initialized `T` if the
|
||||
/// configuration file does not exist.
|
||||
public static func load<T: LoadableConfiguration>(configurationFile: FilePath? = nil) throws -> T {
|
||||
let path = configurationFile ?? Self.configurationFile(in: PathUtils.BaseConfigPath.appRoot.basePath())
|
||||
guard FileManager.default.fileExists(atPath: path.string) else {
|
||||
return T()
|
||||
}
|
||||
do {
|
||||
let data = try Data(contentsOf: URL(filePath: path.string))
|
||||
return try TOMLDecoder().decode(T.self, from: data)
|
||||
} catch {
|
||||
throw ContainerizationError(
|
||||
.invalidArgument,
|
||||
message: "failed to load configuration from '\(path)': \(error)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies a TOML configuration file into a read-only destination under an appRoot base.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - source: The file to copy. When `nil`, defaults to
|
||||
/// `<home>/container/runtime-config.toml`. If the source does not exist,
|
||||
/// this is a no-op.
|
||||
/// - destination: Base directory under which the file is written at
|
||||
/// `<destination>/config/runtime-config.toml`. When `nil`, falls back to
|
||||
/// `PathUtils.BaseConfigPath.appRoot.basePath()`. The destination file is written
|
||||
/// with `READ_ONLY` (`0o444`) permissions.
|
||||
public static func copyConfigurationToReadOnly(
|
||||
from source: FilePath? = nil,
|
||||
to destination: FilePath? = nil
|
||||
) throws {
|
||||
let source =
|
||||
source
|
||||
?? PathUtils.BaseConfigPath.home.basePath()
|
||||
.appending(configFilename)
|
||||
let destinationFile = Self.configurationFile(in: destination ?? PathUtils.BaseConfigPath.appRoot.basePath())
|
||||
do {
|
||||
let fm = FileManager.default
|
||||
guard fm.fileExists(atPath: source.string) else { return }
|
||||
|
||||
let destDir = destinationFile.removingLastComponent()
|
||||
try fm.createDirectory(
|
||||
atPath: destDir.string,
|
||||
withIntermediateDirectories: true
|
||||
)
|
||||
if fm.fileExists(atPath: destinationFile.string) {
|
||||
try fm.setAttributes(
|
||||
[.posixPermissions: READ_AND_WRITE],
|
||||
ofItemAtPath: destinationFile.string
|
||||
)
|
||||
try fm.removeItem(at: URL(filePath: destinationFile.string))
|
||||
}
|
||||
try fm.copyItem(
|
||||
at: URL(filePath: source.string),
|
||||
to: URL(filePath: destinationFile.string)
|
||||
)
|
||||
try fm.setAttributes(
|
||||
[.posixPermissions: READ_ONLY],
|
||||
ofItemAtPath: destinationFile.string
|
||||
)
|
||||
} catch {
|
||||
throw ContainerizationError(
|
||||
.invalidState, message: "Failed to copy user TOML to AppRoot `\(error)`")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import Foundation
|
||||
///
|
||||
/// Each section maps to a nested struct. Missing keys fall back to
|
||||
/// hardcoded defaults via custom `init(from:)` implementations.
|
||||
final public class ContainerSystemConfig: Codable, Sendable {
|
||||
public final class ContainerSystemConfig: Codable, Sendable, Initable {
|
||||
public let build: BuildConfig
|
||||
public let container: ContainerConfig
|
||||
public let dns: DNSConfig
|
||||
@@ -50,6 +50,16 @@ final public class ContainerSystemConfig: Codable, Sendable {
|
||||
self.vminit = vminit
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.build = .init()
|
||||
self.container = .init()
|
||||
self.dns = .init()
|
||||
self.kernel = .init()
|
||||
self.network = .init()
|
||||
self.registry = .init()
|
||||
self.vminit = .init()
|
||||
}
|
||||
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.build = try container.decodeIfPresent(BuildConfig.self, forKey: .build) ?? .init()
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 SystemPackage
|
||||
|
||||
public enum PathUtils {
|
||||
public enum BaseConfigPath {
|
||||
case home
|
||||
case appRoot
|
||||
|
||||
public func basePath(env: [String: String] = ProcessInfo.processInfo.environment) -> FilePath {
|
||||
switch self {
|
||||
case .home:
|
||||
let configHome: String
|
||||
if let xdg = env["XDG_CONFIG_HOME"], !xdg.isEmpty {
|
||||
configHome = xdg
|
||||
} else {
|
||||
configHome = NSHomeDirectory() + "/.config"
|
||||
}
|
||||
return FilePath(configHome).appending("container")
|
||||
case .appRoot:
|
||||
if let envPath = env["CONTAINER_APP_ROOT"], !envPath.isEmpty {
|
||||
return FilePath(envPath)
|
||||
}
|
||||
let appSupportURL = FileManager.default.urls(
|
||||
for: .applicationSupportDirectory,
|
||||
in: .userDomainMask
|
||||
).first!.appendingPathComponent("com.apple.container")
|
||||
return FilePath(appSupportURL.path(percentEncoded: false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerizationError
|
||||
import Foundation
|
||||
import Logging
|
||||
import SystemPackage
|
||||
import TOML
|
||||
|
||||
private let log = Logger(label: "SystemRuntimeOptions")
|
||||
|
||||
/// TOML-backed configuration loader.
|
||||
///
|
||||
/// Decodes a user-provided TOML file into a typed configuration struct.
|
||||
/// Missing keys fall back to the struct's hardcoded defaults (via custom
|
||||
/// `init(from:)` implementations using `decodeIfPresent`).
|
||||
///
|
||||
/// Configuration priority (highest to lowest):
|
||||
/// 1. User config: `$XDG_CONFIG_HOME/container/runtime-config.toml`
|
||||
/// 2. Hardcoded defaults in the config struct's initializer
|
||||
public enum SystemRuntimeOptions {
|
||||
/// Path to the user's configuration file.
|
||||
public static var defaultUserConfigPath: URL {
|
||||
let configHome: String
|
||||
if let xdg = ProcessInfo.processInfo.environment["XDG_CONFIG_HOME"], !xdg.isEmpty {
|
||||
configHome = xdg
|
||||
} else {
|
||||
configHome = NSHomeDirectory() + "/.config"
|
||||
}
|
||||
return URL(fileURLWithPath: configHome)
|
||||
.appendingPathComponent("container")
|
||||
.appendingPathComponent("runtime-config.toml")
|
||||
}
|
||||
|
||||
/// The path to the config file within an application root directory.
|
||||
public static func configFileFromAppRoot(_ appRoot: URL) -> URL {
|
||||
appRoot
|
||||
.appendingPathComponent("config")
|
||||
.appendingPathComponent("runtime-config.toml")
|
||||
}
|
||||
|
||||
/// Load configuration by decoding a TOML file.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - configFile: Full path to the TOML config file.
|
||||
/// - Returns: The decoded configuration. If the file does not exist, all values
|
||||
/// fall back to the type's hardcoded defaults.
|
||||
public static func loadConfig<T: Codable & Sendable>(configFile: URL) throws -> T {
|
||||
let fm = FileManager.default
|
||||
let path = configFile.path(percentEncoded: false)
|
||||
guard fm.fileExists(atPath: path) else {
|
||||
do {
|
||||
return try TOMLDecoder().decode(T.self, from: Data("".utf8))
|
||||
} catch {
|
||||
throw ContainerizationError(.internalError, message: "failed to initialize default configuration: \(error)")
|
||||
}
|
||||
}
|
||||
do {
|
||||
let data = try Data(contentsOf: configFile)
|
||||
return try TOMLDecoder().decode(T.self, from: data)
|
||||
} catch {
|
||||
throw ContainerizationError(.invalidArgument, message: "failed to load configuration from '\(path)': \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
public static func copyConfigToAppRoot(
|
||||
appRoot: URL,
|
||||
userConfigPath: URL = defaultUserConfigPath,
|
||||
) {
|
||||
let fm = FileManager.default
|
||||
|
||||
if fm.fileExists(atPath: userConfigPath.path(percentEncoded: false)) {
|
||||
let configDir = appRoot.appendingPathComponent("config")
|
||||
let destPath = configDir.appendingPathComponent("runtime-config.toml")
|
||||
do {
|
||||
try fm.createDirectory(at: configDir, withIntermediateDirectories: true)
|
||||
if fm.fileExists(atPath: destPath.path(percentEncoded: false)) {
|
||||
try fm.setAttributes([.posixPermissions: 0o644], ofItemAtPath: destPath.path(percentEncoded: false))
|
||||
try fm.removeItem(at: destPath)
|
||||
}
|
||||
try fm.copyItem(
|
||||
at: URL(fileURLWithPath: userConfigPath.path(percentEncoded: false)),
|
||||
to: destPath
|
||||
)
|
||||
try fm.setAttributes(
|
||||
[.posixPermissions: 0o444],
|
||||
ofItemAtPath: destPath.path(percentEncoded: false)
|
||||
)
|
||||
log.info("copied runtime config", metadata: ["dest": "\(destPath.path(percentEncoded: false))"])
|
||||
} catch {
|
||||
// If the config copy-ing fails, we will log an error but it is not fatal since we can utilize the fallback config.
|
||||
log.error("failed to copy runtime config to app root", metadata: ["error": "\(error)"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,9 +58,7 @@ extension ImagesHelper {
|
||||
var logRoot = LogRoot.path
|
||||
|
||||
func run() async throws {
|
||||
let containerSystemConfig: ContainerSystemConfig = try SystemRuntimeOptions.loadConfig(
|
||||
configFile: SystemRuntimeOptions.configFileFromAppRoot(ApplicationRoot.url)
|
||||
)
|
||||
let containerSystemConfig: ContainerSystemConfig = try ConfigurationLoader.load()
|
||||
let commandName = ImagesHelper._commandName
|
||||
let logPath = logRoot.map { $0.appending("\(commandName).log") }
|
||||
let log = ServiceLogger.bootstrap(category: "ImagesHelper", debug: debug, logPath: logPath)
|
||||
|
||||
Reference in New Issue
Block a user