mirror of
https://github.com/munki/munki.git
synced 2026-01-18 05:00:19 -06:00
manifestutil: implement more subcommands
This commit is contained in:
@@ -124,7 +124,7 @@ extension ManifestUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints contents of a given manifest, expanding inlcuded maniifests
|
||||
/// Prints contents of a given manifest, expanding included maniifests
|
||||
extension ManifestUtil {
|
||||
struct ExpandIncludedManifests: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
import ArgumentParser
|
||||
import Foundation
|
||||
|
||||
func getManifestNames(repo: Repo) throws -> [String] {
|
||||
func getManifestNames(repo: Repo) -> [String]? {
|
||||
do {
|
||||
let manifestNames = try repo.list("manifests")
|
||||
return manifestNames.sorted()
|
||||
} catch {
|
||||
printStderr("Could not retrieve manifests: \(error.localizedDescription)")
|
||||
throw ExitCode(-1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ extension ManifestUtil {
|
||||
|
||||
func run() throws {
|
||||
let repo = try connectToRepo()
|
||||
let manifestNames = try getManifestNames(repo: repo)
|
||||
guard let manifestNames = getManifestNames(repo: repo) else {
|
||||
return
|
||||
}
|
||||
if globString.isEmpty {
|
||||
print(manifestNames.joined(separator: "\n"))
|
||||
} else {
|
||||
|
||||
192
code/cli/munki/manifestutil/MUmanifestFileOperations.swift
Normal file
192
code/cli/munki/manifestutil/MUmanifestFileOperations.swift
Normal file
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// MUmanifestFileOperations.swift
|
||||
// manifestutil
|
||||
//
|
||||
// Created by Greg Neagle on 4/14/25.
|
||||
//
|
||||
// Copyright 2024-2025 Greg Neagle.
|
||||
//
|
||||
// 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
|
||||
|
||||
/// Saves a manifest
|
||||
func saveManifest(repo: Repo,
|
||||
manifest: PlistDict,
|
||||
name: String,
|
||||
overwrite: Bool = false) -> Bool
|
||||
{
|
||||
if !overwrite {
|
||||
let existingManifestNames = getManifestNames(repo: repo) ?? []
|
||||
if existingManifestNames.contains(name) {
|
||||
printStderr("Manifest '\(name)' already exists")
|
||||
return false
|
||||
}
|
||||
}
|
||||
do {
|
||||
let data = try plistToData(manifest)
|
||||
try repo.put("manifests/\(name)", content: data)
|
||||
return true
|
||||
} catch {
|
||||
printStderr("Saving \(name) failed: \(error.localizedDescription)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies or renames a manifest.
|
||||
/// (To rename we make a copy under the new name, then delete the original)
|
||||
func copyOrRenameManifest(repo: Repo, sourceName: String, destinationName: String, overwrite: Bool = false, deleteSource: Bool = false) -> Bool {
|
||||
if !overwrite {
|
||||
let existingManifestNames = getManifestNames(repo: repo) ?? []
|
||||
if existingManifestNames.contains(destinationName) {
|
||||
printStderr("Manifest '\(destinationName)' already exists")
|
||||
return false
|
||||
}
|
||||
}
|
||||
do {
|
||||
let data = try repo.get("manifests/\(sourceName)")
|
||||
try repo.put("manifests/\(destinationName)", content: data)
|
||||
if deleteSource {
|
||||
try repo.delete("manifests/\(sourceName)")
|
||||
}
|
||||
return true
|
||||
} catch {
|
||||
printStderr("Renaming \(sourceName) to \(destinationName) failed: \(error.localizedDescription)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new, empty manifest
|
||||
func newManifest(repo: Repo, name: String) -> Bool {
|
||||
let manifest = [
|
||||
"catalogs": [String](),
|
||||
"included_manifests": [String](),
|
||||
"managed_installs": [String](),
|
||||
"managed_uninstalls": [String](),
|
||||
]
|
||||
return saveManifest(repo: repo, manifest: manifest, name: name)
|
||||
}
|
||||
|
||||
/// Deletes a manifest
|
||||
func deleteManifest(repo: Repo, name: String) -> Bool {
|
||||
let existingManifestNames = getManifestNames(repo: repo) ?? []
|
||||
if !existingManifestNames.contains(name) {
|
||||
printStderr("No such manifest: \(name)")
|
||||
return false
|
||||
}
|
||||
do {
|
||||
try repo.delete("manifests/\(name)")
|
||||
return true
|
||||
} catch {
|
||||
printStderr("Deleting \(name) failed: \(error.localizedDescription)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new empty manifest
|
||||
extension ManifestUtil {
|
||||
struct NewManifest: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(
|
||||
abstract: "Creates a new empty manifest")
|
||||
|
||||
@Argument(help: ArgumentHelp(
|
||||
"Name for the newly-created manifest",
|
||||
valueName: "manifest-name"
|
||||
))
|
||||
var manifestName: String
|
||||
|
||||
func run() throws {
|
||||
let repo = try connectToRepo()
|
||||
_ = newManifest(repo: repo, name: manifestName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy a manifest
|
||||
extension ManifestUtil {
|
||||
struct CopyManifest: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(
|
||||
abstract: "Copies a manifest")
|
||||
|
||||
@Argument(help: ArgumentHelp(
|
||||
"Name of the source manifest",
|
||||
valueName: "source-name"
|
||||
))
|
||||
var sourceName: String
|
||||
|
||||
@Argument(help: ArgumentHelp(
|
||||
"Name of the destination manifest",
|
||||
valueName: "destination-name"
|
||||
))
|
||||
var destinationName: String
|
||||
|
||||
func run() throws {
|
||||
let repo = try connectToRepo()
|
||||
_ = copyOrRenameManifest(
|
||||
repo: repo,
|
||||
sourceName: sourceName,
|
||||
destinationName: destinationName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename a manifest
|
||||
extension ManifestUtil {
|
||||
struct RenameManifest: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(
|
||||
abstract: "Renames a manifest")
|
||||
|
||||
@Argument(help: ArgumentHelp(
|
||||
"Name of the source manifest",
|
||||
valueName: "source-name"
|
||||
))
|
||||
var sourceName: String
|
||||
|
||||
@Argument(help: ArgumentHelp(
|
||||
"Name of the destination manifest",
|
||||
valueName: "destination-name"
|
||||
))
|
||||
var destinationName: String
|
||||
|
||||
func run() throws {
|
||||
let repo = try connectToRepo()
|
||||
_ = copyOrRenameManifest(
|
||||
repo: repo,
|
||||
sourceName: sourceName,
|
||||
destinationName: destinationName,
|
||||
deleteSource: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a manifest
|
||||
extension ManifestUtil {
|
||||
struct DeleteManifest: ParsableCommand {
|
||||
static var configuration = CommandConfiguration(
|
||||
abstract: "Deletes a manifest")
|
||||
|
||||
@Argument(help: ArgumentHelp(
|
||||
"Name of the manifest to be deleted",
|
||||
valueName: "manifest-name"
|
||||
))
|
||||
var manifestName: String
|
||||
|
||||
func run() throws {
|
||||
let repo = try connectToRepo()
|
||||
_ = deleteManifest(repo: repo, name: manifestName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,10 +60,10 @@ struct ManifestUtil: AsyncParsableCommand {
|
||||
DisplayManifest.self,
|
||||
ExpandIncludedManifests.self,
|
||||
// Find.self,
|
||||
// NewManifest.self,
|
||||
// CopyManifest.self,
|
||||
// RenameManifest.self,
|
||||
// DeleteManifest.self,
|
||||
NewManifest.self,
|
||||
CopyManifest.self,
|
||||
RenameManifest.self,
|
||||
DeleteManifest.self,
|
||||
// RefreshCache.self,
|
||||
Exit.self,
|
||||
Help.self,
|
||||
|
||||
Reference in New Issue
Block a user