Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Swiftpkgr/State/ProjectEditorModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ final class ProjectEditorModel {

private func saveDraft() throws {
guard let projectURL, let document = buildInfoDocument else {
throw MunkiPkgError.message("No project is open.")
throw SwiftPkgError.message("No project is open.")
}
let configuration = try draft.validatedConfiguration()
try BuildInfoStore.write(configuration, to: projectURL, format: document.format)
Expand Down
10 changes: 6 additions & 4 deletions swiftpkg.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -481,18 +481,19 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 16;
DEVELOPMENT_TEAM = DPXY7JLK67;
ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Swiftpkgr;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 0.3.1;
MARKETING_VERSION = 0.4.0;
PRODUCT_BUNDLE_IDENTIFIER = com.codecarton.Swiftpkgr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
Expand All @@ -506,18 +507,19 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 16;
DEVELOPMENT_TEAM = DPXY7JLK67;
ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Swiftpkgr;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 0.3.1;
MARKETING_VERSION = 0.4.0;
PRODUCT_BUNDLE_IDENTIFIER = com.codecarton.Swiftpkgr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
Expand Down
24 changes: 12 additions & 12 deletions swiftpkg/BuildInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,38 +198,38 @@ public struct PackageConfiguration: Sendable {

private static func stringValue(for key: String, in values: [String: Any]) throws -> String? {
guard let value = values[key] else { return nil }
guard let string = scalarString(value) else { throw MunkiPkgError.invalidConfiguration("build-info key '\(key)' must be a string") }
guard let string = scalarString(value) else { throw SwiftPkgError.invalidConfiguration("build-info key '\(key)' must be a string") }
return string
}

private static func boolValue(for key: String, in values: [String: Any]) throws -> Bool? {
guard let value = values[key] else { return nil }
guard let bool = value as? Bool else { throw MunkiPkgError.invalidConfiguration("build-info key '\(key)' must be a Boolean") }
guard let bool = value as? Bool else { throw SwiftPkgError.invalidConfiguration("build-info key '\(key)' must be a Boolean") }
return bool
}

private static func enumValue<T: RawRepresentable>(_ type: T.Type, key: String, values: [String: Any]) throws -> T? where T.RawValue == String {
guard let string = try stringValue(for: key, in: values) else { return nil }
guard let value = T(rawValue: string) else { throw MunkiPkgError.invalidConfiguration("build-info key '\(key)' has illegal value: \(string)") }
guard let value = T(rawValue: string) else { throw SwiftPkgError.invalidConfiguration("build-info key '\(key)' has illegal value: \(string)") }
return value
}

private static func signingConfiguration(in values: [String: Any]) throws -> SigningConfiguration? {
guard let value = values["signing_info"] else { return nil }
guard let signing = value as? [String: Any], let identity = signing["identity"] as? String else {
throw MunkiPkgError.invalidConfiguration("signing_info must contain a string identity")
throw SwiftPkgError.invalidConfiguration("signing_info must contain a string identity")
}
let certificates: [String]
if let certificate = signing["additional_cert_names"] as? String { certificates = [certificate] }
else if let values = signing["additional_cert_names"] as? [String] { certificates = values }
else if signing["additional_cert_names"] == nil { certificates = [] }
else { throw MunkiPkgError.invalidConfiguration("signing_info additional_cert_names must be a string or string array") }
else { throw SwiftPkgError.invalidConfiguration("signing_info additional_cert_names must be a string or string array") }
return SigningConfiguration(identity: identity, keychain: signing["keychain"] as? String, additionalCertificateNames: certificates, usesTimestamp: signing["timestamp"] as? Bool)
}

private static func notarizationConfiguration(in values: [String: Any]) throws -> NotarizationConfiguration? {
guard let value = values["notarization_info"] else { return nil }
guard let notary = value as? [String: Any] else { throw MunkiPkgError.invalidConfiguration("notarization_info must be a dictionary") }
guard let notary = value as? [String: Any] else { throw SwiftPkgError.invalidConfiguration("notarization_info must be a dictionary") }
let authentication: NotarizationConfiguration.Authentication
if let appleID = notary["apple_id"] as? String, let teamID = notary["team_id"] as? String {
let password = notary["password"] as? String ?? ""
Expand Down Expand Up @@ -295,8 +295,8 @@ public enum BuildInfoStore {
case .json: object = try JSONSerialization.jsonObject(with: data)
case .yaml, .yml: object = try Yams.load(yaml: String(decoding: data, as: UTF8.self)) as Any
}
} catch { throw MunkiPkgError.invalidConfiguration("\(url.path) is not a valid \(format.rawValue) file: \(error.localizedDescription)") }
guard let values = object as? [String: Any] else { throw MunkiPkgError.invalidConfiguration("\(url.path) must contain a dictionary") }
} catch { throw SwiftPkgError.invalidConfiguration("\(url.path) is not a valid \(format.rawValue) file: \(error.localizedDescription)") }
guard let values = object as? [String: Any] else { throw SwiftPkgError.invalidConfiguration("\(url.path) must contain a dictionary") }
return try PackageConfiguration(values: values, defaults: .defaults(for: project))
}

Expand All @@ -318,19 +318,19 @@ public enum BuildInfoStore {
public static func discover(in project: URL, requestedFormat: BuildInfoFormat? = nil, fileManager: FileManager = .default) throws -> BuildInfoDocument {
if let requestedFormat {
let url = project.appendingPathComponent("build-info").appendingPathExtension(requestedFormat.rawValue)
guard fileManager.itemExists(at: url) else { throw MunkiPkgError.invalidConfiguration("No build-info file found!") }
guard fileManager.itemExists(at: url) else { throw SwiftPkgError.invalidConfiguration("No build-info file found!") }
return BuildInfoDocument(url: url, format: requestedFormat)
}
let baseURL = project.appendingPathComponent("build-info")
let formats = BuildInfoFormat.allCases.filter { fileManager.itemExists(at: baseURL.appendingPathExtension($0.rawValue)) }
guard formats.count <= 1 else { throw MunkiPkgError.invalidConfiguration("Multiple build-info files found!") }
guard let format = formats.first else { throw MunkiPkgError.invalidConfiguration("No build-info file found!") }
guard formats.count <= 1 else { throw SwiftPkgError.invalidConfiguration("Multiple build-info files found!") }
guard let format = formats.first else { throw SwiftPkgError.invalidConfiguration("No build-info file found!") }
return BuildInfoDocument(url: baseURL.appendingPathExtension(format.rawValue), format: format)
}

private static func format(for url: URL) throws -> BuildInfoFormat {
guard let format = BuildInfoFormat(rawValue: url.pathExtension.lowercased()) else {
throw MunkiPkgError.invalidConfiguration("Unsupported build-info format: \(url.pathExtension)")
throw SwiftPkgError.invalidConfiguration("Unsupported build-info format: \(url.pathExtension)")
}
return format
}
Expand Down
4 changes: 2 additions & 2 deletions swiftpkg/EnvLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ public enum EnvLoader {

let attributes = try fileManager.attributesOfItem(atPath: path)
if let size = attributes[.size] as? Int, size > maxFileSize {
throw MunkiPkgError.invalidConfiguration("Environment file exceeds the \(maxFileSize)-byte limit: \(path)")
throw SwiftPkgError.invalidConfiguration("Environment file exceeds the \(maxFileSize)-byte limit: \(path)")
}
if let permissions = attributes[.posixPermissions] as? Int, permissions & 0o044 != 0 {
console?.warning("environment file \(path) is group- or world-readable (mode 0\(String(permissions, radix: 8))). Recommend `chmod 600 \(path)`.")
}

guard let data = fileManager.contents(atPath: path), let content = String(data: data, encoding: .utf8) else {
throw MunkiPkgError.invalidConfiguration("Failed to read environment file: \(path)")
throw SwiftPkgError.invalidConfiguration("Failed to read environment file: \(path)")
}

var variables: [String: String] = [:]
Expand Down
6 changes: 3 additions & 3 deletions swiftpkg/Linter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ public struct Linter {
/// (missing directory / undecodable build-info), which is itself a failure.
public func lint(project: URL, requestedFormat: BuildInfoFormat?) throws -> [LintFinding] {
guard fileManager.directoryExists(at: project) else {
throw MunkiPkgError.message("\(project.path): Project not found.")
throw SwiftPkgError.message("\(project.path): Project not found.")
}
var findings: [LintFinding] = []

// Decoding failures throw MunkiPkgError; surface them as a lint error
// Decoding failures throw SwiftPkgError; surface them as a lint error
// rather than a crash so `--lint` always produces a report.
let configuration: PackageConfiguration
do {
configuration = try BuildInfoStore.load(from: project, requestedFormat: requestedFormat)
} catch let error as MunkiPkgError {
} catch let error as SwiftPkgError {
return [LintFinding(.error, error.description)]
}

Expand Down
28 changes: 14 additions & 14 deletions swiftpkg/PackageBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public struct PackageBuildCoordinator: @unchecked Sendable {
let envPath: String
if let explicit = configuration.envFile {
guard fileManager.fileExists(atPath: explicit) else {
throw MunkiPkgError.invalidConfiguration("--env-file not found: \(explicit)")
throw SwiftPkgError.invalidConfiguration("--env-file not found: \(explicit)")
}
envPath = explicit
} else {
Expand Down Expand Up @@ -127,13 +127,13 @@ public struct PackageBuildCoordinator: @unchecked Sendable {
let detail = unresolved.sorted { $0.key < $1.key }
.map { "\($0.key): \($0.value.sorted().joined(separator: ", "))" }
.joined(separator: "; ")
throw MunkiPkgError.invalidConfiguration("Unresolved script placeholders (--strict-env): \(detail)")
throw SwiftPkgError.invalidConfiguration("Unresolved script placeholders (--strict-env): \(detail)")
}

private func packageBOM(for package: URL) throws -> URL {
let result = try runner.run(executable: ToolPaths.pkgutil, arguments: ["--bom", package.path])
guard result.status == 0, let path = result.stdoutString.split(whereSeparator: \.isNewline).first else {
throw MunkiPkgError.processFailed(tool: "pkgutil", message: "pkgutil returned no BOM path")
throw SwiftPkgError.processFailed(tool: "pkgutil", message: "pkgutil returned no BOM path")
}
return URL(fileURLWithPath: String(path))
}
Expand All @@ -151,7 +151,7 @@ public struct PackageBuildCoordinator: @unchecked Sendable {
!name.contains("\0"),
URL(fileURLWithPath: name).lastPathComponent == name
else {
throw MunkiPkgError.invalidConfiguration(
throw SwiftPkgError.invalidConfiguration(
"Package name \"\(name)\" must be a single path component (no \"/\" or \"..\")."
)
}
Expand Down Expand Up @@ -184,7 +184,7 @@ private struct PackageProjectLayout {

func createBuildDirectoryIfNeeded() throws {
if !fileManager.itemExists(at: buildDirectory) { try fileManager.createDirectory(at: buildDirectory, withIntermediateDirectories: true) }
else if !fileManager.directoryExists(at: buildDirectory) { throw MunkiPkgError.message("\(buildDirectory.path) is not a directory.") }
else if !fileManager.directoryExists(at: buildDirectory) { throw SwiftPkgError.message("\(buildDirectory.path) is not a directory.") }
}

func withTemporaryDirectory(_ body: (URL) async throws -> Void) async throws {
Expand Down Expand Up @@ -270,7 +270,7 @@ private struct ComponentPackageBuilder {
try runner.runSuccessfully(executable: ToolPaths.pkgbuild, arguments: arguments, failureMessage: "pkgbuild failed while analyzing payload")
let data = try Data(contentsOf: destination)
guard var propertyList = try PropertyListSerialization.propertyList(from: data, format: nil) as? [[String: Any]] else {
throw MunkiPkgError.message("Couldn't read \(destination.path)")
throw SwiftPkgError.message("Couldn't read \(destination.path)")
}
for index in propertyList.indices where propertyList[index]["BundleIsRelocatable"] as? Bool == true {
propertyList[index]["BundleIsRelocatable"] = false
Expand Down Expand Up @@ -326,11 +326,11 @@ struct NotarizationService: Sendable {
/// happened rather than what was merely requested.
func notarize(package: URL, configuration: NotarizationConfiguration, skipsStapling: Bool) async throws -> Outcome {
if case let .invalid(reason) = configuration.authentication {
throw MunkiPkgError.invalidConfiguration(reason)
throw SwiftPkgError.invalidConfiguration(reason)
}
console.display("Uploading package to Apple notary service")
let submission = try plistOutput(for: ["notarytool", "submit", "--output-format", "plist", package.path] + authenticationArguments(for: configuration), failureMessage: "Notarization upload failed.")
guard let identifier = submission["id"] as? String else { throw MunkiPkgError.notarizationFailed("Unexpected output from notarytool") }
guard let identifier = submission["id"] as? String else { throw SwiftPkgError.notarizationFailed("Unexpected output from notarytool") }
console.display("id \(identifier)", toolName: "notarytool")
if let message = submission["message"] as? String { console.display(message, toolName: "notarytool") }
let accepted = try await waitForAcceptance(identifier, configuration: configuration)
Expand All @@ -351,10 +351,10 @@ struct NotarizationService: Sendable {
let status = output["status"] as? String ?? "Unknown"
let message = output["message"] as? String ?? ""
if status == "Accepted" { console.display("Notarization successful. \(message)"); return true }
if status != "In Progress" && status != "Unknown" { throw MunkiPkgError.notarizationFailed("Notarization failed (\(status)): \(message)") }
if status != "In Progress" && status != "Unknown" { throw SwiftPkgError.notarizationFailed("Notarization failed (\(status)): \(message)") }
console.display("Notarization state: \(status). Trying again in \(delay) seconds")
}
throw MunkiPkgError.notarizationFailed("Timeout exceeded (\(configuration.staplingTimeout)s) waiting for notarization to complete. The package was uploaded but never confirmed Accepted, so it was not stapled. Check with 'xcrun notarytool info \(identifier)' and staple manually if it later succeeds.")
throw SwiftPkgError.notarizationFailed("Timeout exceeded (\(configuration.staplingTimeout)s) waiting for notarization to complete. The package was uploaded but never confirmed Accepted, so it was not stapled. Check with 'xcrun notarytool info \(identifier)' and staple manually if it later succeeds.")
}

/// Carries notarytool's own explanation through, the way every other
Expand All @@ -366,10 +366,10 @@ struct NotarizationService: Sendable {
do {
result = try runner.run(executable: ToolPaths.xcrun, arguments: arguments)
} catch {
throw MunkiPkgError.notarizationFailed("\(failureMessage) \(error.localizedDescription)")
throw SwiftPkgError.notarizationFailed("\(failureMessage) \(error.localizedDescription)")
}
guard result.status == 0 else {
throw MunkiPkgError.notarizationFailed("notarytool: \(result.failureDetail(fallback: failureMessage))")
throw SwiftPkgError.notarizationFailed("notarytool: \(result.failureDetail(fallback: failureMessage))")
}
let data: Data
if result.stdoutString.hasPrefix("Generated JWT"), let newline = result.stdoutString.firstIndex(of: "\n") {
Expand All @@ -381,9 +381,9 @@ struct NotarizationService: Sendable {
do {
object = try PropertyListSerialization.propertyList(from: data, format: nil)
} catch {
throw MunkiPkgError.notarizationFailed("\(failureMessage) \(error.localizedDescription)")
throw SwiftPkgError.notarizationFailed("\(failureMessage) \(error.localizedDescription)")
}
guard let plist = object as? [String: Any] else { throw MunkiPkgError.notarizationFailed(failureMessage) }
guard let plist = object as? [String: Any] else { throw SwiftPkgError.notarizationFailed(failureMessage) }
return plist
}

Expand Down
Loading