├── .github
└── workflows
│ └── swift.yml
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── .version
├── LICENSE
├── Makefile
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
├── PackageBuilder
│ ├── PackageBuilder.swift
│ ├── PackageManifestBuilder.swift
│ ├── Shell.swift
│ ├── TemplateFilesManager.swift
│ ├── Templates
│ │ ├── dslOnlyPackageTemplate.swift
│ │ ├── mainTemplate.swift
│ │ └── packageTemplate.swift
│ └── TemporalPathBuilder.swift
├── PodfileBuilder
│ ├── PodfileBuilder.swift
│ └── podfileTemplate.swift
├── Storage
│ └── FileSystem.swift
├── SwiftyPods
│ ├── Commands
│ │ ├── CreateCommand.swift
│ │ ├── EditCommand.swift
│ │ └── GenerateCommand.swift
│ ├── Services
│ │ ├── CreateService.swift
│ │ ├── EditService.swift
│ │ └── GenerateService.swift
│ ├── TemplateArgumentParser.swift
│ ├── TemplateLocator.swift
│ ├── emtyPodfileTemplate.swift
│ └── main.swift
└── TemplateRenderer
│ └── TemplateRenderer.swift
├── Tests
├── LinuxMain.swift
├── PackageBuilderTests
│ ├── PackageBuilderTests.swift
│ ├── PackageManifestBuilderTests.swift
│ ├── TemplateFilesManagerTests.swift
│ └── XCTestManifests.swift
├── PodfileBuilderTests
│ ├── PodfileBuilderTests.swift
│ └── XCTestManifests.swift
├── SwiftyPodsTests
│ ├── SwiftyPodsTests.swift
│ └── XCTestManifests.swift
└── TemplateRendererTests
│ ├── TemplateRendererTests.swift
│ └── XCTestManifests.swift
└── testfolder
└── podfile.swift
/.github/workflows/swift.yml:
--------------------------------------------------------------------------------
1 | name: Swift
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | SPM:
11 | name: "Tests via SPM"
12 | runs-on: macOS-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Run tests
17 | run: set -o pipefail && swift test
18 |
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 | .DS_Store
92 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.version:
--------------------------------------------------------------------------------
1 | 0.2.0
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 David
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PREFIX?=/usr/local
2 |
3 | build:
4 | swift build -c release
5 |
6 | clean_build:
7 | rm -rf .build
8 | make build
9 |
10 | install: build
11 | mkdir -p "$(PREFIX)/bin"
12 | cp -f ".build/release/swiftypods" "$(PREFIX)/bin/swiftypods"
13 |
14 | get_version:
15 | @cat .version
16 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "swift-argument-parser",
6 | "repositoryURL": "https://github.com/apple/swift-argument-parser",
7 | "state": {
8 | "branch": null,
9 | "revision": "8d31a0905c346a45c87773ad50862b5b3df8dff6",
10 | "version": "0.0.4"
11 | }
12 | },
13 | {
14 | "package": "SwiftShell",
15 | "repositoryURL": "https://github.com/kareman/SwiftShell",
16 | "state": {
17 | "branch": null,
18 | "revision": "fb7fc2c9ad8811caf324431a508fb79e3fb74f99",
19 | "version": "5.0.1"
20 | }
21 | },
22 | {
23 | "package": "SwiftyPodsDSL",
24 | "repositoryURL": "https://github.com/bitomule/SwiftyPodsDSL",
25 | "state": {
26 | "branch": null,
27 | "revision": "dcedec1ca16d041fc072b46925c7b25f7db5e323",
28 | "version": "0.2.0"
29 | }
30 | }
31 | ]
32 | },
33 | "version": 1
34 | }
35 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "SwiftyPods",
7 | products: [
8 | .executable(name: "swiftypods", targets: ["SwiftyPods"]),
9 | .library(name: "PodfileBuilder", targets: ["PodfileBuilder"]),
10 | ],
11 | dependencies: [
12 | .package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1"),
13 | .package(url: "https://github.com/kareman/SwiftShell", from: "5.0.1"),
14 | .package(url: "https://github.com/bitomule/SwiftyPodsDSL", from: "0.2.0")
15 | ],
16 | targets: [
17 | .target(
18 | name: "SwiftyPods",
19 | dependencies: [
20 | .product(name: "ArgumentParser", package: "swift-argument-parser"),
21 | "SwiftyPodsDSL",
22 | "PackageBuilder",
23 | "TemplateRenderer",
24 | "Storage",
25 | "SwiftShell"
26 | ]),
27 | .target(
28 | name: "PackageBuilder",
29 | dependencies: [
30 | "TemplateRenderer",
31 | "Storage",
32 | "SwiftShell"
33 | ]),
34 | .target(
35 | name: "TemplateRenderer",
36 | dependencies: [
37 | "Storage"
38 | ]),
39 | .target(
40 | name: "Storage",
41 | dependencies: []
42 | ),
43 | .target(
44 | name: "PodfileBuilder",
45 | dependencies: [
46 | "Storage",
47 | "SwiftShell",
48 | "TemplateRenderer",
49 | "SwiftyPodsDSL"
50 | ]
51 | ),
52 | .testTarget(
53 | name: "TemplateRendererTests",
54 | dependencies: [
55 | "TemplateRenderer",
56 | "Storage"
57 | ]),
58 | .testTarget(
59 | name: "PackageBuilderTests",
60 | dependencies: [
61 | "TemplateRenderer",
62 | "Storage",
63 | "PackageBuilder"
64 | ]),
65 | .testTarget(
66 | name: "PodfileBuilderTests",
67 | dependencies: [
68 | "TemplateRenderer",
69 | "Storage",
70 | "PodfileBuilder"
71 | ]),
72 | .testTarget(
73 | name: "SwiftyPodsTests",
74 | dependencies: [
75 | "SwiftyPods"
76 | ]),
77 | ]
78 | )
79 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # SwiftyPods
14 |
15 | SwiftyPods is a command-line tool that allows you to generate your CocoaPods podfile using Swift. It uses [SwiftyPodsDSL](https://github.com/bitomule/SwiftyPodsDSL) as syntax.
16 |
17 | ## Motivation
18 |
19 | Reading and editing the podfile becomes impossible when your project based on CocoaPods grows. You start grouping using variables and comments but it's still a long file where the safety is the error you get when you run pod install.
20 |
21 | SwiftyPods enables two big features:
22 | * **Safety**: Swift will type check your file before you finish editing it.
23 | * **Modularization of your podfile**: You can split your podfile in multiple podfile.swift files. You choose: feature pods, module pods, team pods... Once you generate your podfile they will all get merged into a single, classic podfile.
24 |
25 | ### This is what your podfile.swift will look like
26 |
27 | ```swift
28 | let podfile = Podfile(
29 | targets: [
30 | .target(
31 | name: "Target",
32 | project: "Project",
33 | dependencies: [
34 | .dependency(name: "Dependency1"),
35 | .dependency(name: "Dependency2",
36 | version: "1.2.3"),
37 | .dependency(name: "Dependency3",
38 | .git(url: "repo"),
39 | .branch(name: "master"))
40 | ],
41 | childTargets: [
42 | .target(name: "ChildTarget", project: "Project2")
43 | ]
44 | )
45 | ]
46 | )
47 | ```
48 |
49 | ## Installing
50 |
51 | ### Using [Homebrew](http://brew.sh/)
52 |
53 | ```sh
54 | $ brew install bitomule/homebrew-tap/swiftypods
55 | ```
56 |
57 | ### Using [Mint](https://github.com/yonaskolb/mint)
58 |
59 | ```sh
60 | $ mint install bitomule/swiftypods
61 | ```
62 | ### Compiling from source
63 |
64 | ```sh
65 | $ git clone https://github.com/bitomule/SwiftyPods.git
66 | $ cd SwiftyPods
67 | $ make install
68 | ```
69 |
70 | ## Usage
71 | ### Create your first empty podfile
72 |
73 | You can create an empty podfile.swift using the create command:
74 |
75 | ```sh
76 | $ swiftypods create
77 | ```
78 |
79 | It takes an optional path parameter that you can use to create your podfile at an specific location:
80 |
81 | ```sh
82 | $ swiftypods create --path "path/to/folder"
83 | ```
84 |
85 | Once the file is created you can jump directly to editing.
86 |
87 | ### Editing
88 |
89 | You can open your podfile.swift anytime and edit it, but if you want to use the power of the Swift compiler and autocomplete use:
90 |
91 | ```sh
92 | $ swiftypods edit
93 | ```
94 |
95 | This command will:
96 | 1) Find all your podfile.swift files inside the folder
97 | 2) Create a temporal Xcode project inside tmp folder
98 | 3) Open that project for you
99 |
100 | Once the project is opened you can edit your files content.
101 |
102 | When you have finished editing just close the Xcode project, go back to terminal and press any key. This will copy your templates back to original locations and delete temporal folder. Keep in mind that SwiftyPods has to copy edited files back to original locations. If you don't complete the terminal process, your podfiles won't be updated.
103 |
104 | ### Generating podfile
105 |
106 | In order to generate your podfile just run:
107 |
108 | ```sh
109 | $ swiftypods generate
110 | ```
111 |
112 | By default it will use an almost empty podfile template but you can use your own template using:
113 |
114 | ```sh
115 | $ swiftypods generate --template "templateLocation"
116 | ```
117 |
118 | The only thing your template needs to follow is adding {{pods}} where you want your dependencies to get generated. Base podfile template is:
119 |
120 | ```
121 | platform :ios, '13.0'
122 | inhibit_all_warnings!
123 |
124 | {{pods}}
125 | ```
126 |
127 | Use your own template to add features not supported by SwiftyPods like hooks or plugins.
128 |
129 | ## Contributions and support
130 |
131 | Contributions are more than welcome.
132 |
133 | Before you start using SwiftyPods, please take a few minutes to check the implementation so you can identify issues or missing features.
134 |
135 | Keep in mind this is a very experimental project, expect breaking changes.
136 |
137 | This project does not come with Github Issues enabled. If you find an issue, missing feature or missing documentation please [open a Pull Request](https://github.com/bitomule/SwiftyPods/pull/new). Your PR could just contain a draft of the changes you plan to do or a test that reproduces the issue so we can start the discussion there.
138 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/PackageBuilder.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 | import SwiftShell
4 | import TemplateRenderer
5 |
6 | public protocol PackageBuilding {
7 | @discardableResult
8 | func build(from path: URL, files: [URL]) throws -> URL
9 | func finish(originalFiles: [URL], path: URL) throws
10 | func buildProject(from path: URL, files: [URL]) throws -> URL
11 | func clean()
12 | }
13 |
14 | public final class PackageBuilder: PackageBuilding {
15 | private let temporalPathBuilder: TemporalPathBuilding
16 | private let manifestBuilder: PackageManifestBuilding
17 | private let templateFilesManager: TemplateFilesCoping
18 | private let templateRenderer: TemplateRendering
19 | private let storage: FileSysteming
20 | private let bash: BashRunning
21 | private let packageName: String
22 | private let templateWithCommands: String
23 | private let templateWithoutCommands: String
24 | private let mainFileWithCommandsTemplate: String
25 |
26 | public convenience init(packageName: String) {
27 | self.init(
28 | temporalPathBuilder: TemporalPathBuilder(),
29 | manifestBuilder: PackageManifestBuilder(),
30 | templateFilesManager: TemplateFilesManager(),
31 | templateRenderer: TemplateRenderer(),
32 | storage: FileSystem(),
33 | bash: Bash(),
34 | packageName: packageName,
35 | templateWithCommands: packageTemplate,
36 | templateWithoutCommands: dslOnlyPackage,
37 | mainFileWithCommandsTemplate: mainTemplate)
38 | }
39 |
40 | init(
41 | temporalPathBuilder: TemporalPathBuilding,
42 | manifestBuilder: PackageManifestBuilding,
43 | templateFilesManager: TemplateFilesCoping,
44 | templateRenderer: TemplateRendering,
45 | storage: FileSysteming,
46 | bash: BashRunning,
47 | packageName: String,
48 | templateWithCommands: String,
49 | templateWithoutCommands: String,
50 | mainFileWithCommandsTemplate: String
51 | ) {
52 | self.temporalPathBuilder = temporalPathBuilder
53 | self.manifestBuilder = manifestBuilder
54 | self.templateFilesManager = templateFilesManager
55 | self.templateRenderer = templateRenderer
56 | self.storage = storage
57 | self.bash = bash
58 | self.packageName = packageName
59 | self.templateWithCommands = templateWithCommands
60 | self.templateWithoutCommands = templateWithoutCommands
61 | self.mainFileWithCommandsTemplate = mainFileWithCommandsTemplate
62 | }
63 |
64 | @discardableResult
65 | public func build(from path: URL, files: [URL]) throws -> URL {
66 | let temporalPath = try createBasicPackage(path: path, files: files, manifestTemplate: templateWithCommands)
67 | try createMainSwift(sourcesPath: temporalPath, files: files)
68 | return temporalPath
69 | }
70 |
71 | public func buildProject(from path: URL, files: [URL]) throws -> URL {
72 | let temporalPath = try createBasicPackage(path: path, files: files, manifestTemplate: templateWithoutCommands)
73 | try createEmptyMainSwift(sourcesPath: temporalPath, files: files)
74 | print("Generating temporal project. This may take a few seconds...\n")
75 | bash.run(bash: "(cd \(temporalPath.path) && swift package generate-xcodeproj)")
76 | return temporalPath
77 | }
78 |
79 | public func finish(originalFiles: [URL], path: URL) throws {
80 | try copyFilesBack(originalFiles: originalFiles, path: path)
81 | try storage.delete(at: temporalPathBuilder.getRootTemporalPath())
82 | }
83 |
84 | public func clean() {
85 | try? storage.delete(at: temporalPathBuilder.getRootTemporalPath())
86 | }
87 |
88 | private func createBasicPackage(path: URL, files: [URL], manifestTemplate: String) throws -> URL {
89 | let temporalPath = try temporalPathBuilder.build(at: path)
90 | try files.forEach { file in
91 | let newFilePath = temporalPath.appendingPathComponent(file.lastPathComponent)
92 | try templateFilesManager.copyTemplate(from: file, to: newFilePath, override: false)
93 | }
94 | try manifestBuilder.build(at: temporalPath, packageName: packageName, template: manifestTemplate)
95 | return temporalPath
96 | }
97 |
98 | private func copyFilesBack(originalFiles: [URL], path: URL) throws {
99 | try originalFiles.forEach { file in
100 | let newFilePath = path.appendingPathComponent(file.lastPathComponent)
101 | try templateFilesManager.restoreTemplate(from: newFilePath, to: file)
102 | }
103 | }
104 |
105 | private func createMainSwift(sourcesPath: URL, files: [URL]) throws {
106 | let fileNames = try files.map { try templateFilesManager.getTemplateNameFrom(url: $0) }
107 | let mainContent = try templateRenderer.render(template: mainFileWithCommandsTemplate, context: ["podfiles": fileNames.joined(separator: ", ")])
108 | try storage.saveFile(name: "main.swift", path: sourcesPath, content: mainContent, overwrite: true)
109 | }
110 |
111 | private func createEmptyMainSwift(sourcesPath: URL, files: [URL]) throws {
112 | try storage.saveFile(name: "main.swift", path: sourcesPath, content: "", overwrite: true)
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/PackageManifestBuilder.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import TemplateRenderer
3 | import Storage
4 |
5 | protocol PackageManifestBuilding {
6 | func build(at path: URL, packageName: String, template: String) throws
7 | }
8 |
9 | public final class PackageManifestBuilder: PackageManifestBuilding {
10 | enum Constant {
11 | static let packageFileName = "Package.swift"
12 | }
13 | private let templateRenderer: TemplateRendering
14 | private let storage: FileSysteming
15 |
16 | init(templateRenderer: TemplateRendering = TemplateRenderer(),
17 | storage: FileSysteming = FileSystem()) {
18 | self.templateRenderer = templateRenderer
19 | self.storage = storage
20 | }
21 |
22 | func build(at path: URL, packageName: String, template: String) throws {
23 | let content = try templateRenderer.render(template: template, context: ["packageName": packageName])
24 | try storage.saveFile(name: Constant.packageFileName, path: path, content: content, overwrite: true)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/Shell.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import SwiftShell
3 |
4 | protocol BashRunning {
5 | func run(bash: String)
6 | }
7 |
8 | final class Bash: BashRunning {
9 | func run(bash: String) {
10 | main.run(bash: bash)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/TemplateFilesManager.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 |
4 | protocol TemplateFilesCoping {
5 | func copyTemplate(from: URL, to: URL, override: Bool) throws
6 | func restoreTemplate(from: URL, to: URL) throws
7 | func getTemplateNameFrom(url: URL) throws -> String
8 | }
9 |
10 | final class TemplateFilesManager: TemplateFilesCoping {
11 | enum Constant {
12 | static let defaultFileName = "podfile"
13 | static let fileExtension = ".swift"
14 | }
15 |
16 | private let storage: FileSysteming
17 |
18 | init (storage: FileSysteming = FileSystem()) {
19 | self.storage = storage
20 | }
21 |
22 | func copyTemplate(from: URL, to: URL, override: Bool) throws {
23 | try storage.copyFile(from: from, to: buildTemplateURLFromPropertyName(url: to, originalFile: from), override: override)
24 | }
25 |
26 | func restoreTemplate(from: URL, to: URL) throws {
27 | try storage.copyFile(from: buildTemplateURLFromPropertyName(url: from, originalFile: to), to: to, override: true)
28 | }
29 |
30 | func getTemplateNameFrom(url: URL) throws -> String {
31 | try findNameForFile(at: url)
32 | }
33 |
34 | private func buildTemplateURLFromPropertyName(url: URL, originalFile: URL) throws -> URL {
35 | let nameForTarget = try findNameForFile(at: originalFile) + Constant.fileExtension
36 | let targetUrl = url.deletingLastPathComponent()
37 | return targetUrl.appendingPathComponent(nameForTarget)
38 | }
39 |
40 | private func findNameForFile(at url: URL) throws -> String {
41 | let content = try storage.getFile(at: url)
42 | return try getNameFromContent(content: content) ?? Constant.defaultFileName
43 | }
44 |
45 | private func getNameFromContent(content: String) throws -> String? {
46 | return try match(for: #"let ([^\s]+)\s?=\s?Podfile"#, in: content)
47 | }
48 |
49 | private func match(for regex: String, in text: String) throws -> String? {
50 | let regex = try NSRegularExpression(pattern: regex)
51 | guard let wholeMatch = regex.firstMatch(in: text, range: NSRange(text.startIndex..., in: text)),
52 | let matchRange = Range(wholeMatch.range(at: 1), in: text) else {
53 | return nil
54 | }
55 | return String(text[matchRange])
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/Templates/dslOnlyPackageTemplate.swift:
--------------------------------------------------------------------------------
1 | let dslOnlyPackage = """
2 | // swift-tools-version:5.1
3 | // The swift-tools-version declares the minimum version of Swift required to build this package.
4 |
5 | import PackageDescription
6 |
7 | let package = Package(
8 | name: "{{packageName}}",
9 | products: [
10 | .executable(name: "{{packageName}}", targets: ["{{packageName}}"]),],
11 | dependencies: [
12 | .package(url: "https://github.com/bitomule/SwiftyPodsDSL", .branch("master"))
13 | ],
14 | targets: [
15 | .target(
16 | name: "{{packageName}}",
17 | dependencies: [
18 | "SwiftyPodsDSL"
19 | ],
20 | path: ""
21 | )
22 | ]
23 | )
24 | """
25 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/Templates/mainTemplate.swift:
--------------------------------------------------------------------------------
1 | let mainTemplate = """
2 | import Foundation
3 | import ArgumentParser
4 | import SwiftyPodsDSL
5 | import PodfileBuilder
6 |
7 | let podfiles = [{{podfiles}}]
8 |
9 | struct Generate: ParsableCommand {
10 | public static let configuration = CommandConfiguration(abstract: "Generate podfile")
11 |
12 | @Argument(help: "Path where podfile will be generated")
13 | private var path: String
14 |
15 | @Option(name: .shortAndLong, default: nil, help: "Optional path to template file")
16 | private var templatePath: String?
17 |
18 | func run() throws {
19 | try PodfileBuilder().buildPodfile(podfiles: podfiles, path: path, templatePath: templatePath)
20 | }
21 | }
22 |
23 | Generate.main()
24 | """
25 |
26 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/Templates/packageTemplate.swift:
--------------------------------------------------------------------------------
1 | let packageTemplate = """
2 | // swift-tools-version:5.1
3 | // The swift-tools-version declares the minimum version of Swift required to build this package.
4 |
5 | import PackageDescription
6 |
7 | let package = Package(
8 | name: "{{packageName}}",
9 | products: [
10 | .executable(name: "{{packageName}}", targets: ["{{packageName}}"]),],
11 | dependencies: [
12 | .package(url: "git@github.com:bitomule/SwiftyPods", .branch("master")),
13 | .package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1"),
14 | .package(url: "https://github.com/bitomule/SwiftyPodsDSL", .branch("master"))
15 | ],
16 | targets: [
17 | .target(
18 | name: "{{packageName}}",
19 | dependencies: [
20 | .product(name: "ArgumentParser", package: "swift-argument-parser"),
21 | "SwiftyPodsDSL",
22 | "PodfileBuilder"
23 | ],
24 | path: ""
25 | )
26 | ]
27 | )
28 |
29 | """
30 |
--------------------------------------------------------------------------------
/Sources/PackageBuilder/TemporalPathBuilder.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 |
4 | protocol TemporalPathBuilding {
5 | func build(at path: URL) throws -> URL
6 | func getRootTemporalPath() -> String
7 | }
8 |
9 | final class TemporalPathBuilder: TemporalPathBuilding {
10 | enum Constant {
11 | static let temporalFolderPath = "tmp/"
12 | }
13 | private let storage: FileSysteming
14 |
15 | init(storage: FileSysteming = FileSystem()) {
16 | self.storage = storage
17 | }
18 |
19 | func build(at path: URL) throws -> URL {
20 | let uuid = UUID().uuidString
21 | let tmpPath = Constant.temporalFolderPath + uuid + "/"
22 | let newPath = path.appendingPathComponent(tmpPath, isDirectory: true)
23 | try storage.createFolder(at: newPath)
24 | return newPath
25 | }
26 |
27 | func getRootTemporalPath() -> String {
28 | Constant.temporalFolderPath
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/PodfileBuilder/PodfileBuilder.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 | import SwiftyPodsDSL
4 | import TemplateRenderer
5 |
6 | public final class PodfileBuilder {
7 | private enum Constant {
8 | static let propertyName = "pods"
9 | }
10 |
11 | private let templateRenderer: TemplateRendering
12 | private let storage: FileSysteming
13 | private let defaultTemplate: String
14 |
15 | public convenience init() {
16 | self.init(templateRenderer: TemplateRenderer(), storage: FileSystem(), defaultTemplate: podfileTemplate)
17 | }
18 |
19 | init(templateRenderer: TemplateRendering,
20 | storage: FileSysteming,
21 | defaultTemplate: String) {
22 | self.templateRenderer = templateRenderer
23 | self.storage = storage
24 | self.defaultTemplate = defaultTemplate
25 | }
26 |
27 | public func buildPodfile(podfiles: [Podfile], path: String, templatePath: String? = nil) throws {
28 | let url = URL(fileURLWithPath: path, isDirectory: true)
29 | let lines = dependenciesLines(from: podfiles)
30 | let template = try templateRenderer.render(
31 | template: try loadTemplateFromPath(templatePath) ?? defaultTemplate,
32 | context: lines
33 | )
34 | try storage.saveFile(name: "podfile", path: url, content: template, overwrite: true)
35 | }
36 |
37 | private func loadTemplateFromPath(_ path: String?) throws -> String? {
38 | guard let path = path else { return nil }
39 | return try storage.getFile(at: URL(fileURLWithPath: path))
40 | }
41 |
42 | private func dependenciesLines(from podfiles: [Podfile]) -> [String: String] {
43 | [Constant.propertyName: podfiles.map { $0.render() }.joined(separator: "\n")]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Sources/PodfileBuilder/podfileTemplate.swift:
--------------------------------------------------------------------------------
1 | let podfileTemplate = """
2 | platform :ios, '13.0'
3 | inhibit_all_warnings!
4 |
5 | {{pods}}
6 | """
7 |
--------------------------------------------------------------------------------
/Sources/Storage/FileSystem.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public enum FileSystemError: Error {
4 | case fileDoesNotExists(String)
5 | }
6 |
7 | public protocol FileSysteming {
8 | func saveFile(name: String, path: URL, content: String, overwrite: Bool) throws
9 | func getFile(at: URL) throws -> String
10 | func copyFile(from: URL, to: URL, override: Bool) throws
11 | func delete(at path: String) throws
12 | func createFolder(at url:URL) throws
13 | func findFilesInFolder(at url: URL, matching: (URL) -> Bool) throws -> [URL]
14 | func fileExists(at url: URL) -> Bool
15 | }
16 |
17 | public final class FileSystem: FileSysteming {
18 | private let manager: FileManager
19 | private let encoding: String.Encoding
20 |
21 | public init(manager: FileManager = FileManager.default, encoding: String.Encoding = .utf8) {
22 | self.manager = manager
23 | self.encoding = encoding
24 | }
25 |
26 | public func saveFile(name: String, path: URL, content: String, overwrite: Bool) throws {
27 | let newFile = path.appendingPathComponent(name).path
28 | if(overwrite || !manager.fileExists(atPath:newFile)){
29 | manager.createFile(atPath: newFile, contents: content.data(using: encoding), attributes: nil)
30 | } else{
31 | print("File is already created")
32 | }
33 | }
34 |
35 | public func copyFile(from: URL, to: URL, override: Bool) throws {
36 | guard manager.fileExists(atPath: from.path) else {
37 | throw FileSystemError.fileDoesNotExists(from.path)
38 | }
39 | if override && manager.fileExists(atPath: to.path) {
40 | try manager.removeItem(at: to)
41 | }
42 | try manager.copyItem(at: from, to: to)
43 | }
44 |
45 | public func getFile(at url: URL) throws -> String {
46 | try String(contentsOf: url)
47 | }
48 |
49 | public func delete(at path: String) throws {
50 | try manager.removeItem(atPath: path)
51 | }
52 |
53 | public func createFolder(at url: URL) throws {
54 | try manager.createDirectory(atPath: url.path, withIntermediateDirectories: true, attributes: nil)
55 | }
56 |
57 | public func findFilesInFolder(at url: URL, matching: (URL) -> Bool) throws -> [URL] {
58 | try manager
59 | .enumerator(at: url, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles], errorHandler: nil)?
60 | .allObjects
61 | .map { $0 as! URL }
62 | .filter { url in
63 | let resourceValues = try url.resourceValues(forKeys: [.isDirectoryKey])
64 | return !(resourceValues.isDirectory ?? true)
65 | }
66 | .filter(matching) ?? []
67 | }
68 |
69 | public func fileExists(at url: URL) -> Bool {
70 | manager.fileExists(atPath: url.path)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/Commands/CreateCommand.swift:
--------------------------------------------------------------------------------
1 | import ArgumentParser
2 | import Foundation
3 |
4 | struct Create: ParsableCommand {
5 |
6 | public static let configuration = CommandConfiguration(abstract: "Create an empty podfile.swift")
7 |
8 | @Option(name: .shortAndLong, default: nil, help: "Optional path where podfile.swift should be created")
9 | private var path: String?
10 |
11 | func run() throws {
12 | try CreateService().run(path: path)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/Commands/EditCommand.swift:
--------------------------------------------------------------------------------
1 | import ArgumentParser
2 | import Foundation
3 |
4 | struct Edit: ParsableCommand {
5 | public static let configuration = CommandConfiguration(abstract: "Open templates in folder to edit")
6 |
7 | func run() throws {
8 | try EditService().run()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/Commands/GenerateCommand.swift:
--------------------------------------------------------------------------------
1 | import ArgumentParser
2 | import Foundation
3 |
4 | struct Generate: ParsableCommand {
5 |
6 | public static let configuration = CommandConfiguration(abstract: "Generate a pod file from template")
7 |
8 | @Option(name: .shortAndLong, default: nil, help: "Optional path to template file")
9 | private var template: String?
10 |
11 | func run() throws {
12 | try GenerateService().run(template: template)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/Services/CreateService.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 | import PackageBuilder
4 | import SwiftShell
5 |
6 | final class CreateService {
7 | private enum Constant {
8 | static let fileName = "podfile.swift"
9 | }
10 | private let storage: FileSysteming
11 | private let template: String
12 |
13 | init(storage: FileSysteming = FileSystem(),
14 | template: String = emptyPodfileTemplate) {
15 | self.storage = storage
16 | self.template = template
17 | }
18 |
19 | func run(path: String?) throws {
20 | let url: URL
21 | if let path = path {
22 | url = URL(fileURLWithPath: path, isDirectory: true)
23 | } else {
24 | url = URL(fileURLWithPath: "", isDirectory: true)
25 | }
26 | try storage.saveFile(name: Constant.fileName, path: url, content: template, overwrite: false)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/Services/EditService.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import PackageBuilder
3 | import SwiftShell
4 |
5 | final class EditService {
6 | enum Constant {
7 | static let openCommand = "open"
8 | static let packageName = "SwiftyPodsTemporalProject"
9 | static let packageFileName = "Package.swift"
10 | static let projectFileExtension = ".xcodeproj"
11 | }
12 |
13 | private let packageBuilder: PackageBuilding
14 | private let templatesLocator: TemplateLocating
15 |
16 | init(packageBuilder: PackageBuilding = PackageBuilder(packageName: Constant.packageName),
17 | templatesLocator: TemplateLocating = TemplateLocator()) {
18 | self.packageBuilder = packageBuilder
19 | self.templatesLocator = templatesLocator
20 | }
21 |
22 | func run() throws {
23 | packageBuilder.clean()
24 | let baseUrl = URL(fileURLWithPath: "", isDirectory: true)
25 | let files = try getTemplateFiles()
26 | let url = try packageBuilder.buildProject(from: baseUrl, files: files)
27 | open(url: url)
28 | try waitForUserInput(projectUrl: url, files: files)
29 | }
30 |
31 | private func getTemplateFiles() throws -> [URL] {
32 | let baseUrl = URL(fileURLWithPath: "", isDirectory: true)
33 | return try templatesLocator.findTemplates(at: baseUrl)
34 | }
35 |
36 | private func waitForUserInput(projectUrl: URL, files: [URL]) throws {
37 | print("Opening Xcode project. Press any key to when you've finished editing")
38 | var ended = false
39 | while !ended, let _ = main.stdin.readSome() {
40 | ended = true
41 | try packageBuilder.finish(originalFiles: files, path: projectUrl)
42 | print("Your edited podfiles are ready")
43 | }
44 | }
45 |
46 | private func open(url: URL) {
47 | let packageURL = url.appendingPathComponent(Constant.packageName + Constant.projectFileExtension)
48 | main.run(Constant.openCommand, packageURL.relativeString)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/Services/GenerateService.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 | import PackageBuilder
4 | import SwiftShell
5 |
6 | final class GenerateService {
7 | private enum Constant {
8 | static let templateFileName = "podfile"
9 | static let packageName = "SwiftyPodsTemporalProject"
10 | }
11 | private let templateArgumentParser: TemplateArgumentParser
12 | private let packageBuilder: PackageBuilding
13 | private let templatesLocator: TemplateLocating
14 |
15 | init(templateArgumentParser: TemplateArgumentParser = TemplateArgumentParser(),
16 | packageBuilder: PackageBuilding = PackageBuilder(packageName: Constant.packageName),
17 | templatesLocator: TemplateLocating = TemplateLocator()) {
18 | self.templateArgumentParser = templateArgumentParser
19 | self.packageBuilder = packageBuilder
20 | self.templatesLocator = templatesLocator
21 | }
22 |
23 | func run(template: String?) throws {
24 | packageBuilder.clean()
25 | let baseUrl = URL(fileURLWithPath: "", isDirectory: true)
26 | let files = try templatesLocator.findTemplates(at: baseUrl)
27 | let url = try packageBuilder.build(from: baseUrl, files: files)
28 | try generate(url: url, template: template)
29 | print("Cleaning")
30 | try packageBuilder.finish(originalFiles: files, path: url)
31 | }
32 |
33 | private func generate(url: URL, template: String?) throws {
34 | print("Generating podfile\n")
35 | if let template = template {
36 | let templatePath = URL(fileURLWithPath: template)
37 | try main.runAndPrint(bash: "(cd \(url.path) && swift run \(Constant.packageName) \(URL(fileURLWithPath: "", isDirectory: true).path) --template-path \"\(templatePath.path)\")")
38 | } else {
39 | try main.runAndPrint(bash: "(cd \(url.path) && swift run \(Constant.packageName) \(URL(fileURLWithPath: "", isDirectory: true).path))")
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/TemplateArgumentParser.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 |
4 | class TemplateArgumentParser {
5 | private let storage: FileSysteming
6 |
7 | init(storage: FileSysteming = FileSystem()) {
8 | self.storage = storage
9 | }
10 |
11 | func getTemplateName(template: String) -> URL? {
12 | let templateURL = URL(fileURLWithPath: template)
13 | guard storage.fileExists(at: templateURL) else {
14 | return nil
15 | }
16 | return templateURL
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/TemplateLocator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 |
4 | protocol TemplateLocating {
5 | func findTemplates(at path: URL) throws -> [URL]
6 | }
7 |
8 | final class TemplateLocator: TemplateLocating {
9 | enum Constant {
10 | static let templateName = "podfile.swift"
11 | }
12 |
13 | private let storage: FileSysteming
14 |
15 | init(storage: FileSysteming = FileSystem()) {
16 | self.storage = storage
17 | }
18 |
19 | func findTemplates(at path: URL) throws -> [URL] {
20 | try storage.findFilesInFolder(at: path, matching: { $0.lastPathComponent == Constant.templateName })
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/emtyPodfileTemplate.swift:
--------------------------------------------------------------------------------
1 | let emptyPodfileTemplate = """
2 | import Foundation
3 | import SwiftyPodsDSL
4 |
5 | let podfile = Podfile(targets: [])
6 | """
7 |
--------------------------------------------------------------------------------
/Sources/SwiftyPods/main.swift:
--------------------------------------------------------------------------------
1 | import ArgumentParser
2 |
3 | struct SwiftyPods: ParsableCommand {
4 | static let configuration = CommandConfiguration(
5 | abstract: "A Swift command-line tool to manage podfile",
6 | subcommands: [
7 | Generate.self,
8 | Edit.self,
9 | Create.self
10 | ])
11 |
12 | init() { }
13 | }
14 |
15 | SwiftyPods.main()
16 |
--------------------------------------------------------------------------------
/Sources/TemplateRenderer/TemplateRenderer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import Storage
3 |
4 | public protocol TemplateRendering {
5 | func render(
6 | template: String,
7 | context: [String: String]
8 | ) throws -> String
9 |
10 | func render(
11 | templateFile: URL,
12 | context: [String: String]
13 | ) throws -> String
14 | }
15 |
16 | public final class TemplateRenderer: TemplateRendering {
17 | private let storage: FileSysteming
18 |
19 | public init(storage: FileSysteming = FileSystem()){
20 | self.storage = storage
21 | }
22 |
23 | public func render(
24 | template: String,
25 | context: [String: String]
26 | ) throws -> String {
27 | context.reduce(template) { result, dict in
28 | return generateFile(template: result, value: dict.value, keyToReplace: dict.key)
29 | }
30 | }
31 |
32 | public func render(
33 | templateFile: URL,
34 | context: [String: String]
35 | ) throws -> String {
36 | let template = try storage.getFile(at: templateFile)
37 | return context.reduce(template) { result, dict in
38 | return generateFile(template: result, value: dict.value, keyToReplace: dict.key)
39 | }
40 | }
41 |
42 | private func generateFile(template: String, value: String, keyToReplace: String) -> String {
43 | template.replacingOccurrences(of: "{{\(keyToReplace)}}", with: value)
44 | }
45 |
46 | private func filePath(from url: URL) -> String {
47 | url.deletingLastPathComponent().relativeString
48 | }
49 |
50 | private func fileName(from url: URL) -> String {
51 | url.lastPathComponent
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import SwiftyPodsTests
4 | import TemplateRendererTests
5 |
6 | var tests = [XCTestCaseEntry]()
7 | tests += SwiftyPodsTests.allTests()
8 | tests += TemplateRendererTests.allTests()
9 | XCTMain(tests)
10 |
--------------------------------------------------------------------------------
/Tests/PackageBuilderTests/PackageBuilderTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import class Foundation.Bundle
3 | @testable import PackageBuilder
4 | import TemplateRenderer
5 | import Storage
6 |
7 | final class PackageBuilderTests: XCTestCase {
8 | private var packageBuilder: PackageBuilder!
9 |
10 | // MARK: - Build
11 |
12 | func testBuildCreatesTemporalPath() throws {
13 | let temporalPathBuilder = TemporalPathBuidingMock()
14 | packageBuilder = PackageBuilder(
15 | temporalPathBuilder: temporalPathBuilder,
16 | manifestBuilder: PackageManifestBuildingMock(),
17 | templateFilesManager: TemplateFilesCopingMock(),
18 | templateRenderer: TemplateRenderingMock(),
19 | storage: FileSystemMock(),
20 | bash: BashMock(),
21 | packageName: "packageName",
22 | templateWithCommands: "",
23 | templateWithoutCommands: "",
24 | mainFileWithCommandsTemplate: "")
25 |
26 | _ = try packageBuilder.build(from: URL(fileURLWithPath: ""), files: [])
27 |
28 | XCTAssertNotNil(temporalPathBuilder.buildArguments)
29 | XCTAssertEqual(temporalPathBuilder.buildArguments!.path.relativePath, ".")
30 | }
31 |
32 | func testBuildCopiesFiles() throws {
33 | let templateFilesManager = TemplateFilesCopingMock()
34 | templateFilesManager.templateName = "TemplateName"
35 | packageBuilder = PackageBuilder(
36 | temporalPathBuilder: TemporalPathBuidingMock(),
37 | manifestBuilder: PackageManifestBuildingMock(),
38 | templateFilesManager: templateFilesManager,
39 | templateRenderer: TemplateRenderingMock(),
40 | storage: FileSystemMock(),
41 | bash: BashMock(),
42 | packageName: "packageName",
43 | templateWithCommands: "",
44 | templateWithoutCommands: "",
45 | mainFileWithCommandsTemplate: "")
46 |
47 | _ = try packageBuilder.build(from: URL(fileURLWithPath: ""), files: [URL(fileURLWithPath: "aFolder/podfile.swift")])
48 |
49 | XCTAssertNotNil(templateFilesManager.copyTemplateArguments)
50 | XCTAssertEqual(templateFilesManager.copyTemplateArguments!.from.relativePath, "aFolder/podfile.swift")
51 | XCTAssertEqual(templateFilesManager.copyTemplateArguments!.to.relativePath, "tmp/podfile.swift")
52 | }
53 |
54 | func testBuildCreatesManifest() throws {
55 | let manifestBuilderMock = PackageManifestBuildingMock()
56 | packageBuilder = PackageBuilder(
57 | temporalPathBuilder: TemporalPathBuidingMock(),
58 | manifestBuilder: manifestBuilderMock,
59 | templateFilesManager: TemplateFilesCopingMock(),
60 | templateRenderer: TemplateRenderingMock(),
61 | storage: FileSystemMock(),
62 | bash: BashMock(),
63 | packageName: "packageName",
64 | templateWithCommands: "templateWithCommands",
65 | templateWithoutCommands: "templateWithoutCommands",
66 | mainFileWithCommandsTemplate: "")
67 |
68 | _ = try packageBuilder.build(from: URL(fileURLWithPath: ""), files: [URL(fileURLWithPath: "aFolder/podfile.swift")])
69 |
70 | XCTAssertNotNil(manifestBuilderMock.buildArguments)
71 | XCTAssertEqual(manifestBuilderMock.buildArguments!.path.relativePath, "tmp")
72 | XCTAssertEqual(manifestBuilderMock.buildArguments!.packageName, "packageName")
73 | XCTAssertEqual(manifestBuilderMock.buildArguments!.template, "templateWithCommands")
74 | }
75 |
76 | func testBuildCreatesCommandsMain() throws {
77 | let fileSystemMock = FileSystemMock()
78 | let templateRendererMock = TemplateRenderingMock()
79 | packageBuilder = PackageBuilder(
80 | temporalPathBuilder: TemporalPathBuidingMock(),
81 | manifestBuilder: PackageManifestBuildingMock(),
82 | templateFilesManager: TemplateFilesCopingMock(),
83 | templateRenderer: templateRendererMock,
84 | storage: fileSystemMock,
85 | bash: BashMock(),
86 | packageName: "packageName",
87 | templateWithCommands: "templateWithCommands",
88 | templateWithoutCommands: "templateWithoutCommands",
89 | mainFileWithCommandsTemplate: "mainTemplateHere")
90 |
91 | _ = try packageBuilder.build(from: URL(fileURLWithPath: ""), files: [URL(fileURLWithPath: "aFolder/podfile.swift")])
92 |
93 | XCTAssertNotNil(templateRendererMock.renderArguments)
94 | XCTAssertEqual(templateRendererMock.renderArguments!.template, "mainTemplateHere")
95 | XCTAssertNotNil(templateRendererMock.renderArguments!.context["podfiles"])
96 |
97 | XCTAssertNotNil(fileSystemMock.saveFileArguments)
98 | XCTAssertEqual(fileSystemMock.saveFileArguments!.name, "main.swift")
99 | XCTAssertEqual(fileSystemMock.saveFileArguments!.path.relativePath, "tmp")
100 | XCTAssertEqual(fileSystemMock.saveFileArguments!.content, "")
101 | XCTAssertTrue(fileSystemMock.saveFileArguments!.overwrite)
102 | }
103 |
104 | // MARK: - BuildProject
105 |
106 | func testBuildProjectCreatesEmptyMain() throws {
107 | let fileSystemMock = FileSystemMock()
108 | packageBuilder = PackageBuilder(
109 | temporalPathBuilder: TemporalPathBuidingMock(),
110 | manifestBuilder: PackageManifestBuildingMock(),
111 | templateFilesManager: TemplateFilesCopingMock(),
112 | templateRenderer: TemplateRenderingMock(),
113 | storage: fileSystemMock,
114 | bash: BashMock(),
115 | packageName: "packageName",
116 | templateWithCommands: "templateWithCommands",
117 | templateWithoutCommands: "templateWithoutCommands",
118 | mainFileWithCommandsTemplate: "")
119 |
120 | _ = try packageBuilder.buildProject(from: URL(fileURLWithPath: ""), files: [URL(fileURLWithPath: "aFolder/podfile.swift")])
121 |
122 | XCTAssertNotNil(fileSystemMock.saveFileArguments)
123 | XCTAssertEqual(fileSystemMock.saveFileArguments!.name, "main.swift")
124 | XCTAssertEqual(fileSystemMock.saveFileArguments!.path.relativePath, "tmp")
125 | XCTAssertEqual(fileSystemMock.saveFileArguments!.content, "")
126 | XCTAssertTrue(fileSystemMock.saveFileArguments!.overwrite)
127 | }
128 |
129 | func testBuildProjectCreatesManifest() throws {
130 | let manifestBuilderMock = PackageManifestBuildingMock()
131 | packageBuilder = PackageBuilder(
132 | temporalPathBuilder: TemporalPathBuidingMock(),
133 | manifestBuilder: manifestBuilderMock,
134 | templateFilesManager: TemplateFilesCopingMock(),
135 | templateRenderer: TemplateRenderingMock(),
136 | storage: FileSystemMock(),
137 | bash: BashMock(),
138 | packageName: "packageName",
139 | templateWithCommands: "templateWithCommands",
140 | templateWithoutCommands: "templateWithoutCommands",
141 | mainFileWithCommandsTemplate: "")
142 |
143 | _ = try packageBuilder.buildProject(from: URL(fileURLWithPath: ""), files: [URL(fileURLWithPath: "aFolder/podfile.swift")])
144 |
145 | XCTAssertNotNil(manifestBuilderMock.buildArguments)
146 | XCTAssertEqual(manifestBuilderMock.buildArguments!.path.relativePath, "tmp")
147 | XCTAssertEqual(manifestBuilderMock.buildArguments!.packageName, "packageName")
148 | XCTAssertEqual(manifestBuilderMock.buildArguments!.template, "templateWithoutCommands")
149 | }
150 |
151 | func testBuildProjectRunsGenerateXcodeProj() throws {
152 | let bashMock = BashMock()
153 | packageBuilder = PackageBuilder(
154 | temporalPathBuilder: TemporalPathBuidingMock(),
155 | manifestBuilder: PackageManifestBuildingMock(),
156 | templateFilesManager: TemplateFilesCopingMock(),
157 | templateRenderer: TemplateRenderingMock(),
158 | storage: FileSystemMock(),
159 | bash: bashMock,
160 | packageName: "packageName",
161 | templateWithCommands: "templateWithCommands",
162 | templateWithoutCommands: "templateWithoutCommands",
163 | mainFileWithCommandsTemplate: "")
164 |
165 | let path = try packageBuilder.buildProject(from: URL(fileURLWithPath: ""), files: [URL(fileURLWithPath: "aFolder/podfile.swift")])
166 |
167 | XCTAssertNotNil(bashMock.bashArgument)
168 | XCTAssertEqual(bashMock.bashArgument!, "(cd \(path.path) && swift package generate-xcodeproj)")
169 | }
170 |
171 | static var allTests = [
172 | ("testBuildCreatesTemporalPath", testBuildCreatesTemporalPath),
173 | ("testBuildCopiesFiles", testBuildCopiesFiles),
174 | ("testBuildCreatesManifest", testBuildCreatesManifest),
175 | ("testBuildProjectCreatesEmptyMain", testBuildProjectCreatesEmptyMain),
176 | ("testBuildProjectCreatesManifest", testBuildProjectCreatesManifest),
177 | ("testBuildCreatesCommandsMain", testBuildCreatesCommandsMain),
178 | ("testBuildProjectRunsGenerateXcodeProj", testBuildProjectRunsGenerateXcodeProj)
179 | ]
180 | }
181 |
182 | // MARK: - Mocks
183 |
184 | private final class TemporalPathBuidingMock: TemporalPathBuilding {
185 | struct BuildArguments {
186 | let path: URL
187 | }
188 | var buildArguments: BuildArguments?
189 |
190 | func build(at path: URL) throws -> URL {
191 | buildArguments = BuildArguments(path: path)
192 | return URL(fileURLWithPath: "tmp")
193 | }
194 |
195 | func getRootTemporalPath() -> String {
196 | fatalError()
197 | }
198 | }
199 |
200 | private final class PackageManifestBuildingMock: PackageManifestBuilding {
201 | struct BuildArguments {
202 | let path: URL
203 | let packageName: String
204 | let template: String
205 | }
206 | var buildArguments: BuildArguments?
207 |
208 | func build(at path: URL, packageName: String, template: String) throws {
209 | buildArguments = BuildArguments(path: path, packageName: packageName, template: template)
210 | }
211 | }
212 |
213 | private final class TemplateFilesCopingMock: TemplateFilesCoping {
214 | struct CopyTemplateArguments {
215 | let from: URL
216 | let to: URL
217 | let override: Bool
218 | }
219 | var copyTemplateArguments: CopyTemplateArguments?
220 | var templateName: String?
221 |
222 | func copyTemplate(from: URL, to: URL, override: Bool) throws {
223 | copyTemplateArguments = CopyTemplateArguments(from: from, to: to, override: override)
224 | }
225 |
226 | func restoreTemplate(from: URL, to: URL) throws {
227 | fatalError()
228 | }
229 |
230 | func getTemplateNameFrom(url: URL) throws -> String {
231 | templateName ?? ""
232 | }
233 | }
234 |
235 | private final class TemplateRenderingMock: TemplateRendering {
236 | struct RenderArguments {
237 | let template: String
238 | let context: [String: String]
239 | }
240 | var renderArguments: RenderArguments?
241 |
242 | func render(
243 | template: String,
244 | context: [String: String]
245 | ) throws -> String {
246 | renderArguments = RenderArguments(template: template, context: context)
247 | return ""
248 | }
249 |
250 | func render(
251 | templateFile: URL,
252 | context: [String: String]
253 | ) throws -> String {
254 | ""
255 | }
256 | }
257 |
258 | private final class FileSystemMock: FileSysteming {
259 | struct SaveFileArguments {
260 | let name: String
261 | let path: URL
262 | let content: String
263 | let overwrite: Bool
264 | }
265 | var saveFileArguments: SaveFileArguments?
266 |
267 | func saveFile(name: String, path: URL, content: String, overwrite: Bool) throws {
268 | saveFileArguments = SaveFileArguments(name: name, path: path, content: content, overwrite: overwrite)
269 | }
270 |
271 | func getFile(at: URL) throws -> String {
272 | podfileStub
273 | }
274 |
275 | func copyFile(from: URL, to: URL, override: Bool) throws {}
276 |
277 | func delete(at path: String) throws {
278 | fatalError()
279 | }
280 |
281 | func createFolder(at url:URL) throws {
282 | fatalError()
283 | }
284 |
285 | func findFilesInFolder(at url: URL, matching: (URL) -> Bool) throws -> [URL] {
286 | []
287 | }
288 |
289 | func fileExists(at url: URL) -> Bool {
290 | false
291 | }
292 | }
293 |
294 | private final class BashMock: BashRunning {
295 | var bashArgument: String?
296 |
297 | func run(bash: String) {
298 | bashArgument = bash
299 | }
300 | }
301 |
302 | private let podfileStub = """
303 | import Foundation
304 | import SwiftyPodsDSL
305 |
306 | let example = Podfile(
307 | targets: [
308 | .target(
309 | name: "Target",
310 | project: "Project",
311 | dependencies: [
312 | .dependency(name: "Dependency1"),
313 | .dependency(name: "Dependency2",
314 | version: "1.2.3"),
315 | .dependency(name: "Dependency3",
316 | .git(url: "repo"),
317 | .branch(name: "master"))
318 | ],
319 | childTargets: [
320 | .target(name: "ChildTarget", project: "Project2")
321 | ]
322 | )
323 | ]
324 | )
325 | """
326 |
--------------------------------------------------------------------------------
/Tests/PackageBuilderTests/PackageManifestBuilderTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import class Foundation.Bundle
3 | @testable import PackageBuilder
4 | import TemplateRenderer
5 | import Storage
6 |
7 | final class PackageManifestBuilderTests: XCTestCase {
8 | private var manifestBuilder: PackageManifestBuilder!
9 |
10 | func testSavesManifest() throws {
11 | let templateRendererMock = TemplateRendererMock()
12 | templateRendererMock.rendered = "Rendered"
13 | let fileSystemMock = FileSystemMock()
14 | manifestBuilder = PackageManifestBuilder(templateRenderer: templateRendererMock,
15 | storage:fileSystemMock)
16 | try manifestBuilder.build(at: URL(fileURLWithPath: "packagePath"), packageName: "TestPackage", template: "")
17 | XCTAssertNotNil(fileSystemMock.saveFileArguments)
18 | XCTAssertEqual(fileSystemMock.saveFileArguments!.name, "Package.swift")
19 | XCTAssertEqual(fileSystemMock.saveFileArguments!.path.relativePath, "packagePath")
20 | XCTAssertEqual(fileSystemMock.saveFileArguments!.content, "Rendered")
21 | XCTAssertTrue(fileSystemMock.saveFileArguments!.overwrite)
22 | }
23 |
24 |
25 | static var allTests = [
26 | ("testSavesManifest", testSavesManifest)
27 | ]
28 | }
29 |
30 | // MARK: - Mocks
31 |
32 | private final class TemplateRendererMock: TemplateRendering {
33 | var rendered: String = ""
34 |
35 | func render(
36 | template: String,
37 | context: [String: String]
38 | ) throws -> String {
39 | rendered
40 | }
41 |
42 | func render(
43 | templateFile: URL,
44 | context: [String: String]
45 | ) throws -> String {
46 | rendered
47 | }
48 | }
49 |
50 | private final class FileSystemMock: FileSysteming {
51 | struct SaveFileArguments {
52 | let name: String
53 | let path: URL
54 | let content: String
55 | let overwrite: Bool
56 | }
57 | var saveFileArguments: SaveFileArguments?
58 |
59 | func saveFile(name: String, path: URL, content: String, overwrite: Bool) throws {
60 | saveFileArguments = SaveFileArguments(name: name, path: path, content: content, overwrite: overwrite)
61 | }
62 |
63 | func getFile(at: URL) throws -> String {
64 | fatalError()
65 | }
66 |
67 | func copyFile(from: URL, to: URL, override: Bool) throws {
68 | fatalError()
69 | }
70 |
71 | func delete(at path: String) throws {
72 | fatalError()
73 | }
74 |
75 | func createFolder(at url:URL) throws {
76 | fatalError()
77 | }
78 |
79 | func findFilesInFolder(at url: URL, matching: (URL) -> Bool) throws -> [URL] {
80 | []
81 | }
82 |
83 | func fileExists(at url: URL) -> Bool {
84 | false
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Tests/PackageBuilderTests/TemplateFilesManagerTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import class Foundation.Bundle
3 | @testable import PackageBuilder
4 | import TemplateRenderer
5 | import Storage
6 |
7 | final class TemplateFilesManagerTests: XCTestCase {
8 | private var templateFilesManager: TemplateFilesManager!
9 |
10 | func testCopiesTemplate() throws {
11 | let fileSystemMock = FileSystemMock()
12 | templateFilesManager = TemplateFilesManager(storage: fileSystemMock)
13 |
14 | try templateFilesManager.copyTemplate(from: URL(fileURLWithPath: "one.swift"), to: URL(fileURLWithPath: "two.swift"), override: true)
15 |
16 | XCTAssertNotNil(fileSystemMock.copyFileArguments)
17 | XCTAssertEqual(fileSystemMock.copyFileArguments!.from.relativePath, "one.swift")
18 | XCTAssertEqual(fileSystemMock.copyFileArguments!.to.relativePath, "./example.swift")
19 | XCTAssertTrue(fileSystemMock.copyFileArguments!.override)
20 | }
21 |
22 | func testRestoresTemplate() throws {
23 | let fileSystemMock = FileSystemMock()
24 | templateFilesManager = TemplateFilesManager(storage: fileSystemMock)
25 |
26 | try templateFilesManager.restoreTemplate(from: URL(fileURLWithPath: "./example.swift"), to: URL(fileURLWithPath: "one.swift"))
27 |
28 | XCTAssertNotNil(fileSystemMock.copyFileArguments)
29 | XCTAssertEqual(fileSystemMock.copyFileArguments!.from.relativePath, "./example.swift")
30 | XCTAssertEqual(fileSystemMock.copyFileArguments!.to.relativePath, "one.swift")
31 | XCTAssertTrue(fileSystemMock.copyFileArguments!.override)
32 | }
33 |
34 | func testGetsTemplateNameFromFile() throws {
35 | let fileSystemMock = FileSystemMock()
36 | templateFilesManager = TemplateFilesManager(storage: fileSystemMock)
37 |
38 | let name = try templateFilesManager.getTemplateNameFrom(url: URL(fileURLWithPath: "one.swift"))
39 |
40 | XCTAssertEqual(name, "example")
41 | }
42 |
43 |
44 | static var allTests = [
45 | ("testCopiesTemplate", testCopiesTemplate),
46 | ("testRestoresTemplate", testRestoresTemplate),
47 | ("testGetsTemplateNameFromFile", testGetsTemplateNameFromFile)
48 | ]
49 | }
50 |
51 | // MARK: - Mocks
52 |
53 | private final class FileSystemMock: FileSysteming {
54 | struct CopyFileArguments {
55 | let from: URL
56 | let to: URL
57 | let override: Bool
58 | }
59 | var copyFileArguments: CopyFileArguments?
60 |
61 | func saveFile(name: String, path: URL, content: String, overwrite: Bool) throws {
62 | fatalError()
63 | }
64 |
65 | func getFile(at: URL) throws -> String {
66 | podfileStub
67 | }
68 |
69 | func copyFile(from: URL, to: URL, override: Bool) throws {
70 | copyFileArguments = CopyFileArguments(from: from, to: to, override: override)
71 | }
72 |
73 | func delete(at path: String) throws {
74 | fatalError()
75 | }
76 |
77 | func createFolder(at url:URL) throws {
78 | fatalError()
79 | }
80 |
81 | func findFilesInFolder(at url: URL, matching: (URL) -> Bool) throws -> [URL] {
82 | []
83 | }
84 |
85 | func fileExists(at url: URL) -> Bool {
86 | false
87 | }
88 | }
89 |
90 | private let podfileStub = """
91 | import Foundation
92 | import SwiftyPodsDSL
93 |
94 | let example = Podfile(
95 | targets: [
96 | .target(
97 | name: "Target",
98 | project: "Project",
99 | dependencies: [
100 | .dependency(name: "Dependency1"),
101 | .dependency(name: "Dependency2",
102 | version: "1.2.3"),
103 | .dependency(name: "Dependency3",
104 | .git(url: "repo"),
105 | .branch(name: "master"))
106 | ],
107 | childTargets: [
108 | .target(name: "ChildTarget", project: "Project2")
109 | ]
110 | )
111 | ]
112 | )
113 | """
114 |
--------------------------------------------------------------------------------
/Tests/PackageBuilderTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(PackageBuilderTests.allTests),
7 | testCase(PackageManifestBuilderTests.allTests),
8 | testCase(TemplateFilesManagerTests.allTests)
9 | ]
10 | }
11 | #endif
12 |
--------------------------------------------------------------------------------
/Tests/PodfileBuilderTests/PodfileBuilderTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import class Foundation.Bundle
3 | @testable import PodfileBuilder
4 | import Storage
5 | import TemplateRenderer
6 |
7 | final class PodfileBuilderTests: XCTestCase {
8 | private var podfileBuilder: PodfileBuilder!
9 |
10 | func testSavesGeneratedEmptyPodfile() throws {
11 | let fileSystemMock = FileSystemMock()
12 | let templateRenderMock = TemplateRenderingMock()
13 | let defaultTemplate = "content{{pods}}"
14 | podfileBuilder = PodfileBuilder(templateRenderer: templateRenderMock, storage: fileSystemMock, defaultTemplate: defaultTemplate)
15 |
16 | try podfileBuilder.buildPodfile(podfiles: [], path: "path")
17 |
18 | XCTAssertNotNil(fileSystemMock.saveFileArguments)
19 | XCTAssertNotNil(templateRenderMock.renderArguments)
20 | XCTAssertEqual(fileSystemMock.saveFileArguments!.name, "podfile")
21 | XCTAssertEqual(templateRenderMock.renderArguments!.context["pods"], "")
22 | XCTAssertEqual(templateRenderMock.renderArguments!.template, defaultTemplate)
23 | }
24 |
25 | func testSavesGeneratedPodfileFromFile() throws {
26 | let fileSystemMock = FileSystemMock()
27 | let fileTemplate = "fileTemplate{{pods}}"
28 | fileSystemMock.getFile = fileTemplate
29 | let templateRenderMock = TemplateRenderingMock()
30 | let defaultTemplate = "content{{pods}}"
31 | podfileBuilder = PodfileBuilder(templateRenderer: templateRenderMock, storage: fileSystemMock, defaultTemplate: defaultTemplate)
32 |
33 | try podfileBuilder.buildPodfile(podfiles: [], path: "path", templatePath: "path")
34 |
35 | XCTAssertNotNil(fileSystemMock.saveFileArguments)
36 | XCTAssertNotNil(templateRenderMock.renderArguments)
37 | XCTAssertEqual(fileSystemMock.saveFileArguments!.name, "podfile")
38 | XCTAssertEqual(templateRenderMock.renderArguments!.context["pods"], "")
39 | XCTAssertEqual(templateRenderMock.renderArguments!.template, fileTemplate)
40 | }
41 |
42 | static var allTests = [
43 | ("testSavesGeneratedEmptyPodfile", testSavesGeneratedEmptyPodfile)
44 | ]
45 | }
46 |
47 | // MARK: - Mocks
48 |
49 | private final class FileSystemMock: FileSysteming {
50 | struct SaveFileArguments {
51 | let name: String
52 | let path: URL
53 | let content: String
54 | let overwrite: Bool
55 | }
56 | var saveFileArguments: SaveFileArguments?
57 |
58 | var getFile: String?
59 |
60 | func saveFile(name: String, path: URL, content: String, overwrite: Bool) throws {
61 | saveFileArguments = SaveFileArguments(name: name, path: path, content: content, overwrite: overwrite)
62 | }
63 |
64 | func getFile(at: URL) throws -> String {
65 | getFile ?? ""
66 | }
67 |
68 | func copyFile(from: URL, to: URL, override: Bool) throws {
69 | fatalError()
70 | }
71 |
72 | func delete(at path: String) throws {
73 | fatalError()
74 | }
75 |
76 | func createFolder(at url:URL) throws {
77 | fatalError()
78 | }
79 |
80 | func findFilesInFolder(at url: URL, matching: (URL) -> Bool) throws -> [URL] {
81 | []
82 | }
83 |
84 | func fileExists(at url: URL) -> Bool {
85 | false
86 | }
87 | }
88 |
89 | private final class TemplateRenderingMock: TemplateRendering {
90 | struct RenderArguments {
91 | let template: String
92 | let context: [String: String]
93 | }
94 | var renderArguments: RenderArguments?
95 |
96 | func render(
97 | template: String,
98 | context: [String: String]
99 | ) throws -> String {
100 | renderArguments = RenderArguments(template: template, context: context)
101 | return ""
102 | }
103 |
104 | func render(
105 | templateFile: URL,
106 | context: [String: String]
107 | ) throws -> String {
108 | ""
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Tests/PodfileBuilderTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(TemplateRendererTests.allTests)
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Tests/SwiftyPodsTests/SwiftyPodsTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import class Foundation.Bundle
3 |
4 | final class SwiftyPodsTests: XCTestCase {
5 | // func testExample() throws {
6 | // // This is an example of a functional test case.
7 | // // Use XCTAssert and related functions to verify your tests produce the correct
8 | // // results.
9 | //
10 | // // Some of the APIs that we use below are available in macOS 10.13 and above.
11 | // guard #available(macOS 10.13, *) else {
12 | // return
13 | // }
14 | //
15 | // let fooBinary = productsDirectory.appendingPathComponent("SwiftyPods")
16 | //
17 | // let process = Process()
18 | // process.executableURL = fooBinary
19 | //
20 | // let pipe = Pipe()
21 | // process.standardOutput = pipe
22 | //
23 | // try process.run()
24 | // process.waitUntilExit()
25 | //
26 | // let data = pipe.fileHandleForReading.readDataToEndOfFile()
27 | // let output = String(data: data, encoding: .utf8)
28 | //
29 | // XCTAssertEqual(output, "Hello, world!\n")
30 | // }
31 | //
32 | // /// Returns path to the built products directory.
33 | // var productsDirectory: URL {
34 | // #if os(macOS)
35 | // for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
36 | // return bundle.bundleURL.deletingLastPathComponent()
37 | // }
38 | // fatalError("couldn't find the products directory")
39 | // #else
40 | // return Bundle.main.bundleURL
41 | // #endif
42 | // }
43 | //
44 | // static var allTests = [
45 | // ("testExample", testExample),
46 | // ]
47 | }
48 |
--------------------------------------------------------------------------------
/Tests/SwiftyPodsTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(SwiftyPodsTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Tests/TemplateRendererTests/TemplateRendererTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import class Foundation.Bundle
3 | import TemplateRenderer
4 | import Storage
5 |
6 | final class TemplateRendererTests: XCTestCase {
7 | private var templateRenderer: TemplateRenderer!
8 |
9 | func testGeneratesfromString() throws {
10 | templateRenderer = TemplateRenderer(storage: FileSystemMock())
11 | let expectedTemplate = "Hello David"
12 | let generatedTemplate = try templateRenderer.render(template: "Hello {{name}}", context: ["name": "David"])
13 |
14 | XCTAssertEqual(generatedTemplate, expectedTemplate)
15 | }
16 |
17 | func testGeneratesfromFile() throws {
18 | let storageMock = FileSystemMock()
19 | storageMock.fileContent = "Hello {{name}}"
20 | templateRenderer = TemplateRenderer(storage: storageMock)
21 | let expectedTemplate = "Hello David"
22 | let generatedTemplate = try templateRenderer.render(templateFile: URL(fileURLWithPath: ""), context: ["name": "David"])
23 |
24 | XCTAssertEqual(generatedTemplate, expectedTemplate)
25 | }
26 |
27 | static var allTests = [
28 | ("testGeneratesfromString", testGeneratesfromString),
29 | ("testGeneratesfromFile", testGeneratesfromFile)
30 | ]
31 | }
32 |
33 | // MARK: - Mocks
34 |
35 | private final class FileSystemMock: FileSysteming {
36 | var fileContent: String?
37 |
38 | func saveFile(name: String, path: URL, content: String, overwrite: Bool) throws {
39 | fatalError()
40 | }
41 |
42 | func getFile(at: URL) throws -> String {
43 | if let fileContent = fileContent {
44 | return fileContent
45 | }
46 | fatalError()
47 | }
48 |
49 | func copyFile(from: URL, to: URL, override: Bool) throws {
50 | fatalError()
51 | }
52 |
53 | func delete(at path: String) throws {
54 | fatalError()
55 | }
56 |
57 | func createFolder(at url:URL) throws {
58 | fatalError()
59 | }
60 |
61 | func findFilesInFolder(at url: URL, matching: (URL) -> Bool) throws -> [URL] {
62 | []
63 | }
64 |
65 | func fileExists(at url: URL) -> Bool {
66 | false
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Tests/TemplateRendererTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(TemplateRendererTests.allTests)
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/testfolder/podfile.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import SwiftyPodsDSL
3 |
4 | let example = Podfile(
5 | targets: [
6 | .target(
7 | name: "Target",
8 | project: "Project",
9 | dependencies: [
10 | .dependency(name: "Dependency1"),
11 | .dependency(name: "Dependency2",
12 | version: "1.2.3"),
13 | .dependency(name: "Dependency3",
14 | .git(url: "repo"),
15 | .branch(name: "master"))
16 | ],
17 | childTargets: [
18 | .target(name: "ChildTarget", project: "Project2")
19 | ]
20 | )
21 | ]
22 | )
23 |
--------------------------------------------------------------------------------