Implement iconimporter

This commit is contained in:
Greg Neagle
2024-09-13 18:49:38 -07:00
parent 3f6194e5d3
commit 75c621f7ce
2 changed files with 462 additions and 2 deletions
+305 -2
View File
@@ -1,11 +1,314 @@
//
// main.swift
// iconimporter.swift
// iconimporter
//
// Created by Greg Neagle on 9/13/24.
//
// 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 ArgumentParser
import Foundation
print("Hello, World!")
// MARK: icon generation and copying
/// Copies png file in path to repo as icons/iconname.png
func copyIconToRepo(_ repo: Repo, iconname: String, path: String) {
let iconRef = "icons/\(iconname).png"
do {
try repo.put(iconRef, fromFile: path)
print("\tWrote: \(iconRef)")
} catch {
printStderr("\tError uploading \(iconRef): \(error.localizedDescription)")
}
}
/// Returns filesystem path to installer item, downloading if needed
func getInstallerItemPath(repo: Repo, pkginfo: PlistDict) -> String? {
let itemName = pkginfo["name"] as? String ?? "UNKNOWN"
guard let installerItem = pkginfo["installer_item_location"] as? String else {
printStderr("Could not get installer item location for \(itemName).")
return nil
}
let installerItemRef = "pkgs/\(installerItem)"
// first attempt to ask the repo plugin for the filesystem path to the item
if let tempPath = repo.pathFor(installerItemRef) {
return tempPath
} else {
// need to download the item from the repo because
// we don't have direct filesystem access to it
guard let tempFile = tempFile() else {
printStderr("Could not get temp file to download \(installerItem).")
return nil
}
do {
try repo.get(installerItemRef, toFile: tempFile)
return tempFile
} catch {
printStderr("Could not download \(installerItem) from repo: \(error.localizedDescription)")
return nil
}
}
}
/// Generate a PNG from a disk image containing an Install macOS app
func generatePNGFromStartOSInstallItem(_ repo: Repo, item: PlistDict) {
let itemName = item["name"] as? String ?? "UNKNOWN"
guard let dmgPath = getInstallerItemPath(repo: repo, pkginfo: item) else {
printStderr("Skipping.")
return
}
guard let mountpoint = try? mountdmg(dmgPath) else {
printStderr("Could not mount the disk image for \(itemName). Skipping.")
return
}
defer { unmountdmg(mountpoint) }
guard let appPath = findInstallMacOSApp(mountpoint) else {
printStderr("Could not find Install macOS app for \(itemName). Skipping.")
return
}
guard let iconPath = findIconForApp(appPath) else {
print("\tNo application icons found.")
return
}
if let iconTemp = tempFile(),
convertIconToPNG(iconPath: iconPath, destinationPath: iconTemp)
{
copyIconToRepo(repo, iconname: itemName, path: iconTemp)
} else {
printStderr("\tError converting \(iconPath) to png.")
}
}
/// Generate a PNG from a disk image containing an application
// TODO: handle case where there are multiple apps in items_to_copy
func generatePNGFromDMGItem(_ repo: Repo, item: PlistDict) {
let itemName = item["name"] as? String ?? "UNKNOWN"
guard let dmgPath = getInstallerItemPath(repo: repo, pkginfo: item) else {
printStderr("Skipping.")
return
}
guard let mountpoint = try? mountdmg(dmgPath) else {
printStderr("Could not mount the disk image for \(itemName). Skipping.")
return
}
defer { unmountdmg(mountpoint) }
let itemsToCopy = item["items_to_copy"] as? [PlistDict] ?? []
let apps = itemsToCopy.filter {
($0["source_item"] as? String ?? "").hasSuffix(".app")
}
if apps.isEmpty {
print("\tNo application icons found.")
return
}
if let appPath = apps[0]["source_item"] as? String,
let iconPath = findIconForApp((mountpoint as NSString).appendingPathComponent(appPath))
{
if let iconTemp = tempFile(),
convertIconToPNG(iconPath: iconPath, destinationPath: iconTemp)
{
copyIconToRepo(repo, iconname: itemName, path: iconTemp)
} else {
print("\tError converting \(iconPath) to png.")
}
} else {
print("\tNo application icons found.")
}
}
/// Generate PNGS from applications inside a pkg
func generatePNGsFromPkg(_ repo: Repo, item: PlistDict) {
let itemName = item["name"] as? String ?? "UNKNOWN"
guard let installerItemPath = getInstallerItemPath(repo: repo, pkginfo: item) else {
printStderr("Skipping.")
return
}
var pkgPath = ""
var savedMountPoint: String?
if hasValidDiskImageExt(installerItemPath) {
guard let mountpoint = try? mountdmg(installerItemPath) else {
printStderr("Could not mount the disk image for \(itemName). Skipping.")
return
}
savedMountPoint = mountpoint
if let packagePath = item["package_path"] as? String {
pkgPath = (mountpoint as NSString).appendingPathComponent(packagePath)
} else {
// find first item that appears to be a pkg at the root
if let itemList = try? FileManager.default.contentsOfDirectory(atPath: mountpoint) {
for item in itemList {
if hasValidPackageExt(item) {
pkgPath = (mountpoint as NSString).appendingPathComponent(item)
break
}
}
}
}
} else if hasValidPackageExt(installerItemPath) {
pkgPath = installerItemPath
}
var iconPaths = [String]()
if !pkgPath.isEmpty {
if pathIsDirectory(pkgPath) {
iconPaths = extractAppIconsFromBundlePkg(pkgPath)
} else {
iconPaths = extractAppIconsFromFlatPkg(pkgPath)
}
}
if let savedMountPoint {
unmountdmg(savedMountPoint)
}
if iconPaths.count == 1 {
if let iconTemp = tempFile(),
convertIconToPNG(iconPath: iconPaths[0], destinationPath: iconTemp)
{
copyIconToRepo(repo, iconname: itemName, path: iconTemp)
} else {
printStderr("\tError converting \(iconPaths[0]) to png.")
}
} else if iconPaths.count > 1 {
for (index, iconPath) in iconPaths.enumerated() {
let iconName = itemName + "_\(index + 1)"
if let iconTemp = tempFile(),
convertIconToPNG(iconPath: iconPath, destinationPath: iconTemp)
{
copyIconToRepo(repo, iconname: iconName, path: iconTemp)
} else {
printStderr("\tError converting \(iconPath) to png.")
}
}
} else {
print("\tNo application icons found.")
}
}
/// Builds a list of items to check; only the latest version of an item is retained.
/// If itemlist is given, include items only on that list.
func findItemsToCheck(_ repo: Repo, items: [String] = []) -> [PlistDict] {
var catalogItems = [PlistDict]()
do {
let allCatalogData = try repo.get("catalogs/all")
catalogItems = try (readPlist(fromData: allCatalogData)) as? [PlistDict] ?? []
} catch {
printStderr("Error getting catalog data from repo: \(error.localizedDescription)")
return []
}
var itemDB = [String: PlistDict]()
for catalogItem in catalogItems {
let itemName = catalogItem["name"] as? String ?? "UNKNOWN"
let itemVersion = catalogItem["version"] as? String ?? "UNKNOWN"
if !items.isEmpty, !items.contains(itemName) {
continue
}
if !itemDB.keys.contains(itemName) {
itemDB[itemName] = catalogItem
} else {
let dbVersion = itemDB[itemName]?["version"] as? String ?? ""
if MunkiVersion(itemVersion) > MunkiVersion(dbVersion) {
itemDB[itemName] = catalogItem
}
}
}
return Array(itemDB.values)
}
/// Generate PNGs from either pkgs or disk images containing applications
func generatePNGsFromMunkiItems(_ repo: Repo, force: Bool = false, items: [String] = []) {
let iconsList = (try? repo.list("icons")) ?? []
let itemList = findItemsToCheck(repo, items: items)
for item in itemList {
let itemName = item["name"] as? String ?? "UNKNOWN"
var iconName = item["icon_name"] as? String ?? itemName
print("Processing \(itemName)...")
let iconNameExt = (iconName as NSString).pathExtension
if iconNameExt.isEmpty {
iconName += ".png"
}
if iconsList.contains(iconName), !force {
print("Found existing icon at \(iconName)")
continue
}
let installerType = item["installer_type"] as? String ?? ""
if installerType == "copy_from_dmg" {
generatePNGFromDMGItem(repo, item: item)
} else if installerType == "startosinstall" {
generatePNGFromStartOSInstallItem(repo, item: item)
} else if installerType == "" {
generatePNGsFromPkg(repo, item: item)
} else {
print("\tCan't process installer type: \(installerType)")
}
}
// clean up any temp files we may have generated during this run
TempDir.shared.cleanUp()
}
// MARK: options and main run command
@main
struct IconImporter: ParsableCommand {
static let configuration = CommandConfiguration(
commandName: "iconimporter",
abstract: "Imports icons into a Munki repo"
)
@Flag(name: .shortAndLong,
help: "Create pngs even if there is an existing icon in the repo.")
var force = false
@Option(name: .shortAndLong,
help: "Only run for given pkginfo item name(s).")
var item: [String] = []
@Option(name: .long,
help: "Optional. Custom plugin to connect to repo.")
var plugin = "FileRepo"
@Option(name: [.customLong("repo_url"), .customLong("repo-url")],
help: "Optional. repo fileshare URL used by repo plugin.")
var repoURL = ""
@Argument(help: ArgumentHelp(
"Optional path to Munki repo directory.",
valueName: "munki-repo-path"
))
var repoPath = ""
mutating func validate() throws {
if repoURL.isEmpty, repoPath.isEmpty {
throw ValidationError("Must specify a repo URL or repo path!")
}
if repoURL.isEmpty {
// repoPath must be defined
while repoPath.hasSuffix("/") {
repoPath = String(repoPath.dropLast())
}
if let url = URL(string: repoPath),
url.scheme != nil
{
repoURL = url.absoluteString
} else {
repoURL = URL(fileURLWithPath: repoPath).absoluteString
}
}
}
mutating func run() throws {
do {
let repo = try repoConnect(url: repoURL, plugin: plugin)
generatePNGsFromMunkiItems(repo, force: force, items: item)
} catch {
printStderr("Could not connect to the munki repo: \(error.localizedDescription)")
throw ExitCode(-1)
}
}
}
@@ -184,6 +184,36 @@
C07AED702C67DF6B00DE6119 /* gurl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED6E2C67DF6B00DE6119 /* gurl.swift */; };
C07AED722C67F77000DE6119 /* sslerrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED712C67F77000DE6119 /* sslerrors.swift */; };
C07AED732C67F77000DE6119 /* sslerrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED712C67F77000DE6119 /* sslerrors.swift */; };
C0848D1A2C94D1840008B463 /* iconimporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0848D192C94D1840008B463 /* iconimporter.swift */; };
C0848D1F2C94D4610008B463 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = C0848D1E2C94D4610008B463 /* ArgumentParser */; };
C0848D202C94D7600008B463 /* FileRepo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FD52C2B7F2100090743 /* FileRepo.swift */; };
C0848D212C94D7640008B463 /* GitFileRepo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364482C2F8EFE008DB215 /* GitFileRepo.swift */; };
C0848D222C94D7660008B463 /* RepoFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013644C2C30F5D8008DB215 /* RepoFactory.swift */; };
C0848D232C94D76A0008B463 /* repoutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D00FA72C45814F0021DA9C /* repoutils.swift */; };
C0848D242C94D7B50008B463 /* fileutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A9B52C3DF6D0007F0B34 /* fileutils.swift */; };
C0848D252C94D7B90008B463 /* iconutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A9BE2C409738007F0B34 /* iconutils.swift */; };
C0848D262C94D7F00008B463 /* errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D00FB52C45BCB90021DA9C /* errors.swift */; };
C0848D272C94D8060008B463 /* plistutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364422C2DD1BA008DB215 /* plistutils.swift */; };
C0848D282C94D8120008B463 /* constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FB12C2B22D300090743 /* constants.swift */; };
C0848D292C94D8230008B463 /* cliutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FD92C2CF19600090743 /* cliutils.swift */; };
C0848D2A2C94D8360008B463 /* display.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364572C3265D6008DB215 /* display.swift */; };
C0848D2B2C94D83E0008B463 /* munkilog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074DB2C33AE5F00B86310 /* munkilog.swift */; };
C0848D2C2C94DDA50008B463 /* munkistatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2A52C62C12E0019A067 /* munkistatus.swift */; };
C0848D2D2C94DDAF0008B463 /* prefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FAF2C2B22A400090743 /* prefs.swift */; };
C0848D2E2C94DDE30008B463 /* reports.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074DE2C33B9A000B86310 /* reports.swift */; };
C0848D2F2C94DE240008B463 /* admincommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013643E2C2DCA5C008DB215 /* admincommon.swift */; };
C0848D302C94ED600008B463 /* dmgutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364532C321FE7008DB215 /* dmgutils.swift */; };
C0848D312C94EE7A0008B463 /* utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FD12C2B654300090743 /* utils.swift */; };
C0848D322C94EEA60008B463 /* version.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D00FAF2C458EAA0021DA9C /* version.swift */; };
C0848D332C94EF010008B463 /* osinstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A9B12C3DB88E007F0B34 /* osinstaller.swift */; };
C0848D342C94F62C0008B463 /* pkgutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074E72C34932400B86310 /* pkgutils.swift */; };
C0848D352C94F6420008B463 /* versionutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074EA2C34A6AD00B86310 /* versionutils.swift */; };
C0848D362C94F6610008B463 /* ODutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C213F2C8E1FF50023E9D9 /* ODutils.swift */; };
C0848D372C94F68B0008B463 /* info.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0209B882C63DDF1007664A0 /* info.swift */; };
C0848D382C94F6D10008B463 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
C0848D392C94F6D70008B463 /* launchd.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2562C5D391C0019A067 /* launchd.swift */; };
C0848D3A2C94F70C0008B463 /* scriptutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2A22C61939E0019A067 /* scriptutils.swift */; };
C0848D3B2C94F7190008B463 /* osutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074E42C34910F00B86310 /* osutils.swift */; };
C0D00FA12C457E2B0021DA9C /* munkihash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A98E2C39C135007F0B34 /* munkihash.swift */; };
C0D00FA22C457E4E0021DA9C /* display.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364572C3265D6008DB215 /* display.swift */; };
C0D00FA32C457E5F0021DA9C /* fileutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A9B52C3DF6D0007F0B34 /* fileutils.swift */; };
@@ -309,6 +339,15 @@
);
runOnlyForDeploymentPostprocessing = 1;
};
C0848D152C94D1840008B463 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
C0D9C2492C5C5C920019A067 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -413,6 +452,8 @@
C07AED6A2C66F56C00DE6119 /* manifests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = manifests.swift; sourceTree = "<group>"; };
C07AED6E2C67DF6B00DE6119 /* gurl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = gurl.swift; sourceTree = "<group>"; };
C07AED712C67F77000DE6119 /* sslerrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = sslerrors.swift; sourceTree = "<group>"; };
C0848D172C94D1840008B463 /* iconimporter */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = iconimporter; sourceTree = BUILT_PRODUCTS_DIR; };
C0848D192C94D1840008B463 /* iconimporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iconimporter.swift; sourceTree = "<group>"; };
C0D00FA72C45814F0021DA9C /* repoutils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = repoutils.swift; sourceTree = "<group>"; };
C0D00FAF2C458EAA0021DA9C /* version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = version.swift; sourceTree = "<group>"; };
C0D00FB52C45BCB90021DA9C /* errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = errors.swift; sourceTree = "<group>"; };
@@ -486,6 +527,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0848D142C94D1840008B463 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C0848D1F2C94D4610008B463 /* ArgumentParser in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0D9C2482C5C5C920019A067 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -603,6 +652,7 @@
C0D9C24C2C5C5C920019A067 /* app_usage_monitor */,
C0D9C28B2C5EDFAE0019A067 /* appusaged */,
C0209B902C63E3B7007664A0 /* launchapp */,
C0848D182C94D1840008B463 /* iconimporter */,
C07A6FA62C2A82B400090743 /* Products */,
C01364502C311DFA008DB215 /* Frameworks */,
);
@@ -620,6 +670,7 @@
C0D9C24B2C5C5C920019A067 /* app_usage_monitor */,
C0D9C28A2C5EDFAE0019A067 /* appusaged */,
C0209B8F2C63E3B7007664A0 /* launchapp */,
C0848D172C94D1840008B463 /* iconimporter */,
);
name = Products;
sourceTree = "<group>";
@@ -739,6 +790,14 @@
path = network;
sourceTree = "<group>";
};
C0848D182C94D1840008B463 /* iconimporter */ = {
isa = PBXGroup;
children = (
C0848D192C94D1840008B463 /* iconimporter.swift */,
);
path = iconimporter;
sourceTree = "<group>";
};
C0D9C24C2C5C5C920019A067 /* app_usage_monitor */ = {
isa = PBXGroup;
children = (
@@ -893,6 +952,26 @@
productReference = C07A6FC42C2B5C0700090743 /* makecatalogs */;
productType = "com.apple.product-type.tool";
};
C0848D162C94D1840008B463 /* iconimporter */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0848D1D2C94D1840008B463 /* Build configuration list for PBXNativeTarget "iconimporter" */;
buildPhases = (
C0848D132C94D1840008B463 /* Sources */,
C0848D142C94D1840008B463 /* Frameworks */,
C0848D152C94D1840008B463 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = iconimporter;
packageProductDependencies = (
C0848D1E2C94D4610008B463 /* ArgumentParser */,
);
productName = iconimporter;
productReference = C0848D172C94D1840008B463 /* iconimporter */;
productType = "com.apple.product-type.tool";
};
C0D9C24A2C5C5C920019A067 /* app_usage_monitor */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0D9C24F2C5C5C920019A067 /* Build configuration list for PBXNativeTarget "app_usage_monitor" */;
@@ -978,6 +1057,9 @@
C07A6FC32C2B5C0700090743 = {
CreatedOnToolsVersion = 15.4;
};
C0848D162C94D1840008B463 = {
CreatedOnToolsVersion = 15.4;
};
C0D9C24A2C5C5C920019A067 = {
CreatedOnToolsVersion = 15.4;
};
@@ -1014,6 +1096,7 @@
C0D9C24A2C5C5C920019A067 /* app_usage_monitor */,
C0D9C2892C5EDFAE0019A067 /* appusaged */,
C0209B8E2C63E3B7007664A0 /* launchapp */,
C0848D162C94D1840008B463 /* iconimporter */,
);
};
/* End PBXProject section */
@@ -1275,6 +1358,42 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0848D132C94D1840008B463 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0848D2C2C94DDA50008B463 /* munkistatus.swift in Sources */,
C0848D352C94F6420008B463 /* versionutils.swift in Sources */,
C0848D392C94F6D70008B463 /* launchd.swift in Sources */,
C0848D252C94D7B90008B463 /* iconutils.swift in Sources */,
C0848D322C94EEA60008B463 /* version.swift in Sources */,
C0848D362C94F6610008B463 /* ODutils.swift in Sources */,
C0848D3A2C94F70C0008B463 /* scriptutils.swift in Sources */,
C0848D332C94EF010008B463 /* osinstaller.swift in Sources */,
C0848D382C94F6D10008B463 /* bootstrapping.swift in Sources */,
C0848D342C94F62C0008B463 /* pkgutils.swift in Sources */,
C0848D302C94ED600008B463 /* dmgutils.swift in Sources */,
C0848D272C94D8060008B463 /* plistutils.swift in Sources */,
C0848D2F2C94DE240008B463 /* admincommon.swift in Sources */,
C0848D372C94F68B0008B463 /* info.swift in Sources */,
C0848D2A2C94D8360008B463 /* display.swift in Sources */,
C0848D212C94D7640008B463 /* GitFileRepo.swift in Sources */,
C0848D282C94D8120008B463 /* constants.swift in Sources */,
C0848D2B2C94D83E0008B463 /* munkilog.swift in Sources */,
C0848D2E2C94DDE30008B463 /* reports.swift in Sources */,
C0848D2D2C94DDAF0008B463 /* prefs.swift in Sources */,
C0848D312C94EE7A0008B463 /* utils.swift in Sources */,
C0848D242C94D7B50008B463 /* fileutils.swift in Sources */,
C0848D3B2C94F7190008B463 /* osutils.swift in Sources */,
C0848D1A2C94D1840008B463 /* iconimporter.swift in Sources */,
C0848D262C94D7F00008B463 /* errors.swift in Sources */,
C0848D222C94D7660008B463 /* RepoFactory.swift in Sources */,
C0848D292C94D8230008B463 /* cliutils.swift in Sources */,
C0848D202C94D7600008B463 /* FileRepo.swift in Sources */,
C0848D232C94D76A0008B463 /* repoutils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0D9C2472C5C5C920019A067 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1616,6 +1735,30 @@
};
name = Release;
};
C0848D1B2C94D1840008B463 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 52ZFZKM6BK;
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
C0848D1C2C94D1840008B463 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 52ZFZKM6BK;
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
C0D9C2502C5C5C920019A067 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1752,6 +1895,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0848D1D2C94D1840008B463 /* Build configuration list for PBXNativeTarget "iconimporter" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0848D1B2C94D1840008B463 /* Debug */,
C0848D1C2C94D1840008B463 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0D9C24F2C5C5C920019A067 /* Build configuration list for PBXNativeTarget "app_usage_monitor" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -1793,6 +1945,11 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
C0848D1E2C94D4610008B463 /* ArgumentParser */ = {
isa = XCSwiftPackageProductDependency;
package = C0E994182C5C10E1006FDF44 /* XCRemoteSwiftPackageReference "swift-argument-parser" */;
productName = ArgumentParser;
};
C0E994192C5C10E1006FDF44 /* ArgumentParser */ = {
isa = XCSwiftPackageProductDependency;
package = C0E994182C5C10E1006FDF44 /* XCRemoteSwiftPackageReference "swift-argument-parser" */;