Last active
April 14, 2023 08:35
-
-
Save csknns/c6cdb90e1e2a8afff066c3fc0ebe1cff to your computer and use it in GitHub Desktop.
Replaces referenced environment variables (e.g. $(BUILD_VERSION)) in a plist file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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