mirror of
https://github.com/munki/munki.git
synced 2026-01-26 17:09:30 -06:00
Finally getting the hang of how documentation comments work
This commit is contained in:
@@ -40,6 +40,7 @@ extension FetchError: LocalizedError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a sha256 hash of the file in an extended attribute, generating the hash if needed.
|
||||
func storeCachedChecksum(toPath path: String, hash: String? = nil) -> String? {
|
||||
let fhash: String = if let hash {
|
||||
hash
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import Foundation
|
||||
import Security
|
||||
|
||||
/// A simple logging function to use if none is given to Gurl
|
||||
func defaultLogger(_ message: String) {
|
||||
print(message)
|
||||
}
|
||||
@@ -42,8 +43,7 @@ struct GurlOptions {
|
||||
var log: (String) -> Void = defaultLogger // logging function
|
||||
}
|
||||
|
||||
/// A class for getting content from a URL
|
||||
/// using NSURLSession and friends
|
||||
/// A class for getting content from a URL using NSURLSession and friends
|
||||
class Gurl: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate {
|
||||
let GURL_XATTR = "com.googlecode.munki.downloadData"
|
||||
|
||||
@@ -199,9 +199,8 @@ class Gurl: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionData
|
||||
}
|
||||
}
|
||||
|
||||
/// Since HTTP header names are not case-sensitive, we normalize a
|
||||
/// dictionary of HTTP headers by converting all the key names to
|
||||
/// lower case
|
||||
/// Normalize a dictionary of HTTP headers by converting all the key names to
|
||||
/// lower case, since HTTP header names are not case-sensitive
|
||||
func normalizeHeaderDict(_ headers: [String: String]) -> [String: String] {
|
||||
var normalizedHeaders = [String: String]()
|
||||
for (key, value) in headers {
|
||||
|
||||
@@ -7,19 +7,24 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Uses URL to compose a URL from basURL and additional relative path
|
||||
func composedURLWithBase(_ baseURLString: String, adding path: String) -> String {
|
||||
let baseURL = URL(string: baseURLString)
|
||||
let composedURL = URL(string: path, relativeTo: baseURL)
|
||||
return composedURL?.absoluteString ?? ""
|
||||
}
|
||||
|
||||
/// Returns a URL to something in a Munki repo.
|
||||
/// If type is empty, returns the base URL for the Munki repo.
|
||||
/// If type is one of the supported types, returns the type-specific URL.
|
||||
/// If type is specifiied and resource is also specifiied, a full URL to the resource is
|
||||
/// constructed and returned
|
||||
func munkiRepoURL(_ type: String = "", resource: String = "") -> String? {
|
||||
/// we could use composedURLWithBase, but that doesn't handle
|
||||
/// URLs in the format of CGI invocations correctly, and would not
|
||||
/// be consistent with the behavior of the Python version of Munki
|
||||
/// So instead we'll do simple string concatenation
|
||||
/// (with percent-encoding of the resource path)
|
||||
|
||||
// we could use composedURLWithBase, but that doesn't handle
|
||||
// URLs in the format of CGI invocations correctly, and would not
|
||||
// be consistent with the behavior of the Python version of Munki
|
||||
// So instead we'll do simple string concatenation
|
||||
// (with percent-encoding of the resource path)
|
||||
let munkiBaseURL = pref("SoftwareRepoURL") as? String ?? ""
|
||||
if type.isEmpty {
|
||||
return munkiBaseURL
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// For a URL, return the path that the download should cache to.
|
||||
func getDownloadCachePath(_ urlString: String) -> String {
|
||||
/// For a URL, return the path that the download should cache to.
|
||||
///
|
||||
/// Returns a string.
|
||||
return managedInstallsDir(subpath: "Cache/" + baseName(urlString))
|
||||
}
|
||||
|
||||
/// Determine if there is enough disk space to download and install the installer item.
|
||||
func enoughDiskSpaceFor(
|
||||
_ item: PlistDict,
|
||||
installList: [PlistDict] = [],
|
||||
@@ -21,9 +20,6 @@ func enoughDiskSpaceFor(
|
||||
warn: Bool = true,
|
||||
precaching: Bool = false
|
||||
) -> Bool {
|
||||
/// Determine if there is enough disk space to download and install
|
||||
/// the installer item.
|
||||
|
||||
// fudgefactor is 100MB
|
||||
let fudgefactor = 102_400 // KBytes
|
||||
var alreadyDownloadedSize = 0
|
||||
@@ -78,17 +74,15 @@ func enoughDiskSpaceFor(
|
||||
return false
|
||||
}
|
||||
|
||||
/// Downloads an (un)installer item.
|
||||
/// Returns true if the item was downloaded, false if it was already cached.
|
||||
/// Thows an error if there are issues
|
||||
func downloadInstallerItem(
|
||||
_ item: PlistDict,
|
||||
installInfo: PlistDict,
|
||||
uninstalling: Bool = false,
|
||||
precaching: Bool = false
|
||||
) throws -> Bool {
|
||||
/// Downloads an (un)installer item.
|
||||
/// Returns true if the item was downloaded,
|
||||
/// false if it was already cached.
|
||||
/// Thows an error if there are issues
|
||||
|
||||
let downloadItemKey = if uninstalling, item.keys.contains("uninstaller_item_location") {
|
||||
"uninstaller_item_location"
|
||||
} else {
|
||||
@@ -171,9 +165,8 @@ func downloadInstallerItem(
|
||||
|
||||
let ICON_HASHES_PLIST_NAME = "_icon_hashes.plist"
|
||||
|
||||
/// Remove any cached/downloaded icons that aren't in the list of ones to keep
|
||||
func cleanUpIconsDir(keepList: [String] = []) {
|
||||
/// Remove any cached/downloaded icons that aren't in the list of ones
|
||||
/// to keep
|
||||
let itemsToKeep = keepList + [ICON_HASHES_PLIST_NAME]
|
||||
let iconsDir = managedInstallsDir(subpath: "icons")
|
||||
cleanUpDir(iconsDir, keeping: itemsToKeep)
|
||||
@@ -196,9 +189,8 @@ func getIconHashes() -> [String: String] {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to download icons (actually image files) for items in itemList
|
||||
func downloadIcons(_ itemList: [PlistDict]) {
|
||||
/// Attempts to download icons (actually image files) for items in
|
||||
/// itemList
|
||||
var iconsToKeep = [String]()
|
||||
let iconsDir = managedInstallsDir(subpath: "icons")
|
||||
let iconHashes = getIconHashes()
|
||||
@@ -258,14 +250,12 @@ func downloadIcons(_ itemList: [PlistDict]) {
|
||||
cleanUpIconsDir(keepList: iconsToKeep)
|
||||
}
|
||||
|
||||
/// Download client customization resources (if any).
|
||||
|
||||
/// Munki's preferences can specify an explicit name under ClientResourcesFilename
|
||||
/// if that doesn't exist, use the primary manifest name as the filename.
|
||||
/// If that fails, try site_default.zip
|
||||
func downloadClientResources() {
|
||||
/// Download client customization resources (if any).
|
||||
|
||||
/// Munki's preferences can specify an explicit name
|
||||
/// under ClientResourcesFilename
|
||||
/// if that doesn't exist, use the primary manifest name as the
|
||||
/// filename. If that fails, try site_default.zip
|
||||
|
||||
// build a list of resource names to request from the server
|
||||
var filenames = [String]()
|
||||
if let resourcesName = pref("ClientResourcesFilename") as? String {
|
||||
@@ -320,9 +310,8 @@ func downloadClientResources() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to download a catalog from the Munki server. Returns the path to the downloaded catalog file.
|
||||
func downloadCatalog(_ catalogName: String) -> String? {
|
||||
/// Attempt to download a catalog from the Munki server, Returns the path to
|
||||
/// the downloaded catalog file
|
||||
let catalogPath = managedInstallsDir(subpath: "catalogs/\(catalogName)")
|
||||
displayDetail("Getting catalog \(catalogName)...")
|
||||
let message = "Retrieving catalog \(catalogName)..."
|
||||
@@ -342,9 +331,9 @@ func downloadCatalog(_ catalogName: String) -> String? {
|
||||
|
||||
// TODO: precaching support (in progress)
|
||||
|
||||
/// Returns a list of items from InstallInfo.plist's optional_installs
|
||||
/// that have precache=true and (installed=false or needs_update=true)
|
||||
private func itemsToPrecache(_ installInfo: PlistDict) -> [PlistDict] {
|
||||
/// Returns a list of items from InstallInfo.plist's optional_installs
|
||||
/// that have precache=true and (installed=false or needs_update=true)
|
||||
func boolValueFor(_ item: PlistDict, key: String) -> Bool {
|
||||
return item[key] as? Bool ?? false
|
||||
}
|
||||
@@ -358,8 +347,8 @@ private func itemsToPrecache(_ installInfo: PlistDict) -> [PlistDict] {
|
||||
return [PlistDict]()
|
||||
}
|
||||
|
||||
/// Download any applicable precache items into our Cache folder
|
||||
func precache() {
|
||||
/// Download any applicable precache items into our Cache folder
|
||||
guard let installInfo = getInstallInfo() else {
|
||||
// nothing to do
|
||||
return
|
||||
@@ -378,8 +367,8 @@ func precache() {
|
||||
displayInfo("### Ending precaching session ###")
|
||||
}
|
||||
|
||||
/// Discard precached items to free up space for managed installs
|
||||
func uncache(_: Int) {
|
||||
/// Discard precached items to free up space for managed installs
|
||||
guard let installInfo = getInstallInfo() else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ extension ManifestError: LocalizedError {
|
||||
}
|
||||
}
|
||||
|
||||
/// a Singleton class to track manifest name -> local path
|
||||
class Manifests {
|
||||
/// a Singleton class to track manifest_name -> local path
|
||||
static let shared = Manifests()
|
||||
|
||||
var db: [String: String]
|
||||
@@ -62,14 +62,13 @@ class Manifests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a manifest from the server.
|
||||
///
|
||||
/// Returns:
|
||||
/// string local path to the downloaded manifest
|
||||
/// Throws:
|
||||
/// ManifestError
|
||||
func getManifest(_ name: String, suppressErrors: Bool = false) throws -> String {
|
||||
/// Gets a manifest from the server.
|
||||
///
|
||||
/// Returns:
|
||||
/// string local path to the downloaded manifest
|
||||
/// Throws:
|
||||
/// ManifestError
|
||||
|
||||
// have we already retrieved it this session?
|
||||
if let manifestLocalPath = Manifests.shared.get(name) {
|
||||
return manifestLocalPath
|
||||
@@ -123,9 +122,9 @@ func getManifest(_ name: String, suppressErrors: Bool = false) throws -> String
|
||||
return manifestLocalPath
|
||||
}
|
||||
|
||||
/// Gets the primary client manifest from the server.
|
||||
/// Can throw all the same errors as getManifest
|
||||
func getPrimaryManifest(alternateIdentifier: String? = nil) throws -> String {
|
||||
/// Gets the primary client manifest from the server.
|
||||
/// Can throw all the same errors as getManifest
|
||||
var clientIdentifier = ""
|
||||
if let alternateIdentifier, !alternateIdentifier.isEmpty {
|
||||
clientIdentifier = alternateIdentifier
|
||||
@@ -187,16 +186,16 @@ func getPrimaryManifest(alternateIdentifier: String? = nil) throws -> String {
|
||||
return manifest
|
||||
}
|
||||
|
||||
/// Removes any manifest files that are no longer in use by this client
|
||||
func cleanUpManifests() {
|
||||
/// Removes any manifest files that are no longer in use by this client
|
||||
let manifestDir = managedInstallsDir(subpath: "manifests")
|
||||
let exceptions = ["SelfServeManifest"]
|
||||
let keepList = Manifests.shared.list() + exceptions
|
||||
cleanUpDir(manifestDir, keeping: keepList)
|
||||
}
|
||||
|
||||
/// Reads a manifest file, returns a dictionary.
|
||||
func manifestData(_ path: String) -> PlistDict? {
|
||||
/// Reads a manifest file, returns a dictionary.
|
||||
if pathExists(path) {
|
||||
do {
|
||||
if let plist = try readPlist(fromFile: path) as? PlistDict {
|
||||
@@ -218,6 +217,7 @@ func manifestData(_ path: String) -> PlistDict? {
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Returns the value for key for a manifest
|
||||
func getManifestValue(_ path: String, forKey key: String) -> Any? {
|
||||
if let manifest = manifestData(path) {
|
||||
if let value = manifest[key] {
|
||||
@@ -229,9 +229,8 @@ func getManifestValue(_ path: String, forKey key: String) -> Any? {
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Remove the given itemname from the self-serve manifest's managed_uninstalls list
|
||||
func removeItemFromSelfServeSection(itemname: String, section: String) {
|
||||
/// Remove the given itemname from the self-serve manifest's
|
||||
/// managed_uninstalls list
|
||||
displayDebug1("Removing \(itemname) from SelfServeManifest's \(section)...")
|
||||
let manifestPath = managedInstallsDir(subpath: "manifests/SelfServeManifest")
|
||||
if !pathExists(manifestPath) {
|
||||
@@ -258,9 +257,8 @@ func removeItemFromSelfServeSection(itemname: String, section: String) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the given itemname from the self-serve manifest's managed_installs list
|
||||
func removeFromSelfServeInstalls(_ itemName: String) {
|
||||
/// Remove the given itemname from the self-serve manifest's
|
||||
/// managed_installs list
|
||||
removeItemFromSelfServeSection(itemname: itemName, section: "managed_installs")
|
||||
}
|
||||
|
||||
@@ -270,6 +268,13 @@ func removeFromSelfServeUninstalls(_ itemName: String) {
|
||||
removeItemFromSelfServeSection(itemname: itemName, section: "managed_uninstalls")
|
||||
}
|
||||
|
||||
/// Processes keys in manifests to build the lists of items to install and
|
||||
/// remove.
|
||||
///
|
||||
/// Can be recursive if manifests include other manifests.
|
||||
/// Probably doesn't handle circular manifest references well.
|
||||
///
|
||||
/// manifest can be a path to a manifest file or a dictionary object.
|
||||
func processManifest(
|
||||
_ manifestdata: PlistDict,
|
||||
forKey key: String,
|
||||
@@ -277,14 +282,6 @@ func processManifest(
|
||||
parentCatalogs: [String] = [],
|
||||
manifestName: String = "embedded manifest"
|
||||
) async throws {
|
||||
/// Processes keys in manifests to build the lists of items to install and
|
||||
/// remove.
|
||||
///
|
||||
/// Can be recursive if manifests include other manifests.
|
||||
/// Probably doesn't handle circular manifest references well.
|
||||
///
|
||||
/// manifest can be a path to a manifest file or a dictionary object.
|
||||
|
||||
let manifestCatalogs = manifestdata["catalogs"] as? [String] ?? []
|
||||
var catalogList = [String]()
|
||||
if !manifestCatalogs.isEmpty {
|
||||
@@ -391,13 +388,13 @@ func processManifest(
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a manifest _file_
|
||||
func processManifest(
|
||||
atPath manifestPath: String,
|
||||
forKey key: String,
|
||||
installInfo: inout PlistDict,
|
||||
parentCatalogs: [String] = []
|
||||
) async throws {
|
||||
/// processe a manifest _file_
|
||||
displayDebug1("** Processing manifest \(baseName(manifestPath)) for \(key)")
|
||||
if let manifestdata = manifestData(manifestPath) {
|
||||
try await processManifest(
|
||||
|
||||
Reference in New Issue
Block a user