├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── variable-injector │ ├── Core │ ├── CommandLine │ │ └── Tool.swift │ ├── Extensions │ │ └── TokenSyntax+Extensions.swift │ ├── Logger.swift │ └── Rewriters │ │ └── EnvirionmentVariableLiteralRewriter.swift │ └── Tool │ └── main.swift ├── Tests └── variable-injectorTests │ └── VariableInjectorTests.swift └── scripts └── build-and-install.sh /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | jobs: 14 | Darwin: 15 | name: Darwin 16 | env: 17 | DEVELOPER_DIR: "/Applications/Xcode_15.2.app/Contents/Developer" 18 | runs-on: macos-14 19 | steps: 20 | - uses: actions/checkout@v1 21 | - name: Test 22 | run: | 23 | swift build 24 | swift test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/swift,xcode,macos,swiftpm,swiftpackagemanager 2 | # Edit at https://www.gitignore.io/?templates=swift,xcode,macos,swiftpm,swiftpackagemanager 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | 25 | # Directories potentially created on remote AFP share 26 | .AppleDB 27 | .AppleDesktop 28 | Network Trash Folder 29 | Temporary Items 30 | .apdisk 31 | 32 | ### Swift ### 33 | # Xcode 34 | # 35 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 36 | 37 | ## Build generated 38 | /.build 39 | build/ 40 | DerivedData/ 41 | 42 | ## Various settings 43 | *.pbxuser 44 | !default.pbxuser 45 | *.mode1v3 46 | !default.mode1v3 47 | *.mode2v3 48 | !default.mode2v3 49 | *.perspectivev3 50 | !default.perspectivev3 51 | xcuserdata/ 52 | 53 | ## Other 54 | *.moved-aside 55 | *.xccheckout 56 | *.xcscmblueprint 57 | 58 | ## Obj-C/Swift specific 59 | *.hmap 60 | *.ipa 61 | *.dSYM.zip 62 | *.dSYM 63 | 64 | ## Playgrounds 65 | timeline.xctimeline 66 | playground.xcworkspace 67 | 68 | # Swift Package Manager 69 | # 70 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 71 | # Packages/ 72 | # Package.pins 73 | # Package.resolved 74 | .build/ 75 | 76 | # CocoaPods 77 | # 78 | # We recommend against adding the Pods directory to your .gitignore. However 79 | # you should judge for yourself, the pros and cons are mentioned at: 80 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 81 | # 82 | # Pods/ 83 | # 84 | # Add this line if you want to avoid checking in source code from the Xcode workspace 85 | # *.xcworkspace 86 | 87 | # Carthage 88 | # 89 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 90 | # Carthage/Checkouts 91 | 92 | Carthage/Build 93 | 94 | # fastlane 95 | # 96 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 97 | # screenshots whenever they are needed. 98 | # For more information about the recommended setup visit: 99 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 100 | 101 | fastlane/report.xml 102 | fastlane/Preview.html 103 | fastlane/screenshots/**/*.png 104 | fastlane/test_output 105 | 106 | # Code Injection 107 | # 108 | # After new code Injection tools there's a generated folder /iOSInjectionProject 109 | # https://github.com/johnno1962/injectionforxcode 110 | 111 | iOSInjectionProject/ 112 | 113 | ### SwiftPackageManager ### 114 | Packages 115 | xcuserdata 116 | *.xcodeproj 117 | 118 | ### SwiftPM ### 119 | 120 | ### Xcode ### 121 | # Xcode 122 | # 123 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 124 | 125 | ## Build generated 126 | 127 | ## Various settings 128 | 129 | ## Other 130 | 131 | ## Obj-C/Swift specific 132 | 133 | ## Playgrounds 134 | 135 | # Swift Package Manager 136 | # 137 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 138 | # Packages/ 139 | # Package.pins 140 | # Package.resolved 141 | .swiftpm/ 142 | 143 | # CocoaPods 144 | # 145 | # We recommend against adding the Pods directory to your .gitignore. However 146 | # you should judge for yourself, the pros and cons are mentioned at: 147 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 148 | # 149 | # Pods/ 150 | # 151 | # Add this line if you want to avoid checking in source code from the Xcode workspace 152 | # *.xcworkspace 153 | 154 | # Carthage 155 | # 156 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 157 | # Carthage/Checkouts 158 | 159 | 160 | # fastlane 161 | # 162 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 163 | # screenshots whenever they are needed. 164 | # For more information about the recommended setup visit: 165 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 166 | 167 | 168 | # Code Injection 169 | # 170 | # After new code Injection tools there's a generated folder /iOSInjectionProject 171 | # https://github.com/johnno1962/injectionforxcode 172 | 173 | 174 | 175 | ### Xcode Patch ### 176 | *.xcodeproj/* 177 | !*.xcodeproj/project.pbxproj 178 | !*.xcodeproj/xcshareddata/ 179 | !*.xcworkspace/contents.xcworkspacedata 180 | /*.gcno 181 | **/xcshareddata/WorkspaceSettings.xcsettings 182 | 183 | ### Unix Swap Files ### 184 | *.swp 185 | 186 | # End of https://www.gitignore.io/api/swift,xcode,macos,swiftpm,swiftpackagemanager 187 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2020 Luciano Almeida 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | BIN_DIR=/usr/local/bin 3 | LIB_DIR=/usr/local/lib 4 | SWIFT_PATH=$(shell dirname `xcrun --find swift`) 5 | PLATFORM=$(shell uname -m) 6 | 7 | build: 8 | swift build -c release --disable-sandbox 9 | 10 | install: build 11 | install_name_tool -add_rpath $(LIB_DIR) .build/$(PLATFORM)-apple-macosx/release/variable-injector 12 | install ".build/$(PLATFORM)-apple-macosx/release/variable-injector" "$(BIN_DIR)" 13 | 14 | uninstall: 15 | rm -rf "$(BIN_DIR)/variable-injector" 16 | 17 | clean: 18 | rm -rf .build 19 | 20 | release: 21 | swift build -c release --triple arm64-apple-macosx 22 | swift build -c release --triple x86_64-apple-macosx10.15 23 | rm -f .build/arm64-apple-macosx/build.db 24 | rm -f .build/x86_64-apple-macosx/build.db 25 | cd .build && zip -r ./arm64-apple-macosx.zip ./arm64-apple-macosx 26 | cd .build && zip -r ./x86_64-apple-macosx.zip ./x86_64-apple-macosx 27 | .PHONY: build install uninstall clean release 28 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "swift-argument-parser", 6 | "repositoryURL": "https://github.com/apple/swift-argument-parser.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "c8ed701b513cf5177118a175d85fbbbcd707ab41", 10 | "version": "1.3.0" 11 | } 12 | }, 13 | { 14 | "package": "swift-syntax", 15 | "repositoryURL": "https://github.com/apple/swift-syntax.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "74203046135342e4a4a627476dd6caf8b28fe11b", 19 | "version": "509.0.0" 20 | } 21 | } 22 | ] 23 | }, 24 | "version": 1 25 | } 26 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "variable-injector", 8 | platforms: [ 9 | .macOS(.v10_15), 10 | .iOS(.v13), 11 | .tvOS(.v13), 12 | .watchOS(.v6), 13 | .macCatalyst(.v13), 14 | ], 15 | products: [ 16 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 17 | .executable( 18 | name: "variable-injector", 19 | targets: ["variable-injector"]), 20 | ], 21 | dependencies: [ 22 | .package(url: "https://github.com/apple/swift-syntax.git", exact: "509.0.0"), 23 | .package(url: "https://github.com/apple/swift-argument-parser.git", exact: "1.3.0") 24 | ], 25 | targets: [ 26 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 27 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 28 | .target( 29 | name: "variable-injector-core", 30 | dependencies: [ 31 | .product(name: "SwiftSyntax", package: "swift-syntax"), 32 | .product(name: "SwiftParser", package: "swift-syntax"), 33 | .product(name: "ArgumentParser", package: "swift-argument-parser") 34 | ], 35 | path: "Sources/variable-injector/Core"), 36 | .executableTarget( 37 | name: "variable-injector", 38 | dependencies: ["variable-injector-core"], 39 | path: "Sources/variable-injector/Tool"), 40 | .testTarget( 41 | name: "variable-injectorTests", 42 | dependencies: ["variable-injector-core"]), 43 | ] 44 | ) 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 2 | 3 | # :warning: Deprecated :warning: 4 | 5 | This tool is deprecated and will no longer be updated by the maintainer. 6 | 7 | 8 | # Swift Variable Injector 9 | 10 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) 11 | [![Build Status](https://github.com/LucianoPAlmeida/variable-injector/workflows/CI/badge.svg?branch=master)](https://github.com/LucianoPAlmeida/variable-injector/actions) 12 | [![Swift](https://img.shields.io/badge/Swift-5.9-orange.svg)](https://swift.org) 13 | [![Xcode](https://img.shields.io/badge/Xcode-15.0-blue.svg)](https://developer.apple.com/xcode) 14 | [![SPM](https://img.shields.io/badge/SPM-orange.svg)](https://swift.org/package-manager/) 15 | 16 | Variable injector is a very simple project with the goal of inject CI pipelines environment variables values into **Swift** code static values before compilation and Continuous deployment to a specific environment(Development, Testing or Production) where we can define the values injected for each build and deployment e.g. an API URL that the App uses and is different for each environment. Also, it allows us to not expose our production keys and values in statically in our code. 17 | 18 | The project uses [SwiftSyntax](https://github.com/apple/swift-syntax) to perform a reliable substitutions of static literal strings with the CI environment variables values. 19 | 20 | ## Installation 21 | 22 | ### Manual Installation 23 | 24 | Just clone the repo and run `make install` 25 | 26 | With that installed and on our `bin` folder, now we can use it. 27 | 28 | ## Usage 29 | 30 | For a detailed example of using this, check out the article [Continuous Integration Environment Variables in iOS projects using Swift](https://medium.com/@lucianoalmeida1/continuous-integration-environment-variables-in-ios-projects-using-swift-f72e50176a91) on [Medium](https://medium.com). 31 | 32 | **Note** 33 | If you having issues with XCode 11 use the version 0.3.3 of the tool. 34 | 35 | You should have a `class` or `struct` with your environment variables declaration following the $(VAR_NAME) pattern. 36 | Example: 37 | 38 | ```swift 39 | struct CI { 40 | static var serviceAPIKey: String = "$(SERVICE_PROD_KEY)" 41 | static var otherAPIKey: String = "$(OTHER_PROD_KEY)" 42 | } 43 | ``` 44 | 45 | With the environments static declarations matching the pattern 46 | 47 | ```sh 48 | variable-injector --file ${SRCROOT}/Environment/CI.swift 49 | 50 | ``` 51 | 52 | If environment variables with those names, as in the example you have the `SERVICE_PROD_KEY` and `OTHER_PROD_KEY` are defined on the build machine for this pipeline the injector you replace the string literal with the environment variable value. 53 | 54 | Example of the file after the substitution. 55 | 56 | ```swift 57 | struct CI { 58 | static var serviceAPIKey: String = "h344hjk2h4j3h24jk32h43j2k4h32jk4hkj324h" 59 | static var otherAPIKey: String = "dsa76d7adas7d6as87d6as78d6aklre423s7d6as8d7s6" 60 | } 61 | ``` 62 | 63 | ### Using as Run Script in Build Phases 64 | 65 | You can add the script call for variable replacement on your build phases. We just have setup our Development keys in our local machine as environment variables and build the project. 66 | 67 | **Important:** Is very important to add this Run Script phase before the Compile Sources phase. So the variables will be replaced and then compiled :)) 68 | 69 | ```sh 70 | if which variable-injector >/dev/null; then 71 | variable-injector --file ${SRCROOT}/YourProject/YourGroupFolderPath/File.swift --verbose # Pass your parameters 72 | else 73 | echo "Warning: Swift Variable Injector not installed, download from https://github.com/LucianoPAlmeida/variable-injector" 74 | fi 75 | ``` 76 | 77 | ### Options 78 | 79 | We can ignore patterns that match $(ENV_VAR) to avoid the replace. 80 | 81 | ```sh 82 | variable-injector --file ${SRCROOT}/Environment/CI.swift --ignore OTHER_PROD_KEY 83 | 84 | ``` 85 | 86 | And also, to see the logs of variables, values and source output you can use `--verbose` 87 | 88 | **IMPORTANT** 89 | The verbose mode you print the values of your environment variables on the logs. So you may be careful and use it only for debug proposes. 90 | 91 | ```sh 92 | variable-injector --file ${SRCROOT}/Environment/CI.swift --verbose 93 | 94 | ``` 95 | 96 | After that we can just proceed to the build, archive and other steps of our CI/CD pipeline. 97 | 98 | ## Licence 99 | 100 | Variable Injector is released under the [MIT License](https://opensource.org/licenses/MIT). 101 | -------------------------------------------------------------------------------- /Sources/variable-injector/Core/CommandLine/Tool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArgumentsHandler.swift 3 | // VariableInjector 4 | // 5 | // Created by Luciano Almeida on 03/11/18. 6 | // 7 | 8 | import Foundation 9 | import SwiftSyntax 10 | import SwiftParser 11 | import ArgumentParser 12 | 13 | public struct VariableInjectorTool: ParsableCommand { 14 | 15 | public static let configuration = CommandConfiguration(abstract: 16 | "Variable injector is a very simple project with the goal of inject CI" + 17 | "pipelines environment variables values into Swift code static members before compilation") 18 | 19 | @Flag(name: [.customLong("verbose"), .customShort("v")], 20 | help: "Indicates if the tool will run in verbose mode or not") 21 | public var isVerbose: Bool = false 22 | 23 | @Option(name: [.customLong("file"), .customShort("f")], 24 | help: "The path(s) to the file(s) where the tool should run.") 25 | public var files: [String] = [] 26 | 27 | @Option(name: [.customLong("ignore"), .customShort("i")], 28 | help: "The names of variables that will be ignored even if that matches $(ENV_VAR) to avoid the replacing") 29 | public var varLiteralsToIgnore: [String] = [] 30 | 31 | public init() {} 32 | 33 | public mutating func validate() throws { 34 | if files.isEmpty { 35 | throw ValidationError("The path to at least one file where the variables will be injected should be provided. Use --file $path-to-file") 36 | } 37 | } 38 | 39 | public func run() throws { 40 | //Separator 41 | let printSeparator = String(repeating: "=", count: 70) 42 | // Logger 43 | let logger = isVerbose ? Logger() : nil 44 | 45 | // Loading files 46 | for file in files { 47 | let url = URL(fileURLWithPath: file) 48 | 49 | guard FileManager.default.fileExists(atPath: file) else { 50 | logger?.log(message: "File not found. Skipping: \(url)") 51 | continue; 52 | } 53 | 54 | logger?.log(message: "\(printSeparator)\n") 55 | logger?.log(message: "FILE: \(url.lastPathComponent)") 56 | logger?.log(message: "\(printSeparator)\n") 57 | 58 | var contentsFile: String = "" 59 | do { 60 | contentsFile = try String(contentsOf: url) 61 | } catch { 62 | logger?.log(message: "Could not get content of \(url). Error: \(error)") 63 | continue 64 | } 65 | let sourceFile = Parser.parse(source: contentsFile) 66 | 67 | let envVarRewriter = EnvironmentVariableLiteralRewriter( 68 | environment: ProcessInfo.processInfo.environment, 69 | ignoredLiteralValues: varLiteralsToIgnore, 70 | logger: logger 71 | ) 72 | let result = envVarRewriter.visit(sourceFile) 73 | 74 | var contents: String = "" 75 | result.write(to: &contents) 76 | 77 | try contents.write(to: url, atomically: true, encoding: .utf8) 78 | 79 | logger?.log(message: "\(printSeparator)\n") 80 | logger?.log(message: contents) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/variable-injector/Core/Extensions/TokenSyntax+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Extensions.swift 3 | // VariableInjector 4 | // 5 | // Created by Luciano Almeida on 06/12/19. 6 | // 7 | 8 | import SwiftSyntax 9 | 10 | extension TokenSyntax { 11 | var stringLiteral: String? { 12 | switch tokenKind { 13 | case .stringSegment(let text): 14 | return text 15 | default: 16 | return nil 17 | } 18 | } 19 | 20 | func byReplacingStringLiteral(string: String) -> TokenSyntax { 21 | switch tokenKind { 22 | case .stringSegment: 23 | return .stringSegment("\(string)") 24 | default: 25 | return self 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/variable-injector/Core/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.swift 3 | // variable-injector-core 4 | // 5 | // Created by Luciano Almeida on 04/11/18. 6 | // 7 | 8 | public class Logger { 9 | public init() {} 10 | 11 | public func log(message: @autoclosure () -> Any) { 12 | print(message()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/variable-injector/Core/Rewriters/EnvirionmentVariableLiteralRewriter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnvironmentVariableLiteralRewriter.swift 3 | // VariableInjector 4 | // 5 | // Created by Luciano Almeida on 02/11/18. 6 | // 7 | 8 | import Foundation 9 | import SwiftSyntax 10 | 11 | private let stringLiteralEnvVarPattern: String = "\"*\\$\\((\\w+)\\)\"*" 12 | 13 | public class EnvironmentVariableLiteralRewriter: SyntaxRewriter { 14 | public var ignoredLiteralValues: Set = [] 15 | private var environment: [String: String] = [:] 16 | public var logger: Logger? 17 | 18 | public init(environment: [String: String]) { 19 | self.environment = environment 20 | } 21 | 22 | public convenience init( 23 | environment: [String: String], 24 | ignoredLiteralValues: [String], 25 | logger: Logger? = nil) { 26 | self.init(environment: environment) 27 | self.ignoredLiteralValues = Set(ignoredLiteralValues) 28 | self.logger = logger 29 | } 30 | 31 | override public func visit(_ token: TokenSyntax) -> TokenSyntax { 32 | // Matching ENV var pattern e.g. $(ENV_VAR) 33 | guard matchesLiteralPattern(token) else { return token } 34 | 35 | guard let text = token.stringLiteral else { return token } 36 | 37 | let envVar = extractTextEnvVariableName(text) 38 | 39 | guard shouldPerformSubstitution(for: envVar), let envValue = environment[envVar] else { 40 | return token 41 | } 42 | 43 | logger?.log(message: "Injecting ENV_VAR: \(text), value: \(envValue)") 44 | return token.byReplacingStringLiteral(string: envValue) 45 | } 46 | 47 | private func shouldPerformSubstitution(for text: String) -> Bool { 48 | return !ignoredLiteralValues.contains(text) 49 | } 50 | 51 | private func extractTextEnvVariableName(_ text: String) -> String { 52 | let regex = try? NSRegularExpression(pattern: stringLiteralEnvVarPattern, options: .caseInsensitive) 53 | let matches = regex?.matches(in: text, options: .anchored, range: NSRange(location: 0, length: text.count)) 54 | 55 | guard let match = matches?.first else { return "" } 56 | 57 | let range = match.range(at: 1) 58 | let start = text.index(text.startIndex, offsetBy: range.location) 59 | let end = text.index(start, offsetBy: range.length) 60 | 61 | return String(text[start.. Bool { 68 | switch token.tokenKind { 69 | case .stringSegment(let text): 70 | return text.range(of: stringLiteralEnvVarPattern, 71 | options: .regularExpression, 72 | range: nil, 73 | locale: nil) != nil 74 | default: 75 | return false 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/variable-injector/Tool/main.swift: -------------------------------------------------------------------------------- 1 | import SwiftSyntax 2 | import Foundation 3 | import variable_injector_core 4 | 5 | VariableInjectorTool.main() 6 | -------------------------------------------------------------------------------- /Tests/variable-injectorTests/VariableInjectorTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftSyntax 3 | import SwiftParser 4 | import variable_injector_core 5 | 6 | final class VariableInjectorTests: XCTestCase { 7 | 8 | let testSource = """ 9 | import Foundation 10 | 11 | class Environment { 12 | static var serverURL: String = "$(SERVER_URL)" 13 | var apiVersion: String = "$(API_VERSION)" 14 | var notReplaceIt: String = "Do not replace it" 15 | } 16 | """ 17 | 18 | func testVariableSubstitution() throws { 19 | let stubEnvironment = ["SERVER_URL": "http://ci.injected.server.url.com"] 20 | let sourceFile = Parser.parse(source: testSource) 21 | 22 | let envVarRewriter = EnvironmentVariableLiteralRewriter(environment: stubEnvironment) 23 | let result = envVarRewriter.visit(sourceFile) 24 | 25 | var contents: String = "" 26 | result.write(to: &contents) 27 | 28 | let expected = testSource.replacingOccurrences(of: "$(SERVER_URL)", with: "http://ci.injected.server.url.com") 29 | 30 | 31 | XCTAssertEqual(expected, contents) 32 | } 33 | 34 | func testVariableSubstitutionWithExclusion() throws { 35 | let stubEnvironment = ["SERVER_URL": "http://ci.injected.server.url.com", "API_VERSION": "v1"] 36 | let sourceFile = Parser.parse(source: testSource) 37 | 38 | let envVarRewriter = EnvironmentVariableLiteralRewriter(environment: stubEnvironment, ignoredLiteralValues: ["SERVER_URL"]) 39 | let result = envVarRewriter.visit(sourceFile) 40 | 41 | var contents: String = "" 42 | result.write(to: &contents) 43 | 44 | let expected = testSource.replacingOccurrences(of: "$(API_VERSION)", with: "v1") 45 | 46 | XCTAssertEqual(expected, contents) 47 | } 48 | 49 | static var allTests = [ 50 | ("testVariableSubstitution", testVariableSubstitution), 51 | ("testVariableSubstitutionWithExclusion", testVariableSubstitutionWithExclusion), 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /scripts/build-and-install.sh: -------------------------------------------------------------------------------- 1 | CURRENT_DIR="$PWD" 2 | cd /tmp 3 | 4 | # Clone, build from source and install 5 | git clone https://github.com/LucianoPAlmeida/variable-injector.git 6 | cd variable-injector 7 | 8 | if [[ ! $TAG ]]; then 9 | TAG=$(git describe --tags --abbrev=0) 10 | fi; 11 | 12 | git checkout $TAG 13 | 14 | make install 15 | 16 | # Clean all 17 | cd .. 18 | rm -rf variable-injector 19 | 20 | cd "$CURRENT_DIR" 21 | --------------------------------------------------------------------------------