Convert more comments to documentation

This commit is contained in:
Greg Neagle
2024-09-11 14:23:40 -07:00
parent 4729445418
commit f7cbba03a8
5 changed files with 84 additions and 84 deletions

View File

@@ -22,20 +22,20 @@ import Foundation
let ADMIN_BUNDLE_ID = "com.googlecode.munki.munkiimport" as CFString
/// Return an admin preference. Since this uses CFPreferencesCopyAppValue,
/// Preferences can be defined several places. Precedence is:
/// - MCX/configuration profile
/// - ~/Library/Preferences/ByHost/com.googlecode.munki.munkiimport.XXXXXX.plist
/// - ~/Library/Preferences/com.googlecode.munki.munkiimport.plist
/// - /Library/Preferences/com.googlecode.munki.munkiimport.plist
/// - .GlobalPreferences defined at various levels (ByHost, user, system)
/// But typically these preferences are _not_ managed and are stored in the
/// user's preferences (~/Library/Preferences/com.googlecode.munki.munkiimport.plist)
func adminPref(_ pref_name: String) -> Any? {
/* Return an admin preference. Since this uses CFPreferencesCopyAppValue,
Preferences can be defined several places. Precedence is:
- MCX/configuration profile
- ~/Library/Preferences/ByHost/com.googlecode.munki.munkiimport.XXXXXX.plist
- ~/Library/Preferences/com.googlecode.munki.munkiimport.plist
- /Library/Preferences/com.googlecode.munki.munkiimport.plist
- .GlobalPreferences defined at various levels (ByHost, user, system)
But typically these preferences are _not_ managed and are stored in the
user's preferences (~/Library/Preferences/com.googlecode.munki.munkiimport.plist)
*/
return CFPreferencesCopyAppValue(pref_name as CFString, ADMIN_BUNDLE_ID)
}
/// Adds `count` spaces to the start of `str`
func leftPad(_ str: String, _ count: Int) -> String {
if str.count < count {
return String(repeating: " ", count: count - str.count) + str

View File

@@ -31,6 +31,7 @@ struct MakeCatalogOptions {
var verbose: Bool = false
}
/// Struct that handles building catalogs
struct CatalogsMaker {
var repo: Repo
var options: MakeCatalogOptions
@@ -52,8 +53,8 @@ struct CatalogsMaker {
try getPkgsList()
}
/// Returns a list of pkginfo identifiers
mutating func getPkgsinfoList() throws {
// returns a list of pkginfo identifiers
do {
pkgsinfoList = try listItemsOfKind(repo, "pkgsinfo")
} catch is MunkiError {
@@ -62,8 +63,8 @@ struct CatalogsMaker {
}
}
/// Returns a list of pkg identifiers
mutating func getPkgsList() throws {
// returns a list of pkg identifiers
do {
pkgsList = try listItemsOfKind(repo, "pkgs")
} catch is MunkiError {
@@ -72,8 +73,8 @@ struct CatalogsMaker {
}
}
/// Builds a dictionary containing hashes for all our repo icons
mutating func hashIcons() -> [String: String] {
// Builds a dictionary containing hashes for all our repo icons
if options.verbose {
print("Getting list of icons...")
}
@@ -100,8 +101,8 @@ struct CatalogsMaker {
return iconHashes
}
/// Returns a case-insentitive match for installer_item from pkgsList, if any
func caseInsensitivePkgsListContains(_ installer_item: String) -> String? {
// returns a case-insentitive match for installer_item from pkgsList, if any
for repo_pkg in pkgsList {
if installer_item.lowercased() == repo_pkg.lowercased() {
return repo_pkg
@@ -110,9 +111,8 @@ struct CatalogsMaker {
return nil
}
/// Returns true if referenced installer items are present, false otherwise. Updates list of errors.
mutating func verify(_ identifier: String, _ pkginfo: PlistDict) -> Bool {
// Returns true if referenced installer items are present,
// false otherwise. Updates list of errors.
if let installer_type = pkginfo["installer_type"] as? String {
if ["nopkg", "apple_update_metadata"].contains(installer_type) {
// no associated installer item (pkg) for these types
@@ -179,8 +179,8 @@ struct CatalogsMaker {
return true
}
/// Processes pkginfo files and updates catalogs and errors instance variables
mutating func processPkgsinfo() {
// Processes pkginfo files and updates catalogs and errors instance variables
catalogs["all"] = [PlistDict]()
// Walk through the pkginfo files
for pkginfoIdentifier in pkgsinfoList {
@@ -251,8 +251,8 @@ struct CatalogsMaker {
}
}
/// Clear out old catalogs
mutating func cleanupCatalogs() {
// clear out old catalogs
do {
let catalogList = try repo.list("catalogs")
for catalogName in catalogList {
@@ -270,12 +270,10 @@ struct CatalogsMaker {
}
}
/// Assembles all pkginfo files into catalogs.
/// User calling this needs to be able to write to the repo/catalogs directory.
/// Returns a list of any errors it encountered
mutating func makecatalogs() -> [String] {
// Assembles all pkginfo files into catalogs.
// User calling this needs to be able to write to the repo/catalogs
// directory.
// Returns a list of any errors it encountered
// process pkgsinfo items
processPkgsinfo()

View File

@@ -21,9 +21,9 @@
import Darwin.C
import Foundation
/// If there is exactly one supported architecture, return a string with it
/// Otherwise return empty string
func getSingleArch(_ pkginfo: PlistDict) -> String {
// If there is exactly one supported architecture, return a string with it
// Otherwise return empty string
if let archList = pkginfo["supported_architectures"] as? [String],
archList.count == 1
{
@@ -32,12 +32,11 @@ func getSingleArch(_ pkginfo: PlistDict) -> String {
return ""
}
/// Copies an item to the appropriate place in the repo.
/// If itempath is a path within the repo/pkgs directory, copies nothing.
/// Renames the item if an item already exists with that name.
/// Returns the identifier for the item in the repo.
func copyInstallerItemToRepo(_ repo: Repo, itempath: String, version: String, subdirectory: String = "") throws -> String {
// Copies an item to the appropriate place in the repo.
// If itempath is a path within the repo/pkgs directory, copies nothing.
// Renames the item if an item already exists with that name.
// Returns the identifier for the item in the repo.
let destPath = ("pkgs" as NSString).appendingPathComponent(subdirectory)
var itemName = (itempath as NSString).lastPathComponent
var destIdentifier = (destPath as NSString).appendingPathComponent(itemName)
@@ -86,9 +85,9 @@ func copyInstallerItemToRepo(_ repo: Repo, itempath: String, version: String, su
}
}
/// Saves pkginfo to <munki_repo>/pkgsinfo/subdirectory
/// Can throw PlistError.writeError, RepoError, or RepoCopyError
func copyPkgInfoToRepo(_ repo: Repo, pkginfo: PlistDict, subdirectory: String = "") throws -> String {
// Saves pkginfo to <munki_repo>/pkgsinfo/subdirectory
// Can throw PlistError.writeError, RepoError, or RepoCopyError
let pkginfoData = try plistToData(pkginfo)
let destinationPath = ("pkgsinfo" as NSString).appendingPathComponent(subdirectory)
var pkginfoExt = adminPref("pkginfo_extension") as? String ?? ""
@@ -143,8 +142,8 @@ struct CatalogDatabase {
var items: [PlistDict]
}
/// Builds a dictionary we use like a database to look up info
func makeCatalogDB(_ repo: Repo) throws -> CatalogDatabase {
// Builds a dictionary we use like a database to look up info
let allCatalog: Data
let catalogItems: [PlistDict]
do {
@@ -261,9 +260,9 @@ func makeCatalogDB(_ repo: Repo) throws -> CatalogDatabase {
return catalogDB
}
/// Looks through repo catalogs looking for matching pkginfo
/// Returns a pkginfo dictionary, or nil
func findMatchingPkginfo(_ repo: Repo, _ pkginfo: PlistDict) -> PlistDict? {
// Looks through repo catalogs looking for matching pkginfo
// Returns a pkginfo dictionary, or nil
var catalogDB: CatalogDatabase
do {
@@ -348,8 +347,8 @@ func findMatchingPkginfo(_ repo: Repo, _ pkginfo: PlistDict) -> PlistDict? {
return nil
}
/// Return repo identifier for icon
func getIconIdentifier(_ pkginfo: PlistDict) -> String {
// Return repo identifier for icon
var iconName = pkginfo["icon_name"] as? String ?? pkginfo["name"] as? String ?? ""
if (iconName as NSString).pathExtension.isEmpty {
iconName += ".png"
@@ -357,8 +356,8 @@ func getIconIdentifier(_ pkginfo: PlistDict) -> String {
return ("icons" as NSString).appendingPathComponent(iconName)
}
/// Returns true if there is an icon for this item in the repo
func iconIsInRepo(_ repo: Repo, pkginfo: PlistDict) -> Bool {
// Returns true if there is an icon for this item in the repo
let iconIdentifer = getIconIdentifier(pkginfo)
do {
let iconList = try listItemsOfKind(repo, "icons")
@@ -372,9 +371,9 @@ func iconIsInRepo(_ repo: Repo, pkginfo: PlistDict) -> Bool {
}
}
/// Convert icon file to png and save to repo icon path.
/// Returns resource path to icon in repo
func convertAndInstallIcon(_ repo: Repo, name: String, iconPath: String) throws -> String {
// Convert icon file to png and save to repo icon path.
// Returns resource path to icon in repo
guard let tmpDir = TempDir.shared.makeTempDir() else {
throw MunkiError("Could not create a temp directory")
}
@@ -397,9 +396,9 @@ func convertAndInstallIcon(_ repo: Repo, name: String, iconPath: String) throws
throw MunkiError("Could not create icon \(pngName) in repo: failed to convert icon to png")
}
/// Generates a product icon from a startosinstall item
/// and uploads to the repo. Returns repo identifier for icon
func generatePNGFromStartOSInstallItem(_ repo: Repo, installerDMG: String, itemname: String) throws -> String {
// Generates a product icon from a startosinstall item
// and uploads to the repo. Returns repo identifier for icon
do {
let mountpoint = try mountdmg(installerDMG)
defer {
@@ -421,9 +420,9 @@ func generatePNGFromStartOSInstallItem(_ repo: Repo, installerDMG: String, itemn
}
}
/// Generates a product icon from a copy_from_dmg item
/// and uploads to the repo. Returns repo path to icon
func generatePNGFromDMGitem(_ repo: Repo, dmgPath: String, pkginfo: PlistDict) throws -> String {
// Generates a product icon from a copy_from_dmg item
// and uploads to the repo. Returns repo path to icon
guard let itemname = pkginfo["name"] as? String else {
throw MunkiError("pkginfo is missing 'name'")
}
@@ -457,10 +456,10 @@ func generatePNGFromDMGitem(_ repo: Repo, dmgPath: String, pkginfo: PlistDict) t
}
}
/// Generates a product icon (or candidate icons) from an installer pkg
/// and uploads to the repo. Returns repo path(s) to icon(s)
/// itemPath can be a path to a disk image or to a package
func generatePNGsFromPkg(_ repo: Repo, itemPath: String, pkginfo: PlistDict, importMultiple: Bool = true) throws -> [String] {
// Generates a product icon (or candidate icons) from an installer pkg
// and uploads to the repo. Returns repo path(s) to icon(s)
// itemPath can be a path to a disk image or to a package
guard let itemname = pkginfo["name"] as? String else {
// this should essentially never happen
throw MunkiError("Pkginfo is missing 'name': \(pkginfo)")
@@ -522,8 +521,8 @@ func generatePNGsFromPkg(_ repo: Repo, itemPath: String, pkginfo: PlistDict, imp
return importedPaths
}
/// Saves a product icon to the repo. Returns repo path.
func copyIconToRepo(_ repo: Repo, iconPath: String) throws -> String {
// Saves a product icon to the repo. Returns repo path.
let destPath = "icons"
let iconName = (iconPath as NSString).lastPathComponent
let repoIdentifier = (destPath as NSString).appendingPathComponent(iconName)
@@ -555,9 +554,9 @@ func copyIconToRepo(_ repo: Repo, iconPath: String) throws -> String {
}
}
/// Extracts an icon (or icons) from an installer item, converts to png, and
/// copies to repo. Returns repo path to imported icon(s)
func extractAndCopyIcon(_ repo: Repo, installerItem: String, pkginfo: PlistDict, importMultiple: Bool = true) throws -> [String] {
// Extracts an icon (or icons) from an installer item, converts to png, and
// copies to repo. Returns repo path to imported icon(s)
let installerType = pkginfo["installer_type"] as? String ?? ""
switch installerType {
case "copy_from_dmg", "stage_os_installer":
@@ -580,6 +579,7 @@ func extractAndCopyIcon(_ repo: Repo, installerItem: String, pkginfo: PlistDict,
return [String]()
}
/// A subclass of AsyncProcessRunner to create disk images
class HdiUtilCreateFromFolderRunner: AsyncProcessRunner {
init(sourceDir: String, outputPath: String) {
let tool = "/usr/bin/hdiutil"
@@ -600,10 +600,10 @@ class HdiUtilCreateFromFolderRunner: AsyncProcessRunner {
}
}
/// Wraps dirPath (generally an app bundle or bundle-style pkg into a disk image.
/// Returns path to the created dmg file
/// async because it can take a while, depending on the size of the item
func makeDmg(_ dirPath: String) async -> String {
// Wraps dirPath (generally an app bundle or bundle-style pkg
// into a disk image. Returns path to the created dmg file
// async because it can take a while, depending on the size of the item
let itemname = (dirPath as NSString).lastPathComponent
print("Making disk image containing \(itemname)...")
let dmgName = (itemname as NSString).deletingPathExtension + ".dmg"
@@ -622,8 +622,8 @@ func makeDmg(_ dirPath: String) async -> String {
return dmgPath
}
/// Prompts the user for a subdirectory for the pkg and pkginfo
func promptForSubdirectory(_ repo: Repo, _ subdirectory: String?) -> String {
// Prompts the user for a subdirectory for the pkg and pkginfo
var existingSubdirs: Set<String> = []
let pkgsinfoList = (try? repo.list("pkgsinfo")) ?? [String]()
for item in pkgsinfoList {
@@ -646,8 +646,8 @@ func promptForSubdirectory(_ repo: Repo, _ subdirectory: String?) -> String {
}
}
/// Opens pkginfo list in the user's chosen editor.
func editPkgInfoInExternalEditor(_ pkginfo: PlistDict) -> PlistDict {
// Opens pkginfo list in the user's chosen editor.
guard let editor = adminPref("editor") as? String else {
return pkginfo
}

View File

@@ -21,13 +21,12 @@
import ArgumentParser
import Foundation
// Defines option groups for makepkginfo
// These are also used by munkiimport
/// Defines option groups for makepkginfo
/// These are also used by munkiimport
/// Collect all our OptionGroups into a single struct
/// I don't love this because if options move into different groups we have to change other stuff
struct PkginfoOptions {
// collect all our OptionGroups into a single struct
// I don't love this because if options move into different groups
// we have to change other stuff
var override: OverrideOptions
var script: ScriptOptions
var dmg: DragNDropOptions
@@ -39,25 +38,28 @@ struct PkginfoOptions {
var hidden: HiddenPkginfoOptions
}
/// Supported restart actions
enum RestartAction: String, CaseIterable, ExpressibleByArgument {
case RequireRestart
case RecommendRestart
case RequireLogout
}
/// Supported installer types for --installer-type argument
enum InstallerType: String, CaseIterable, ExpressibleByArgument {
case copy_from_dmg
case startosinstall
case stage_os_installer
}
/// Supported values for architecture
enum SupportedArchitecture: String, CaseIterable, ExpressibleByArgument {
case x86_64
case arm64
}
/// Pkginfo Override Options
struct OverrideOptions: ParsableArguments {
// Pkginfo Override Options
@Option(help: "Name to be used to refer to the installer item.")
var name: String? = nil
@@ -92,8 +94,8 @@ struct OverrideOptions: ParsableArguments {
}
}
/// Options related to scripts
struct ScriptOptions: ParsableArguments {
// Script options
@Option(name: [.long, .customLong("installcheck_script")],
help: ArgumentHelp("Path to an optional script to be run to determine if item should be installed. An exit code of 0 indicates installation should occur. Takes precedence over installs items and receipts.", valueName: "path"))
var installcheckScript: String? = nil
@@ -123,8 +125,8 @@ struct ScriptOptions: ParsableArguments {
var uninstallScript: String? = nil
}
/// "Drag-n-drop" Disk Image Options
struct DragNDropOptions: ParsableArguments {
// "Drag-n-drop" Disk Image Options
@Option(name: [.short, .long, .customShort("a"), .customLong("app")],
help: "Name or relative path of the item to be installed. Useful if there is more than one item at the root of the dmg or the item is located in a subdirectory. Absolute paths can be provided as well but they must point to an item located within the dmg.")
var item: String? = nil
@@ -158,8 +160,8 @@ struct DragNDropOptions: ParsableArguments {
}
}
/// Apple package specific options
struct ApplePackageOptions: ParsableArguments {
// Apple package specific options
@Option(name: .shortAndLong,
help: "If the installer item is a disk image containing multiple packages, or the package to be installed is not at the root of the mounted disk image, <pkgname> is a relative path from the root of the mounted disk image to the specific package to be installed.")
var pkgname: String? = nil
@@ -202,8 +204,8 @@ struct ApplePackageOptions: ParsableArguments {
}
}
/// Forced/Unattended (install) options
struct UnattendedInstallOptions: ParsableArguments {
// Forced/Unattended (install) options
@Flag(name: [.long, .customLong("unattended_install")],
help: "Item can be installed without notifying the user.")
var unattendedInstall = false
@@ -227,15 +229,15 @@ struct UnattendedInstallOptions: ParsableArguments {
}
}
/// Options for generating `installs` items
struct GeneratingInstallsOptions: ParsableArguments {
// 'installs' generation options
@Option(name: .shortAndLong,
help: "Path to a filesystem item installed by this installer item, typically an application. This generates an 'installs' item for the pkginfo, to be used to determine if this software has been installed. Can be specified multiple times.")
var file = [String]()
}
/// installer type options
struct InstallerTypeOptions: ParsableArguments {
// installer type options
@Option(name: [.long, .customLong("installer_type")],
help: "Specify an intended installer_type when the installer item could be one of multiple types. Currently supported only to specify the intended type when importing a macOS installer.")
var installerType: InstallerType? = nil

View File

@@ -26,9 +26,9 @@
import Foundation
/// Helps us record information about the environment in which the pkginfo was
/// created so we have a bit of an audit trail. Returns a dictionary.
func pkginfoMetadata() -> PlistDict {
// Helps us record information about the environment in which the pkginfo was
// created so we have a bit of an audit trail. Returns a dictionary.
var metadata = PlistDict()
metadata["created_by"] = NSUserName()
metadata["creation_date"] = Date()
@@ -37,11 +37,11 @@ func pkginfoMetadata() -> PlistDict {
return metadata
}
/// Gets package metadata for the package at pkgpath.
/// Returns pkginfo
func createPkgInfoFromPkg(_ pkgpath: String,
options: PkginfoOptions) throws -> PlistDict
{
// Gets package metadata for the package at pkgpath.
// Returns pkginfo
var info = PlistDict()
if hasValidPackageExt(pkgpath) {
@@ -63,10 +63,10 @@ func createPkgInfoFromPkg(_ pkgpath: String,
return info
}
/// Creates an item for a pkginfo "installs" array
/// Determines if the item is an application, bundle, Info.plist, or a file or
/// directory and gets additional metadata for later comparison.
func createInstallsItem(_ itempath: String) -> PlistDict {
// Creates an item for a pkginfo "installs" array
// Determines if the item is an application, bundle, Info.plist, or a file or
// directory and gets additional metadata for later comparison.
var info = PlistDict()
if isApplication(itempath) {
info["type"] = "application"
@@ -136,8 +136,8 @@ func createInstallsItem(_ itempath: String) -> PlistDict {
return info
}
/// Processes a drag-n-drop dmg to build pkginfo
func createPkgInfoForDragNDrop(_ mountpoint: String, options: PkginfoOptions) throws -> PlistDict {
// processes a drag-n-drop dmg to build pkginfo
var info = PlistDict()
var dragNDropItem = ""
var installsitem = PlistDict()
@@ -248,13 +248,13 @@ func createPkgInfoForDragNDrop(_ mountpoint: String, options: PkginfoOptions) th
return info
}
/// Mounts a disk image if it"s not already mounted
/// Builds pkginfo for the first installer item found at the root level,
/// or a specific one if specified by options.pkgname or options.item
/// Unmounts the disk image if it wasn"t already mounted
func createPkgInfoFromDmg(_ dmgpath: String,
options: PkginfoOptions) throws -> PlistDict
{
// Mounts a disk image if it"s not already mounted
// Builds pkginfo for the first installer item found at the root level,
// or a specific one if specified by options.pkgname or options.item
// Unmounts the disk image if it wasn"t already mounted
var info = PlistDict()
let wasAlreadyMounted = diskImageIsMounted(dmgpath)
var mountpoint = ""
@@ -306,19 +306,19 @@ func createPkgInfoFromDmg(_ dmgpath: String,
return info
}
/// Attempt to read a file with the same name as the input string and return its text,
/// otherwise return the input string
func readFileOrString(_ fileNameOrString: String) -> String {
// attempt to read a file with the same name as the input string and return its text,
// otherwise return the input string
if let fileText = try? String(contentsOfFile: fileNameOrString, encoding: .utf8) {
return fileText
if !pathExists(fileNameOrString) {
return fileNameOrString
}
return fileNameOrString
return (try? String(contentsOfFile: fileNameOrString, encoding: .utf8)) ?? fileNameOrString
}
/// Return a pkginfo dictionary for installeritem
func makepkginfo(_ filepath: String?,
options: PkginfoOptions) throws -> PlistDict
{
// Return a pkginfo dictionary for installeritem
var installeritem = filepath ?? ""
var pkginfo = PlistDict()