-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathsetup-env.swift
154 lines (122 loc) · 4.1 KB
/
setup-env.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import Foundation
// MARK: ConfiguratorError
/// The possible errors that `ProjectConfigurator` can throw.
public enum ConfiguratorError: LocalizedError {
/// The arguments provided to the configurator do not match the requirements
case invalidArguments
/// The source file providing the secret keys can not be found.
case keysFileNotFound
/// Could not read valid keys from the source file provided.
case emptyData
/// One of the keys in the source file was not found in the environment.
case keyMissing(String)
public var errorDescription: String? {
switch self {
case .invalidArguments:
return "The arguments provided to the configurator do not match the requirements"
case .keysFileNotFound:
return "The source file providing the secret keys can not be found."
case .emptyData:
return "Could not read valid keys from the source file provided."
case .keyMissing(let key):
return "The key \"\(key)\" is missing."
}
}
}
// MARK: EnvironmentConfigurator
/// This class configures environment variables for the project.
/// It takes a source file with the keys definition and inject the values
/// to the provided output file.
public final class EnvironmentConfigurator {
enum Defaults {
static let secretsFile = "secrets.xcconfig"
static let keysFile = "keys.env"
}
private let keysSource: String
private let fileManager = FileManager.default
public init(keysSource: String? = nil) {
self.keysSource = (keysSource ?? Defaults.keysFile).expandingTildeInPath
}
public func injectEnvironment(into file: String? = nil) throws {
let keys = try readKeys()
let fullPath = (file ?? Defaults.secretsFile).expandingTildeInPath
if !fileManager.fileExists(atPath: fullPath) {
fileManager.createFile(atPath: fullPath, contents: nil)
}
var rows: [String] = []
for key in keys {
do {
let value = try require(key: key)
rows.append("\(key) = \(value)")
} catch {
// Attempt to write the key-value pairs found before failure.
try rows.joined(separator: "\n")
.write(toFile: fullPath, atomically: true, encoding: .utf8)
throw error
}
}
try rows.joined(separator: "\n")
.write(toFile: fullPath, atomically: true, encoding: .utf8)
}
private func readKeys() throws -> [String] {
guard fileManager.fileExists(atPath: keysSource) else {
throw ConfiguratorError.keysFileNotFound
}
guard
let data = fileManager.contents(atPath: keysSource),
let content = String(data: data, encoding: .utf8)
else {
throw ConfiguratorError.emptyData
}
return content.split(separator: "\n")
.map { String($0) }
.filter { !$0.starts(with: "#") && !$0.isEmpty }
}
private func require(key: String) throws -> String {
let environment = ProcessInfo.processInfo.environment
guard let value = environment[key], !value.isEmpty else {
throw ConfiguratorError.keyMissing(key)
}
return value
}
}
// MARK: Script
private let arguments = CommandLine.arguments
private let executableName: String = arguments.first ?? "setup-env"
private func outputError(_ error: Error) {
fputs("Configurator failed: \(error.localizedDescription)\n", stderr)
}
private func showHelp() {
print("Usage:")
print("\(executableName) <keys_source> <output_xcconfig>")
}
guard arguments.count >= 1 else {
showHelp()
outputError(ConfiguratorError.invalidArguments)
exit(EXIT_FAILURE)
}
private let keysSource = arguments[safe: 1]
private let outputXCConfigFile = arguments[safe: 2]
private let configurator = EnvironmentConfigurator(keysSource: keysSource)
do {
try configurator.injectEnvironment(into: outputXCConfigFile)
print("Environment configured successfuly!")
exit(EXIT_SUCCESS)
} catch {
outputError(error)
exit(EXIT_FAILURE)
}
// MARK: Extensions
internal extension String {
var expandingTildeInPath: String {
NSString(string: self).expandingTildeInPath
}
}
extension Array {
subscript(safe index: Int) -> Element? {
guard index >= 0 && index < count else {
return nil
}
return self[index]
}
}