Merge branch 'Munki7dev'

This commit is contained in:
Greg Neagle
2025-10-28 10:42:25 -07:00
10 changed files with 98 additions and 60 deletions

View File

@@ -422,10 +422,7 @@ struct ManagedSoftwareUpdate: AsyncParsableCommand {
_ = forceInstallPackageCheck() // this might mark some more items as unattended
// now install anything that can be done unattended
munkiLog("Installing only items marked unattended because SuppressLoginwindowInstall is true.")
_ = await doInstallTasks(
doAppleUpdates: appleUpdateCount > 0,
onlyUnattended: true
)
_ = await doInstallTasks(onlyUnattended: true)
return
}
if getIdleSeconds() < 10 {

View File

@@ -332,7 +332,7 @@ func doInstallTasks(doAppleUpdates: Bool = false, onlyUnattended: Bool = false)
}
var munkiItemsRestartAction = PostAction.none
var appleItemsRestartAction = PostAction.none
//var appleItemsRestartAction = PostAction.none
if munkiUpdatesAvailable() > 0 {
// install Munki updates
@@ -341,7 +341,7 @@ func doInstallTasks(doAppleUpdates: Bool = false, onlyUnattended: Bool = false)
if munkiUpdatesContainItemWithInstallerType("startosinstall") {
Report.shared.save()
// install macOS
// TODO: implement this (install macOS via startOSInstall)
// TODO: implement this (install macOS via startOSInstall) (will likely never implement)
}
}
}
@@ -353,7 +353,8 @@ func doInstallTasks(doAppleUpdates: Bool = false, onlyUnattended: Bool = false)
Report.shared.save()
return max(appleItemsRestartAction, munkiItemsRestartAction)
//return max(appleItemsRestartAction, munkiItemsRestartAction) // we no longer support installing Apple updates
return munkiItemsRestartAction
}
/// Handle the need for a forced logout. Start our logouthelper

View File

@@ -790,27 +790,10 @@
C0EEC9A62DA7335900F92942 /* clientcerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = clientcerts.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
C09616492DEA2C0900281B6B /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
tokenize.swift,
);
target = C07A6FB62C2B5ADE00090743 /* munkitester */;
};
C096164D2DEA2C0900281B6B /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
tokenize.swift,
);
target = C0684E5B2DC736EE0091E774 /* munkiCLItesting */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
C00519A22D2A5B850060DDB6 /* authrestartd */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = authrestartd; sourceTree = "<group>"; };
C0276DB22E8C070500D443C3 /* repocheck */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = repocheck; sourceTree = "<group>"; };
C05DB2032DAC53150081FACD /* manifestutil */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (C09616492DEA2C0900281B6B /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C096164D2DEA2C0900281B6B /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = manifestutil; sourceTree = "<group>"; };
C05DB2032DAC53150081FACD /* manifestutil */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = manifestutil; sourceTree = "<group>"; };
C0684DD22DBFDBD20091E774 /* precache_agent */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = precache_agent; sourceTree = "<group>"; };
C0684E2E2DC040450091E774 /* installhelper */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = installhelper; sourceTree = "<group>"; };
C0684E5D2DC736EE0091E774 /* munkiCLItesting */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = munkiCLItesting; sourceTree = "<group>"; };

View File

@@ -0,0 +1,43 @@
//
// fileUtilsTests.swift
// munki
//
// Created by Greg Neagle on 10/22/25.
//
// 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 Testing
struct fileUtilsTests {
@Test func pathIsDirectoryTests() throws {
// setup
let testDirectoryPath = try #require(TempDir.shared.path, "Can't get temp directory path")
try #require(
FileManager.default.createFile(
atPath: testDirectoryPath + "/test.txt", contents: nil, attributes: nil
) != false,
"Can't create test file"
)
try #require(
try? FileManager.default.createSymbolicLink(
atPath: testDirectoryPath + "/symlink",
withDestinationPath: testDirectoryPath
),
"Can't create test symlink"
)
#expect(pathIsDirectory(testDirectoryPath))
#expect(!pathIsDirectory(testDirectoryPath + "/test.txt"))
#expect(!pathIsDirectory(testDirectoryPath + "/symlink"))
#expect(pathIsDirectory(testDirectoryPath + "/symlink", followSymlinks: true))
}
}

View File

@@ -236,7 +236,10 @@ struct PackageInfoFileTests {
let unwrappedPkginfoPath = try #require(
pkginfoPath, "Failed to create temporary pkgInfo file"
)
let receipt = receiptFromPackageInfoFile(unwrappedPkginfoPath)
let receipt = try #require(
receiptFromPackageInfoFile(unwrappedPkginfoPath),
"Could not get receipt from pkginfo"
)
#expect((receipt["packageid"] as? String ?? "") == "com.googlecode.munki.core")
}
@@ -244,7 +247,10 @@ struct PackageInfoFileTests {
let unwrappedPkginfoPath = try #require(
pkginfoPath, "Failed to create temporary pkgInfo file"
)
let receipt = receiptFromPackageInfoFile(unwrappedPkginfoPath)
let receipt = try #require(
receiptFromPackageInfoFile(unwrappedPkginfoPath),
"Could not get receipt from pkginfo"
)
#expect((receipt["version"] as? String ?? "") == "7.0.0.5096")
}
@@ -252,7 +258,10 @@ struct PackageInfoFileTests {
let unwrappedPkginfoPath = try #require(
pkginfoPath, "Failed to create temporary pkgInfo file"
)
let receipt = receiptFromPackageInfoFile(unwrappedPkginfoPath)
let receipt = try #require(
receiptFromPackageInfoFile(unwrappedPkginfoPath),
"Could not get receipt from pkginfo"
)
#expect((receipt["installed_size"] as? Int ?? 0) == 39393)
}
}

View File

@@ -74,26 +74,24 @@ func createInstallsItem(_ itempath: String) -> PlistDict {
} else {
info["type"] = "bundle"
}
if let plist = getBundleInfo(itempath) {
for key in ["CFBundleName", "CFBundleIdentifier",
"CFBundleShortVersionString", "CFBundleVersion"]
{
if let value = plist[key] as? String {
info[key] = value
}
for key in ["CFBundleName", "CFBundleIdentifier",
"CFBundleShortVersionString", "CFBundleVersion"]
{
if let value = plist[key] as? String {
info[key] = value
}
if let minOSVers = plist["LSMinimumSystemVersion"] as? String {
info["minosversion"] = minOSVers
} else if let minOSVersByArch = plist["LSMinimumSystemVersionByArchitecture"] as? [String: String] {
// get the highest/latest of all the minimum os versions
let minOSVersions = minOSVersByArch.values
let versions = minOSVersions.map { MunkiVersion($0) }
if let maxVersion = versions.max() {
info["minosversion"] = maxVersion.value
}
} else if let minSysVers = plist["SystemVersionCheck:MinimumSystemVersion"] as? String {
info["minosversion"] = minSysVers
}
if let minOSVers = plist["LSMinimumSystemVersion"] as? String {
info["minosversion"] = minOSVers
} else if let minOSVersByArch = plist["LSMinimumSystemVersionByArchitecture"] as? [String: String] {
// get the highest/latest of all the minimum os versions
let minOSVersions = minOSVersByArch.values
let versions = minOSVersions.map { MunkiVersion($0) }
if let maxVersion = versions.max() {
info["minosversion"] = maxVersion.value
}
} else if let minSysVers = plist["SystemVersionCheck:MinimumSystemVersion"] as? String {
info["minosversion"] = minSysVers
}
} else if let plist = try? readPlist(fromFile: itempath) as? PlistDict {
// we must be a plist
@@ -115,6 +113,8 @@ func createInstallsItem(_ itempath: String) -> PlistDict {
} else {
info["version_comparison_key"] = "CFBundleShortVersionString"
}
} else if info["CFBundleVersion"] != nil {
info["version_comparison_key"] = "CFBundleVersion"
}
if !info.keys.contains("CFBundleShortVersionString"), !info.keys.contains("CFBundleVersion") {

View File

@@ -168,7 +168,7 @@ class FileRepo: Repo {
}
}
// does root dir exist now?
if !pathIsDirectory(root) {
if !pathIsDirectory(root, followSymlinks: true) {
throw MunkiError("Repo path does not exist")
}
}

View File

@@ -336,21 +336,19 @@ func getHTTPfileIfChangedAtomically(
resume: Bool = false,
followRedirects: String = "none",
) throws -> Bool {
var eTag = ""
var getOnlyIfNewer = false
if pathExists(destinationPath) {
getOnlyIfNewer = true
// see if we have an etag attribute
// see if we have a stored etag or last-modified header
do {
let data = try getXattr(named: GURL_XATTR, atPath: destinationPath)
if let headers = try readPlist(fromData: data) as? [String: String] {
eTag = headers["etag"] ?? ""
// We can use onlyIfNewer if we have either etag or last-modified
if headers["etag"] != nil || headers["last-modified"] != nil {
getOnlyIfNewer = true
}
}
} catch {
// fall through
}
if eTag.isEmpty {
getOnlyIfNewer = false
// fall through - no cached headers
}
}
var headers: [String: String]

View File

@@ -39,7 +39,7 @@ func pathIsRegularFile(_ path: String) -> Bool {
return false
}
/// Returns true if path is a symlink/
/// Returns true if path is a symlink
func pathIsSymlink(_ path: String) -> Bool {
if let fileType = fileType(path) {
return fileType == FileAttributeType.typeSymbolicLink.rawValue
@@ -47,10 +47,17 @@ func pathIsSymlink(_ path: String) -> Bool {
return false
}
/// Returns true if path is a directory/
func pathIsDirectory(_ path: String) -> Bool {
/// Returns true if path is a directory; follows symlinks if followSymlinks=true
func pathIsDirectory(_ path: String, followSymlinks: Bool = false) -> Bool {
if let fileType = fileType(path) {
return fileType == FileAttributeType.typeDirectory.rawValue
if fileType == FileAttributeType.typeDirectory.rawValue {
return true
}
if followSymlinks, fileType == FileAttributeType.typeSymbolicLink.rawValue {
if let target = try? FileManager.default.destinationOfSymbolicLink(atPath: path) {
return pathIsDirectory(target)
}
}
}
return false
}

View File

@@ -19,7 +19,7 @@
// limitations under the License.
/// one single place to define a version for CLI tools
let CLI_TOOLS_VERSION = "7.0.1"
let CLI_TOOLS_VERSION = "7.0.2"
let BUILD = "<BUILD_GOES_HERE>"
/// Returns version of Munki tools