Skip to content

Commit e10069f

Browse files
authored
Allow specifying a custom entrypoint & resources (#169)
Motivation ---------- This should allow users to add a debugserver and launch it. Modifications ------------- - Added a `--resources` flag that points to custom artifacts - Added an option for a custom entrypoint Result ------ You can add and run a debug server. Test Plan --------- Currently undergoing manual testing. **Update:** Success
1 parent b9fd555 commit e10069f

File tree

6 files changed

+106
-23
lines changed

6 files changed

+106
-23
lines changed

Plugins/ContainerImageBuilder/main.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ extension PluginError: CustomStringConvertible {
4848
4949
OPTIONS
5050
--product Product to include in the image
51+
--resources Directory of resources to include in the image
5152
5253
Other arguments are passed to the containertool helper.
5354
"""
@@ -89,16 +90,29 @@ extension PluginError: CustomStringConvertible {
8990

9091
for built in builtExecutables { Diagnostics.remark("Built product: \(built.url.path)") }
9192

92-
let resources = builtExecutables[0].url
93+
let resourcesBundle = builtExecutables[0].url
9394
.deletingLastPathComponent()
9495
.appendingPathComponent(
9596
"\(context.package.displayName)_\(productName).resources"
9697
)
9798

99+
var resources: [String] = []
100+
if FileManager.default.fileExists(atPath: resourcesBundle.path) {
101+
resources.append(resourcesBundle.path)
102+
}
103+
104+
for resource in extractor.extractOption(named: "resources") {
105+
let paths = resource.split(separator: ":", maxSplits: 1)
106+
if paths.count >= 1 && !FileManager.default.fileExists(atPath: String(paths[0])) {
107+
throw PluginError.argumentError("Resource directory \(resource) does not exist")
108+
}
109+
resources.append(resource)
110+
}
111+
98112
// Run a command line helper to upload the image
99113
let helperURL = try context.tool(named: "containertool").url
100114
let helperArgs =
101-
(FileManager.default.fileExists(atPath: resources.path) ? ["--resources", resources.path] : [])
115+
resources.map { "--resources=\($0)" }
102116
+ builtExecutables.map { $0.url.path }
103117
+ extractor.remainingArguments
104118
let helperEnv = ProcessInfo.processInfo.environment.filter { $0.key.starts(with: "CONTAINERTOOL_") }

Sources/containertool/Extensions/Archive+appending.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,25 @@ extension Archive {
4747
try self.appendingFile(name: path.lastPathComponent, data: try [UInt8](Data(contentsOf: path)))
4848
}
4949

50+
func appendingFile(at path: URL, to destinationPath: URL) throws -> Self {
51+
var ret = self
52+
let data = try [UInt8](Data(contentsOf: path))
53+
let components = destinationPath.pathComponents
54+
precondition(!components.isEmpty, "Destination path is empty")
55+
for i in 1..<components.count - 1 {
56+
let directoryPath = components[..<i].joined(separator: "/")
57+
try ret.appendDirectory(name: directoryPath)
58+
}
59+
60+
try ret.appendFile(name: destinationPath.path(), data: data)
61+
return ret
62+
}
63+
5064
/// Recursively append a single directory tree to the archive.
5165
/// Parameters:
5266
/// - root: The path to the directory to add.
5367
/// Returns: A new archive made by appending `root` to the receiver.
54-
func appendingDirectoryTree(at root: URL) throws -> Self {
68+
func appendingDirectoryTree(at root: URL, to destinationPath: URL = URL(filePath: "/")) throws -> Self {
5569
var ret = self
5670

5771
guard let enumerator = FileManager.default.enumerator(atPath: root.path) else {
@@ -66,6 +80,7 @@ extension Archive {
6680
throw ("Unable to get file type for \(subpath)")
6781
}
6882

83+
let subpath = destinationPath.appending(path: subpath).path()
6984
switch filetype {
7085
case .typeRegular:
7186
let resource = try [UInt8](Data(contentsOf: root.appending(path: subpath)))

Sources/containertool/Extensions/RegistryClient+publish.swift

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import class Foundation.FileManager
16+
import struct Foundation.ObjCBool
1517
import struct Foundation.Date
1618
import struct Foundation.URL
1719

@@ -25,6 +27,7 @@ func publishContainerImage<Source: ImageSource, Destination: ImageDestination>(
2527
destination: Destination,
2628
architecture: String,
2729
os: String,
30+
entrypoint: String?,
2831
cmd: [String],
2932
resources: [String],
3033
tag: String?,
@@ -50,17 +53,54 @@ func publishContainerImage<Source: ImageSource, Destination: ImageDestination>(
5053

5154
var resourceLayers: [(descriptor: ContentDescriptor, diffID: ImageReference.Digest)] = []
5255
for resourceDir in resources {
53-
let resourceTardiff = try Archive().appendingRecursively(atPath: resourceDir).bytes
54-
let resourceLayer = try await destination.uploadLayer(
55-
repository: destinationImage.repository,
56-
contents: resourceTardiff
57-
)
56+
let paths = resourceDir.split(separator: ":", maxSplits: 1)
57+
switch paths.count {
58+
case 1:
59+
let resourceTardiff = try Archive().appendingRecursively(atPath: resourceDir).bytes
60+
let resourceLayer = try await destination.uploadLayer(
61+
repository: destinationImage.repository,
62+
contents: resourceTardiff
63+
)
5864

59-
if verbose {
60-
log("resource layer: \(resourceLayer.descriptor.digest) (\(resourceLayer.descriptor.size) bytes)")
61-
}
65+
if verbose {
66+
log("resource layer: \(resourceLayer.descriptor.digest) (\(resourceLayer.descriptor.size) bytes)")
67+
}
68+
69+
resourceLayers.append(resourceLayer)
70+
case 2:
71+
let sourcePath = paths[0]
72+
let destinationPath = paths[1]
73+
74+
var isDirectory: ObjCBool = false
75+
guard FileManager.default.fileExists(atPath: String(sourcePath), isDirectory: &isDirectory) else {
76+
preconditionFailure("Source does not exist: \(source)")
77+
}
78+
79+
let archive: Archive
80+
if isDirectory.boolValue {
81+
// archive = try Archive().appendingDirectoryTree(at: URL(fileURLWithPath: String(sourcePath)))
82+
preconditionFailure("Directory trees are not supported yet")
83+
} else {
84+
archive = try Archive()
85+
.appendingFile(
86+
at: URL(fileURLWithPath: String(sourcePath)),
87+
to: URL(fileURLWithPath: String(destinationPath))
88+
)
89+
}
90+
91+
let resourceLayer = try await destination.uploadLayer(
92+
repository: destinationImage.repository,
93+
contents: archive.bytes
94+
)
6295

63-
resourceLayers.append(resourceLayer)
96+
if verbose {
97+
log("resource layer: \(resourceLayer.descriptor.digest) (\(resourceLayer.descriptor.size) bytes)")
98+
}
99+
100+
resourceLayers.append(resourceLayer)
101+
default:
102+
preconditionFailure("Invalid resource directory: \(resourceDir)")
103+
}
64104
}
65105

66106
// MARK: Upload the application layer
@@ -80,7 +120,11 @@ func publishContainerImage<Source: ImageSource, Destination: ImageDestination>(
80120
// Inherit the configuration of the base image - UID, GID, environment etc -
81121
// and override the entrypoint.
82122
var inheritedConfiguration = baseImageConfiguration.config ?? .init()
83-
inheritedConfiguration.Entrypoint = ["/\(executableURL.lastPathComponent)"]
123+
if let entrypoint {
124+
inheritedConfiguration.Entrypoint = [entrypoint]
125+
} else {
126+
inheritedConfiguration.Entrypoint = ["/\(executableURL.lastPathComponent)"]
127+
}
84128
inheritedConfiguration.Cmd = cmd
85129
inheritedConfiguration.WorkingDir = "/"
86130

Sources/containertool/containertool.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
6565
@Option(help: "Operating system")
6666
var os: String?
6767

68+
@Option(help: "Entrypoint process")
69+
var entrypoint: String?
70+
6871
@Option(parsing: .remaining, help: "Default arguments to pass to the entrypoint process")
6972
var cmd: [String] = []
7073
}
@@ -225,6 +228,7 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
225228
destination: destination,
226229
architecture: architecture,
227230
os: os,
231+
entrypoint: imageConfigurationOptions.entrypoint,
228232
cmd: imageConfigurationOptions.cmd,
229233
resources: imageBuildOptions.resources,
230234
tag: repositoryOptions.tag,

Vendor/github.com/apple/swift-package-manager/Sources/Basics/AuthorizationProvider.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import struct Foundation.Data
1818
import struct Foundation.Date
1919
import struct Foundation.URL
2020
#if canImport(Security)
21-
import Security
21+
import Security
2222
#endif
2323

2424
public protocol AuthorizationProvider: Sendable {
@@ -56,14 +56,14 @@ public final class NetrcAuthorizationProvider: AuthorizationProvider {
5656
}
5757

5858
public func authentication(for url: URL) -> (user: String, password: String)? {
59-
return self.machine(for: url).map { (user: $0.login, password: $0.password) }
59+
self.machine(for: url).map { (user: $0.login, password: $0.password) }
6060
}
6161

6262
private func machine(for url: URL) -> Basics.Netrc.Machine? {
6363
// Since updates are appended to the end of the file, we
6464
// take the _last_ match to use the most recent entry.
6565
if let machine = NetrcAuthorizationProvider.machine(for: url),
66-
let existing = self.netrc?.machines.last(where: { $0.name.lowercased() == machine })
66+
let existing = self.netrc?.machines.last(where: { $0.name.lowercased() == machine })
6767
{
6868
return existing
6969
}

Vendor/github.com/apple/swift-package-manager/Sources/Basics/Netrc.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ public struct Netrc: Sendable {
2727
/// - Parameters:
2828
/// - url: The url to retrieve authorization information for.
2929
public func authorization(for url: URL) -> Authorization? {
30-
guard let index = machines.firstIndex(where: { $0.name == url.host }) ?? machines
31-
.firstIndex(where: { $0.isDefault })
30+
guard
31+
let index = machines.firstIndex(where: { $0.name == url.host })
32+
?? machines
33+
.firstIndex(where: { $0.isDefault })
3234
else {
3335
return .none
3436
}
@@ -53,8 +55,10 @@ public struct Netrc: Sendable {
5355
}
5456

5557
init?(for match: NSTextCheckingResult, string: String, variant: String = "") {
56-
guard let name = RegexUtil.Token.machine.capture(in: match, string: string) ?? RegexUtil.Token.default
57-
.capture(in: match, string: string),
58+
guard
59+
let name = RegexUtil.Token.machine.capture(in: match, string: string)
60+
?? RegexUtil.Token.default
61+
.capture(in: match, string: string),
5862
let login = RegexUtil.Token.login.capture(prefix: variant, in: match, string: string),
5963
let password = RegexUtil.Token.password.capture(prefix: variant, in: match, string: string)
6064
else {
@@ -87,11 +91,12 @@ public struct NetrcParser {
8791
let matches = regex.matches(
8892
in: content,
8993
options: [],
90-
range: NSRange(content.startIndex ..< content.endIndex, in: content)
94+
range: NSRange(content.startIndex..<content.endIndex, in: content)
9195
)
9296

9397
let machines: [Netrc.Machine] = matches.compactMap {
94-
Netrc.Machine(for: $0, string: content, variant: "lp") ?? Netrc
98+
Netrc.Machine(for: $0, string: content, variant: "lp")
99+
?? Netrc
95100
.Machine(for: $0, string: content, variant: "pl")
96101
}
97102

@@ -116,7 +121,8 @@ public struct NetrcParser {
116121
let matches = regex.matches(in: text, range: range)
117122
var trimmedCommentsText = text
118123
matches.forEach {
119-
trimmedCommentsText = trimmedCommentsText
124+
trimmedCommentsText =
125+
trimmedCommentsText
120126
.replacingOccurrences(of: nsString.substring(with: $0.range), with: "")
121127
}
122128
return trimmedCommentsText

0 commit comments

Comments
 (0)