Skip to content

Instantly share code, notes, and snippets.

@csknns
Last active April 14, 2023 08:35
Show Gist options
  • Save csknns/c6cdb90e1e2a8afff066c3fc0ebe1cff to your computer and use it in GitHub Desktop.
Save csknns/c6cdb90e1e2a8afff066c3fc0ebe1cff to your computer and use it in GitHub Desktop.
Replaces referenced environment variables (e.g. $(BUILD_VERSION)) in a plist file
// Usage:
// $/usr/bin/xcrun --sdk macosx swift replacePlistEnvironmentVariables.swift "$inputfile.plist" "$outputfile"
// This will replace all the referenced enviroment variables with their value, for example:
// <string>$(A_VARIABLE)</string>
import Foundation
enum ScriptError: Error {
case invalidPlistArgument(argument: URL)
case sameInputAndOutptFile
}
let inputPlistURL = URL(
fileURLWithPath: CommandLine.arguments[1]
)
let outputPlistURL = URL(
fileURLWithPath: CommandLine.arguments[2]
)
try [inputPlistURL,
outputPlistURL
].forEach { url in
guard url.pathExtension == "plist" else { throw ScriptError.invalidPlistArgument(argument: url) }
}
guard inputPlistURL != outputPlistURL else { throw ScriptError.sameInputAndOutptFile }
let plist = try PropertyListSerialization.propertyList(
from: try Data(contentsOf: inputPlistURL),
format: nil
)
func replacePlistEnvironmentVariables(_ plist: Any) throws -> Any {
switch plist {
case let dictionary as [String: Any]:
return try dictionary.mapValues(replacePlistEnvironmentVariables)
case let array as [Any]:
return try array.map(replacePlistEnvironmentVariables)
case let string as String:
return try NSRegularExpression(
pattern: "\\$\\((\\w+)\\)"
).matches(
in: string,
range: NSRange(
string.startIndex..<string.endIndex,
in: string
)
).reversed().reduce(into: string) { result, match in
let replaceRange = Range(
match.range(at: 0),
in: string
)!
let variableRange = Range(
match.range(at: 1),
in: string
)!
let variable = String(string[variableRange])
if let value = ProcessInfo.processInfo.environment[variable] {
let entities = ["\0", "\t", "\n", "\r", "\"", "\'", "\\"]
var current = value
for entity in entities {
let descriptionCharacters = entity.debugDescription.dropFirst().dropLast()
let description = String(descriptionCharacters)
current = current.replacingOccurrences(of: description, with: entity)
}
result.replaceSubrange(
replaceRange,
with: current
)
}
}
default: return plist
}
}
let resultPlist = try replacePlistEnvironmentVariables(plist)
try PropertyListSerialization.data(
fromPropertyList: resultPlist,
format: .xml,
options: .zero
).write(to: outputPlistURL)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment