diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/GeneratePackage.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/GeneratePackage.xcscheme
index e35be09..193f14c 100644
--- a/.swiftpm/xcode/xcshareddata/xcschemes/GeneratePackage.xcscheme
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/GeneratePackage.xcscheme
@@ -67,15 +67,15 @@
isEnabled = "YES">
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/GeneratePackages.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/GeneratePackages.xcscheme
deleted file mode 100644
index feb960f..0000000
--- a/.swiftpm/xcode/xcshareddata/xcschemes/GeneratePackages.xcscheme
+++ /dev/null
@@ -1,107 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Example/Config/RemoteDependencies.json b/Example/Config/Dependencies.json
similarity index 99%
rename from Example/Config/RemoteDependencies.json
rename to Example/Config/Dependencies.json
index bba5446..622c989 100644
--- a/Example/Config/RemoteDependencies.json
+++ b/Example/Config/Dependencies.json
@@ -21,4 +21,4 @@
"branch": "master"
}
]
-}
+}
\ No newline at end of file
diff --git a/Example/Config/Dependencies.yaml b/Example/Config/Dependencies.yaml
new file mode 100644
index 0000000..070a25e
--- /dev/null
+++ b/Example/Config/Dependencies.yaml
@@ -0,0 +1,13 @@
+dependencies:
+- name: RemoteDependencyA
+ url: https://github.com/DependencyA
+ version: 1.0.0
+- name: RemoteDependencyB
+ url: https://github.com/DependencyB
+ version: 2.0.0
+- name: RemoteDependencyC
+ url: https://github.com/DependencyC
+ revision: abcde1235kjh
+- name: RemoteDependencyD
+ url: https://github.com/DependencyC
+ branch: master
\ No newline at end of file
diff --git a/Example/Packages/Example/Example.json b/Example/Packages/Example.json
similarity index 99%
rename from Example/Packages/Example/Example.json
rename to Example/Packages/Example.json
index cfb6c60..21026ab 100755
--- a/Example/Packages/Example/Example.json
+++ b/Example/Packages/Example.json
@@ -69,4 +69,4 @@
"path": "../LocalXCFramework.xcframework"
}
]
-}
+}
\ No newline at end of file
diff --git a/Example/Packages/Example.yaml b/Example/Packages/Example.yaml
new file mode 100644
index 0000000..0ea54ea
--- /dev/null
+++ b/Example/Packages/Example.yaml
@@ -0,0 +1,38 @@
+name: Example
+platforms:
+ - ".iOS(.v15)"
+ - ".macOS(.v13)"
+swiftToolsVersion: '5.10'
+swiftLanguageVersions:
+ - '5.10'
+ - '6.0'
+products:
+ - productType: library
+ name: Example
+ targets:
+ - Example
+localDependencies:
+ - name: MyLocalFramework
+ path: "../MyFrameworks"
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ - name: RemoteDependencyC
+targets:
+ - name: Example
+ targetType: target
+ dependencies:
+ - name: RemoteDependencyA
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: UnitTests
+ targetType: testTarget
+ dependencies:
+ - name: Example
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
+localBinaryTargets:
+ - name: LocalXCFramework
+ path: "../LocalXCFramework.xcframework"
diff --git a/Example/Packages/Example/Package.swift b/Example/Packages/Example/Package.swift
deleted file mode 100644
index a6f61ee..0000000
--- a/Example/Packages/Example/Package.swift
+++ /dev/null
@@ -1,71 +0,0 @@
-// swift-tools-version: 5.7
-
-// This file was automatically generated by PackageGenerator and untracked
-// PLEASE DO NOT EDIT MANUALLY
-
-import PackageDescription
-
-let package = Package(
- name: "Example",
- defaultLocalization: "en",
- platforms: [
- .iOS(.v15),
- .macOS(.v13),
- ],
- products: [
- .library(
- name: "Example",
- targets: ["Example"]
- ),
- ],
- dependencies: [
- .package(
- path: "../MyFrameworks"
- ),
- .package(
- url: "https://github.com/DependencyA",
- exact: "1.0.0"
- ),
- .package(
- url: "https://github.com/DependencyB",
- exact: "2.0.0"
- ),
- .package(
- url: "https://github.com/DependencyC",
- revision: "abcde1235kjh"
- ),
- ],
- targets: [
- .target(
- name: "Example",
- dependencies: [
- .product(name: "RemoteDependencyA", package: "RemoteDependencyA"),
- .target(name: "LocalXCFramework"),
- ],
- path: "Framework/Sources",
- resources: [
- .process("Resources")
- ],
- plugins: [
- ]
- ),
- .testTarget(
- name: "UnitTests",
- dependencies: [
- .byName(name: "Example"),
- .product(name: "RemoteDependencyB", package: "RemoteDependencyB"),
- .target(name: "LocalXCFramework"),
- ],
- path: "Tests/Sources",
- resources: [
- .process("Resources")
- ],
- plugins: [
- ]
- ),
- .binaryTarget(
- name: "LocalXCFramework",
- path: "../LocalXCFramework.xcframework"
- ),
- ]
-)
\ No newline at end of file
diff --git a/Package.resolved b/Package.resolved
index b690223..353bc1c 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -71,6 +71,15 @@
"revision" : "9f39744e025c7d377987f30b03770805dcb0bcd1",
"version" : "1.1.4"
}
+ },
+ {
+ "identity" : "yams",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/jpsim/Yams.git",
+ "state" : {
+ "revision" : "3036ba9d69cf1fd04d433527bc339dc0dc75433d",
+ "version" : "5.1.3"
+ }
}
],
"version" : 2
diff --git a/Package.swift b/Package.swift
index 5ddc628..7798f00 100644
--- a/Package.swift
+++ b/Package.swift
@@ -12,7 +12,8 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.2"),
.package(url: "https://github.com/SwiftGen/StencilSwiftKit", from: "2.8.0"),
- .package(url: "https://github.com/JohnSundell/ShellOut", from: "2.3.0")
+ .package(url: "https://github.com/JohnSundell/ShellOut", from: "2.3.0"),
+ .package(url: "https://github.com/jpsim/Yams.git", from: "5.0.6")
],
targets: [
.executableTarget(
@@ -21,6 +22,7 @@ let package = Package(
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "ShellOut", package: "ShellOut"),
.product(name: "StencilSwiftKit", package: "StencilSwiftKit"),
+ .product(name: "Yams", package: "Yams")
],
path: "Sources"),
.testTarget(
@@ -28,7 +30,7 @@ let package = Package(
dependencies: ["PackageGenerator"],
path: "Tests",
resources: [
- .process("Resources")
+ .copy("Resources")
]
)
]
diff --git a/README.md b/README.md
index a65cf58..c5df1a9 100644
--- a/README.md
+++ b/README.md
@@ -2,54 +2,18 @@
![Build Status](https://github.com/justeattakeaway/PackageGenerator/actions/workflows/run_tests.yml/badge.svg?branch=main)
-A tool to generate `Package.swift` files using a custom DSL allowing version alignment of dependencies across packages.
+A CLI tool to generate `Package.swift` files using a custom DSL allowing version alignment of dependencies across packages.
## Usage
-`PackageGenerator` uses [ArgumentParser](https://github.com/apple/swift-argument-parser) and [Stencil](https://stencil.fuller.li/).
+`PackageGenerator` uses [ArgumentParser](https://github.com/apple/swift-argument-parser) and [Stencil](https://stencil.fuller.li/). The tool provides a single `generate-package` command requiring the following options:
-The command `generate-package` requires the following arguments:
+- `--spec`: Path to a package spec file (supported formats: json, yaml)
+- `--dependencies`: Path to a dependencies file (supported formats: json, yaml)
+- `--template`: Path to a template file (supported formats: stencil)
-- `path`: Path to the folder containing the packages.
-- `template-path`: Path to the Stencil template.
-- `dependencies-path`: Path to the `RemoteDependencies.json` file.
-
-`RemoteDependencies.json` should contain the list of remote dependencies used by your packages. E.g.
-
-```json
-{
- "dependencies": [
- {
- "name": "Alamofire",
- "url": "https://github.com/Alamofire/Alamofire",
- "version": "5.6.1"
- },
- {
- "name": "ViewInspector",
- "url": "https://github.com/nalexn/ViewInspector",
- "version": "0.9.2"
- },
- {
- "name": "ViewInspector",
- "url": "https://github.com/nalexn/ViewInspector",
- "version": "0.9.2"
- },
- {
- "name": "SnapshotTesting",
- "url": "https://github.com/pointfreeco/swift-snapshot-testing",
- "branch": "master"
- },
- {
- "name": "Fastlane",
- "url": "https://github.com/fastlane/fastlane.git",
- "revision": "2c4f29fe161c5998e30000f96d23384fd0eebe90"
- }
- ]
-}
-```
-
-Packages should be contained in respective folders inside a packages folder and provide a `.json` spec. E.g.
+Here are spec examples in both json and yaml:
```json
{
@@ -82,12 +46,9 @@ Packages should be contained in respective folders inside a packages folder and
"name": "ViewInspector",
"version": "1.2.3"
},
- {
- "name": "Fastlane"
- },
{
"name": "SnapshotTesting"
- },
+ }
],
"targets": [
{
@@ -96,9 +57,6 @@ Packages should be contained in respective folders inside a packages folder and
"dependencies": [
{
"name": "Alamofire"
- },
- {
- "name": "Fastlane"
}
],
"sourcesPath": "Framework/Sources",
@@ -126,19 +84,98 @@ Packages should be contained in respective folders inside a packages folder and
}
```
-> Note that `PackageGenerator` will automatically retrieve `url` & ( `version` || `branch` || `revision` ) values for `remoteDependencies` from the `RemoteDependencies.json` file. If you need to override those values, you can set them in the package spec.
+```yaml
+name: Example
+swiftToolsVersion: '5.10'
+swiftLanguageVersions:
+ - '5.10'
+ - '6.0'
+products:
+ - name: Example
+ productType: library
+ targets:
+ - ExampleTarget
+localDependencies:
+ - name: ExampleLocalDependency
+ path: "../LocalDependencies"
+remoteDependencies:
+ - name: Alamofire
+ - name: ViewInspector
+ version: 1.2.3
+ - name: SnapshotTesting
+targets:
+ - name: ExampleTarget
+ targetType: target
+ dependencies:
+ - name: Alamofire
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: ExampleTargetTests
+ targetType: testTarget
+ dependencies:
+ - name: ExampleTarget
+ isTarget: true
+ - name: ViewInspector
+ - name: SnapshotTesting
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
+```
+
+The dependencies file should contain the list of dependencies used by your package(s):
+
+```json
+{
+ "dependencies": [
+ {
+ "name": "Alamofire",
+ "url": "https://github.com/Alamofire/Alamofire",
+ "version": "5.6.1"
+ },
+ {
+ "name": "SnapshotTesting",
+ "url": "https://github.com/pointfreeco/swift-snapshot-testing",
+ "branch": "master"
+ },
+ {
+ "name": "ViewInspector",
+ "url": "https://github.com/nalexn/ViewInspector",
+ "revision": "23d6fabc6e8f0115c94ad3af5935300c70e0b7fa"
+ }
+ ]
+}
+```
+
+```yaml
+dependencies:
+ - name: Alamofire
+ url: https://github.com/Alamofire/Alamofire
+ version: 5.6.1
+ - name: SnapshotTesting
+ url: https://github.com/pointfreeco/swift-snapshot-testing
+ branch: master
+ - name: ViewInspector
+ url: https://github.com/nalexn/ViewInspector
+ revision: 23d6fabc6e8f0115c94ad3af5935300c70e0b7fa
+```
+
+> Note that `PackageGenerator` will automatically retrieve `url` & ( `version` || `branch` || `revision` ) values for the dependencies. If you need to override those values, you can set them in the package spec.
+
+We provide a default Stencil template we recommend using.
-We provide a default Stencil template that `PackageGenerator` can work with.
+Ideally, you want to use the `PackageGenerator` executable to automate tasks both locally and on CI.
-The command `generate-packages` allows you to generate Package.swift files from a given folder of packages.
-It takes the same arguments as `generate-package` along with `packages-folder-path`. `PackageGenerator` will loop though subfolders and generate Package.swift files from JSON specs.
+You can download a build from the [release page](https://github.com/justeattakeaway/PackageGenerator/releases) or, alternatively, build it from the source code:
+
+```bash
+swift build -c release --arch x86_64 --arch arm64
+```
-Ideally, you want to generate a `PackageGenerator` executable and automate tasks both locally and on CI.
+The executable should be generated at `.build/apple/Products/Release/PackageGenerator`.
## Demo
-In the `PackageGenerator` scheme, enable 'Use custom working directory' and set the value to the folder containing the `PackageGenerator` package.
+In the `GeneratorPackage` scheme, enable 'Use custom working directory' and set the value to the folder containing the `PackageGenerator` package.
The scheme has arguments set to showcase the creation of a `Package.swift` file using some provided files.
When running the default scheme you should see a `Package.swift` file being generated in the `Packages/Example/` folder.
diff --git a/Sources/Commands/GeneratePackage.swift b/Sources/Commands/GeneratePackage.swift
index 76bd44b..041d777 100644
--- a/Sources/Commands/GeneratePackage.swift
+++ b/Sources/Commands/GeneratePackage.swift
@@ -1,34 +1,38 @@
// GeneratePackage.swift
-import Foundation
import ArgumentParser
+import Foundation
struct GeneratePackage: ParsableCommand {
- static let configuration = CommandConfiguration(abstract: "Generate a Package.swift file from a JSON manifest.")
-
- @Option(name: .long, help: "Path to the folder containing the package.")
- private var path: String
-
- @Option(name: .long, help: "Path to the Stencil template.")
- private var templatePath: String
-
- @Option(name: .long, help: "Path to the RemoteDependencies.json file.")
- private var dependenciesPath: String
-
+ static let configuration = CommandConfiguration(abstract: "Generate a Package.swift file from a spec.")
+
+ @Option(name: .long, help: "Path to a package spec file (supported formats: json, yaml)")
+ private var spec: String
+
+ @Option(name: .long, help: "Path to s dependencies file (supported formats: json, yaml)")
+ private var dependencies: String
+
+ @Option(name: .long, help: "Path to a template file (supported formats: stencil)")
+ private var template: String
+
func run() throws {
- let packageFolderUrl = URL(fileURLWithPath: path, isDirectory: true)
- let packageFolderName = packageFolderUrl.lastPathComponent
- let dependenciesUrl = URL(fileURLWithPath: dependenciesPath, isDirectory: false)
- let specGenerator = SpecGenerator(dependenciesUrl: dependenciesUrl, packagesFolder: packageFolderUrl)
- let specUrl = packageFolderUrl.appendingPathComponent("\(packageFolderName).json")
- let spec = try specGenerator.makeSpec(for: packageFolderName, specUrl: specUrl)
- let path = try generatePackage(for: spec)
+ let content = try generatePackageContent()
+ let path = try write(content: content)
print("✅ File successfully saved at \(path).")
}
- private func generatePackage(for spec: Spec) throws -> Path {
- let templater = Templater(templatePath: templatePath)
- let content = try templater.renderTemplate(context: spec.makeContext())
- return try Writer().writePackageFile(content: content, to: path)
+ private func generatePackageContent() throws -> Content {
+ let specUrl = URL(fileURLWithPath: spec, isDirectory: false)
+ let dependenciesUrl = URL(fileURLWithPath: dependencies, isDirectory: false)
+ let specGenerator = SpecGenerator(specUrl: specUrl, dependenciesUrl: dependenciesUrl)
+ let spec = try specGenerator.makeSpec()
+ let templater = Templater(templatePath: template)
+ return try templater.renderTemplate(context: spec.makeContext())
+ }
+
+ private func write(content: Content) throws -> String {
+ let specUrl = URL(fileURLWithPath: spec, isDirectory: false)
+ let packageFolder = specUrl.deletingLastPathComponent()
+ return try Writer().writePackageFile(content: content, to: packageFolder)
}
}
diff --git a/Sources/Commands/GeneratePackages.swift b/Sources/Commands/GeneratePackages.swift
deleted file mode 100644
index c44448f..0000000
--- a/Sources/Commands/GeneratePackages.swift
+++ /dev/null
@@ -1,42 +0,0 @@
-// GeneratePackages.swift
-
-import Foundation
-import ArgumentParser
-
-struct GeneratePackages: ParsableCommand {
- static let configuration = CommandConfiguration(abstract: "Generate Package.swift files from a folder of packages.")
-
- @Option(name: .long, help: "Path to the folder containing the packages.")
- private var packagesFolderPath: String
-
- @Option(name: .long, help: "Path to the Stencil template.")
- private var templatePath: String
-
- @Option(name: .long, help: "Path to the RemoteDependencies.json file.")
- private var dependenciesPath: String
-
- func run() throws {
- let packagesFolderUrl = URL(fileURLWithPath: packagesFolderPath, isDirectory: true)
- let dependenciesUrl = URL(fileURLWithPath: dependenciesPath, isDirectory: false)
- let specGenerator = SpecGenerator(dependenciesUrl: dependenciesUrl, packagesFolder: packagesFolderUrl)
- let specs = try specGenerator.makeSpecs()
-
- let results: [String] = try specs.reduce(into: []) { partialResult, spec in
- let path = try generatePackage(for: spec)
- partialResult.append(path)
- }
-
- for result in results {
- print("✅ File successfully saved at \(result).")
- }
- }
-
- private func generatePackage(for spec: Spec) throws -> Path {
- let templater = Templater(templatePath: templatePath)
- let content = try templater.renderTemplate(context: spec.makeContext())
- let packagesFolderPath = URL(fileURLWithPath: packagesFolderPath, isDirectory: true)
- .appendingPathComponent(spec.name)
- .path
- return try Writer().writePackageFile(content: content, to: packagesFolderPath)
- }
-}
diff --git a/Sources/Core/SpecGenerator.swift b/Sources/Core/SpecGenerator.swift
index 70f3d64..15a1ce1 100644
--- a/Sources/Core/SpecGenerator.swift
+++ b/Sources/Core/SpecGenerator.swift
@@ -1,90 +1,88 @@
// SpecGenerator.swift
import Foundation
+import Yams
/// Class to generate Specs models that can be used to ultimately generate `Package.swift` files.
final class SpecGenerator {
- let decoder = JSONDecoder()
+ enum GeneratorError: Error {
+ case invalidFormat(String)
+ }
+
+ let specUrl: URL
let dependenciesUrl: URL
- let packagesFolder: URL
/// The default initializer.
///
/// - Parameters:
- /// - dependenciesUrl: The path to the RemoteDependencies.json file.
- /// - packagesFolder: The path to the folder containing the packages.
- init(dependenciesUrl: URL, packagesFolder: URL) {
+ /// - packagesFolder: Path to the package spec.
+ /// - dependenciesUrl: Path to the dependencies file.
+ init(specUrl: URL, dependenciesUrl: URL) {
+ self.specUrl = specUrl
self.dependenciesUrl = dependenciesUrl
- self.packagesFolder = packagesFolder
}
/// Generate a Spec model for a given package.
///
- /// - Parameter packageName: The name of the package to generate a Spec for.
/// - Returns: A Spec model.
- func makeSpec(for packageName: PackageName, specUrl: URL) throws -> Spec {
- try makeSpec(specUrl: specUrl)
- }
-
- /// Generate Spec models for all packages.
- ///
- /// - Returns: An array of Spec models.
- func makeSpecs() throws -> [Spec] {
- try specURLs().map(makeSpec)
- }
-
- private func makeSpec(specUrl: URL) throws -> Spec {
- let dependenciesData = try Data(contentsOf: dependenciesUrl)
- let specData = try Data(contentsOf: specUrl)
+ func makeSpec() throws -> Spec {
+ let spec: Spec = try decodeModel(from: specUrl)
+ let dependencies: Dependencies = try decodeModel(from: dependenciesUrl)
- let partialSpec = try decoder.decode(Spec.self, from: specData)
- let dependencies = try decoder.decode(Dependencies.self, from: dependenciesData).dependencies
+ let mappedDependencies: [Spec.RemoteDependency] = spec.remoteDependencies
+ .compactMap { remoteDependency -> Spec.RemoteDependency? in
+ guard let dependency = dependencies.dependencies.first(where: {
+ $0.name == remoteDependency.name
+ }) else {
+ return nil
+ }
+ return Spec.RemoteDependency(
+ name: dependency.name,
+ url: remoteDependency.url ?? dependency.url,
+ version: remoteDependency.version ?? dependency.version,
+ revision: remoteDependency.revision ?? dependency.revision,
+ branch: remoteDependency.branch ?? dependency.branch
+ )
+ }
- let mappedDependencies: [RemoteDependency] = partialSpec.remoteDependencies.compactMap { remoteDependency -> RemoteDependency? in
- guard let dependency = dependencies.first(where: { $0.name == remoteDependency.name }) else { return nil }
- return RemoteDependency(name: dependency.name,
- url: remoteDependency.url ?? dependency.url,
- version: remoteDependency.version ?? dependency.version,
- revision: remoteDependency.revision ?? dependency.revision,
- branch: remoteDependency.branch ?? dependency.branch)
- }
-
- return Spec(name: partialSpec.name,
- platforms: partialSpec.platforms,
- localDependencies: partialSpec.localDependencies,
- remoteDependencies: mappedDependencies,
- products: partialSpec.products,
- targets: partialSpec.targets,
- localBinaryTargets: partialSpec.localBinaryTargets,
- remoteBinaryTargets: partialSpec.remoteBinaryTargets,
- swiftToolsVersion: partialSpec.swiftToolsVersion,
- swiftLanguageVersions: partialSpec.swiftLanguageVersions)
- }
-
- private func specURL(for packageName: PackageName) -> URL {
- packagesFolder.appendingPathComponent("\(packageName)/\(packageName).json")
+ return Spec(
+ name: spec.name,
+ platforms: spec.platforms,
+ localDependencies: spec.localDependencies,
+ remoteDependencies: mappedDependencies,
+ products: spec.products,
+ targets: spec.targets,
+ localBinaryTargets: spec.localBinaryTargets,
+ remoteBinaryTargets: spec.remoteBinaryTargets,
+ swiftToolsVersion: spec.swiftToolsVersion,
+ swiftLanguageVersions: spec.swiftLanguageVersions
+ )
}
- private func specURLs() throws -> [URL] {
- let fileManager = FileManager.default
- let contentURLs = try fileManager.contentsOfDirectory(at: packagesFolder,
- includingPropertiesForKeys: [.nameKey],
- options: .skipsHiddenFiles)
- return contentURLs.map { item in
- let packageName = item.lastPathComponent
- return item.appendingPathComponent("\(packageName).json")
- }
- .filter { specUrl in
- fileManager.fileExists(atPath: specUrl.path)
+ private func decodeModel(from url: URL) throws -> T {
+ let specData = try Data(contentsOf: url)
+ switch url.pathExtension {
+ case "json":
+ let decoder = JSONDecoder()
+ return try decoder.decode(T.self, from: specData)
+ case "yaml", "yml":
+ let decoder = YAMLDecoder()
+ return try decoder.decode(T.self, from: specData)
+ default:
+ throw GeneratorError.invalidFormat(url.pathExtension)
}
- .sorted()
}
}
+// move to other file
+
extension URL: Comparable {
- public static func < (lhs: URL, rhs: URL) -> Bool {
+ public static func < (
+ lhs: URL,
+ rhs: URL
+ ) -> Bool {
lhs.path < rhs.path
}
}
diff --git a/Sources/Core/Writer.swift b/Sources/Core/Writer.swift
index ffed2d3..cb0c561 100644
--- a/Sources/Core/Writer.swift
+++ b/Sources/Core/Writer.swift
@@ -15,8 +15,8 @@ final class Writer {
/// - packageFolder: The path to the folder containing the package.
/// - Returns: The path of the saved `Package.swift` file.
@discardableResult
- func writePackageFile(content: String, to packageFolder: String) throws -> Path {
- let url = URL(fileURLWithPath: packageFolder).appendingPathComponent("Package.swift")
+ func writePackageFile(content: String, to packageFolder: URL) throws -> Path {
+ let url = packageFolder.appendingPathComponent("Package.swift")
try content.write(to: url, atomically: true, encoding: .utf8)
try shellOut(to: "chmod 444", arguments: [url.path])
return url.path
diff --git a/Sources/Models/Spec.swift b/Sources/Models/Spec.swift
index 762aeee..f6f5485 100644
--- a/Sources/Models/Spec.swift
+++ b/Sources/Models/Spec.swift
@@ -5,6 +5,149 @@ import Foundation
public typealias PackageName = String
struct Spec: Decodable {
+
+ struct Product: Decodable {
+ let name: String
+ let productType: String
+ let targets: [String]
+
+ enum CodingKeys: CodingKey {
+ case name
+ case productType
+ case targets
+ }
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ self.name = try container.decode(String.self, forKey: .name)
+ self.productType = try container.decode(ProductType.self, forKey: .productType).rawValue
+ self.targets = try container.decode([String].self, forKey: .targets)
+ }
+ }
+
+ enum ProductType: String, Decodable {
+ case library
+ case executable
+ case plugin
+ }
+
+ struct LocalDependency: Decodable {
+ let name: String
+ let path: String
+ }
+
+ struct RemoteDependency: Decodable {
+ let name: String
+ let url: String?
+ let version: String?
+ let revision: String?
+ let branch: String?
+
+ enum CodingKeys: CodingKey {
+ case name
+ case url
+ case version
+ case revision
+ case branch
+ }
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ self.name = try container.decode(String.self, forKey: .name)
+ self.url = try container.decodeIfPresent(String.self, forKey: .url)
+ self.version = try container.decodeIfPresent(String.self, forKey: .version)
+ self.revision = try container.decodeIfPresent(String.self, forKey: .revision)
+ self.branch = try container.decodeIfPresent(String.self, forKey: .branch)
+ }
+
+ init(name: String, url: String, version: String?, revision: String?, branch: String?) {
+ guard version != nil || revision != nil || branch != nil else {
+ fatalError("You need to provide at least one of the following: version, revision or branch")
+ }
+
+ self.name = name
+ self.url = url
+ self.version = version
+ self.revision = revision
+ self.branch = branch
+ }
+ }
+
+ enum TargetType: String, Decodable {
+ case target
+ case testTarget
+ case executableTarget
+ case plugin
+ }
+
+ struct Target: Decodable {
+ let targetType: String
+ let name: String
+ let dependencies: [TargetDependency]
+ let sourcesPath: String
+ let resourcesPath: String?
+ let exclude: [String]?
+ let swiftSettings: [String]?
+ let cSettings: [String]?
+ let cxxSettings: [String]?
+ let linkerSettings: [String]?
+ let publicHeadersPath: String?
+ let plugins: [Plugin]?
+
+ enum CodingKeys: CodingKey {
+ case targetType
+ case name
+ case dependencies
+ case sourcesPath
+ case resourcesPath
+ case exclude
+ case swiftSettings
+ case cSettings
+ case cxxSettings
+ case linkerSettings
+ case publicHeadersPath
+ case plugins
+ }
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ self.targetType = try container.decode(TargetType.self, forKey: .targetType).rawValue
+ self.name = try container.decode(String.self, forKey: .name)
+ self.dependencies = try container.decode([TargetDependency].self, forKey: .dependencies)
+ self.sourcesPath = try container.decode(String.self, forKey: .sourcesPath)
+ self.resourcesPath = try container.decodeIfPresent(String.self, forKey: .resourcesPath)
+ self.exclude = try container.decodeIfPresent([String].self, forKey: .exclude)
+ self.swiftSettings = try container.decodeIfPresent([String].self, forKey: .swiftSettings)
+ self.cSettings = try container.decodeIfPresent([String].self, forKey: .cSettings)
+ self.cxxSettings = try container.decodeIfPresent([String].self, forKey: .cxxSettings)
+ self.linkerSettings = try container.decodeIfPresent([String].self, forKey: .linkerSettings)
+ self.publicHeadersPath = try container.decodeIfPresent(String.self, forKey: .publicHeadersPath)
+ self.plugins = try container.decodeIfPresent([Plugin].self, forKey: .plugins)
+ }
+ }
+
+ struct Plugin: Decodable {
+ let name: String
+ let package: String?
+ }
+
+ struct TargetDependency: Decodable {
+ let name: String
+ let package: String?
+ let isTarget: Bool?
+ }
+
+ struct LocalBinaryTarget: Decodable {
+ let name: String
+ let path: String
+ }
+
+ struct RemoteBinaryTarget: Decodable {
+ let name: String
+ let url: String
+ let checksum: String
+ }
+
let name: PackageName
let swiftToolsVersion: String?
let platforms: [String]?
@@ -56,145 +199,3 @@ extension Spec {
return values.compactMapValues { $0 }
}
}
-
-struct Product: Decodable {
- let name: String
- let productType: String
- let targets: [String]
-
- enum CodingKeys: CodingKey {
- case name
- case productType
- case targets
- }
-
- init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- self.name = try container.decode(String.self, forKey: .name)
- self.productType = try container.decode(ProductType.self, forKey: .productType).rawValue
- self.targets = try container.decode([String].self, forKey: .targets)
- }
-}
-
-enum ProductType: String, Decodable {
- case library
- case executable
- case plugin
-}
-
-struct LocalDependency: Decodable {
- let name: String
- let path: String
-}
-
-struct RemoteDependency: Decodable {
- let name: String
- let url: String?
- let version: String?
- let revision: String?
- let branch: String?
-
- enum CodingKeys: CodingKey {
- case name
- case url
- case version
- case revision
- case branch
- }
-
- init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- self.name = try container.decode(String.self, forKey: .name)
- self.url = try container.decodeIfPresent(String.self, forKey: .url)
- self.version = try container.decodeIfPresent(String.self, forKey: .version)
- self.revision = try container.decodeIfPresent(String.self, forKey: .revision)
- self.branch = try container.decodeIfPresent(String.self, forKey: .branch)
- }
-
- init(name: String, url: String, version: String?, revision: String?, branch: String?) {
- guard version != nil || revision != nil || branch != nil else {
- fatalError("You need to provide at least one of the following: version, revision or branch")
- }
-
- self.name = name
- self.url = url
- self.version = version
- self.revision = revision
- self.branch = branch
- }
-}
-
-enum TargetType: String, Decodable {
- case target
- case testTarget
- case executableTarget
- case plugin
-}
-
-struct Target: Decodable {
- let targetType: String
- let name: String
- let dependencies: [TargetDependency]
- let sourcesPath: String
- let resourcesPath: String?
- let exclude: [String]?
- let swiftSettings: [String]?
- let cSettings: [String]?
- let cxxSettings: [String]?
- let linkerSettings: [String]?
- let publicHeadersPath: String?
- let plugins: [Plugin]?
-
- enum CodingKeys: CodingKey {
- case targetType
- case name
- case dependencies
- case sourcesPath
- case resourcesPath
- case exclude
- case swiftSettings
- case cSettings
- case cxxSettings
- case linkerSettings
- case publicHeadersPath
- case plugins
- }
-
- init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- self.targetType = try container.decode(TargetType.self, forKey: .targetType).rawValue
- self.name = try container.decode(String.self, forKey: .name)
- self.dependencies = try container.decode([TargetDependency].self, forKey: .dependencies)
- self.sourcesPath = try container.decode(String.self, forKey: .sourcesPath)
- self.resourcesPath = try container.decodeIfPresent(String.self, forKey: .resourcesPath)
- self.exclude = try container.decodeIfPresent([String].self, forKey: .exclude)
- self.swiftSettings = try container.decodeIfPresent([String].self, forKey: .swiftSettings)
- self.cSettings = try container.decodeIfPresent([String].self, forKey: .cSettings)
- self.cxxSettings = try container.decodeIfPresent([String].self, forKey: .cxxSettings)
- self.linkerSettings = try container.decodeIfPresent([String].self, forKey: .linkerSettings)
- self.publicHeadersPath = try container.decodeIfPresent(String.self, forKey: .publicHeadersPath)
- self.plugins = try container.decodeIfPresent([Plugin].self, forKey: .plugins)
- }
-}
-
-struct Plugin: Decodable {
- let name: String
- let package: String?
-}
-
-struct TargetDependency: Decodable {
- let name: String
- let package: String?
- let isTarget: Bool?
-}
-
-struct LocalBinaryTarget: Decodable {
- let name: String
- let path: String
-}
-
-struct RemoteBinaryTarget: Decodable {
- let name: String
- let url: String
- let checksum: String
-}
diff --git a/Sources/PackageGenerator.swift b/Sources/PackageGenerator.swift
index 402633b..aa91bef 100644
--- a/Sources/PackageGenerator.swift
+++ b/Sources/PackageGenerator.swift
@@ -7,7 +7,6 @@ import ArgumentParser
struct PackageGenerator: AsyncParsableCommand {
static let configuration = CommandConfiguration(
subcommands: [
- GeneratePackage.self,
- GeneratePackages.self
+ GeneratePackage.self
])
}
diff --git a/Tests/PackageGeneratorTests.swift b/Tests/PackageGeneratorTests.swift
index 62efdf9..c576da9 100644
--- a/Tests/PackageGeneratorTests.swift
+++ b/Tests/PackageGeneratorTests.swift
@@ -22,7 +22,7 @@ final class PackageGeneratorTests: XCTestCase {
.appendingPathComponent("Resources")
lazy var packagesFolderUrl = resourcesFolder.appendingPathComponent("Packages")
- lazy var dependenciesUrl = resourcesFolder.appendingPathComponent("TestRemoteDependencies.json")
+ lazy var dependenciesFilename = "TestDependencies"
lazy var templatePath = resourcesFolder.appendingPathComponent("Package.stencil")
func test_SingleProduct() throws {
@@ -62,25 +62,30 @@ final class PackageGeneratorTests: XCTestCase {
}
private func assertPackage(for packageType: PackageType) throws {
- let packageSpecUrl = resourcesFolder
- .appendingPathComponent("Packages")
- .appendingPathComponent(packageType.rawValue)
- .appendingPathComponent(packageType.rawValue)
- .appendingPathExtension("json")
-
- let packageUrl = resourcesFolder
- .appendingPathComponent("Packages")
- .appendingPathComponent(packageType.rawValue)
- .appendingPathComponent("\(packageType.rawValue)Package")
- .appendingPathExtension("swift")
-
- let specGenerator = SpecGenerator(dependenciesUrl: dependenciesUrl, packagesFolder: packagesFolderUrl)
- let spec = try specGenerator.makeSpec(for: packageType.rawValue, specUrl: packageSpecUrl)
- let templater = Templater(templatePath: templatePath.absoluteString)
- let packageContent = try templater.renderTemplate(context: spec.makeContext())
-
- let expectedPackageContent = try String(contentsOf: packageUrl)
-
- XCTAssertEqual(packageContent, expectedPackageContent)
+ for `extension` in ["json", "yml"] {
+ let specUrl = resourcesFolder
+ .appendingPathComponent("Packages")
+ .appendingPathComponent(packageType.rawValue)
+ .appendingPathComponent(packageType.rawValue)
+ .appendingPathExtension(`extension`)
+
+ let packageUrl = resourcesFolder
+ .appendingPathComponent("Packages")
+ .appendingPathComponent(packageType.rawValue)
+ .appendingPathComponent("Package")
+ .appendingPathExtension("swift")
+
+ let dependenciesUrl = resourcesFolder
+ .appendingPathComponent(dependenciesFilename)
+ .appendingPathExtension("yml")
+ let specGenerator = SpecGenerator(specUrl: specUrl, dependenciesUrl: dependenciesUrl)
+ let spec = try specGenerator.makeSpec()
+ let templater = Templater(templatePath: templatePath.absoluteString)
+ let packageContent = try templater.renderTemplate(context: spec.makeContext())
+
+ let expectedPackageContent = try String(contentsOf: packageUrl)
+
+ XCTAssertEqual(packageContent, expectedPackageContent)
+ }
}
}
diff --git a/Tests/Resources/Packages/BranchProduct/BranchProduct.yml b/Tests/Resources/Packages/BranchProduct/BranchProduct.yml
new file mode 100755
index 0000000..d78d6ba
--- /dev/null
+++ b/Tests/Resources/Packages/BranchProduct/BranchProduct.yml
@@ -0,0 +1,31 @@
+name: BranchProduct
+products:
+ - name: BranchProduct
+ productType: library
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ - name: RemoteDependencyD
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ - name: RemoteDependencyD
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
\ No newline at end of file
diff --git a/Tests/Resources/Packages/BranchProduct/BranchProductPackage.swift b/Tests/Resources/Packages/BranchProduct/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/BranchProduct/BranchProductPackage.swift
rename to Tests/Resources/Packages/BranchProduct/Package.swift
diff --git a/Tests/Resources/Packages/ComplexTargets/ComplexTargets.yml b/Tests/Resources/Packages/ComplexTargets/ComplexTargets.yml
new file mode 100755
index 0000000..cc37dfb
--- /dev/null
+++ b/Tests/Resources/Packages/ComplexTargets/ComplexTargets.yml
@@ -0,0 +1,43 @@
+name: ComplexTarget
+products:
+ - name: ComplexTarget
+ productType: library
+ targets:
+ - TargetA
+ - LocalBinaryTarget
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ exclude:
+ - /path1
+ - /path1/path2
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ swiftSettings:
+ - .define("setting")
+ - '.unsafeFlags(["flag"])'
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
+localBinaryTargets:
+ - name: LocalBinaryTarget
+ path: path/LocalBinaryTarget
+remoteBinaryTargets:
+ - name: RemoteBinaryTarget
+ url: 'https://github.com/RemoteBinaryTarget.zip'
+ checksum: '12345'
\ No newline at end of file
diff --git a/Tests/Resources/Packages/ComplexTargets/ComplexTargetsPackage.swift b/Tests/Resources/Packages/ComplexTargets/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/ComplexTargets/ComplexTargetsPackage.swift
rename to Tests/Resources/Packages/ComplexTargets/Package.swift
diff --git a/Tests/Resources/Packages/CustomPlatforms/CustomPlatforms.yml b/Tests/Resources/Packages/CustomPlatforms/CustomPlatforms.yml
new file mode 100755
index 0000000..2a7acb6
--- /dev/null
+++ b/Tests/Resources/Packages/CustomPlatforms/CustomPlatforms.yml
@@ -0,0 +1,32 @@
+name: CustomPlatforms
+platforms:
+ - .iOS(.v16)
+ - .macOS(.v12)
+products:
+ - name: CustomPlatforms
+ productType: library
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
\ No newline at end of file
diff --git a/Tests/Resources/Packages/CustomPlatforms/CustomPlatformsPackage.swift b/Tests/Resources/Packages/CustomPlatforms/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/CustomPlatforms/CustomPlatformsPackage.swift
rename to Tests/Resources/Packages/CustomPlatforms/Package.swift
diff --git a/Tests/Resources/Packages/DependencyOverride/DependencyOverride.yml b/Tests/Resources/Packages/DependencyOverride/DependencyOverride.yml
new file mode 100755
index 0000000..2259378
--- /dev/null
+++ b/Tests/Resources/Packages/DependencyOverride/DependencyOverride.yml
@@ -0,0 +1,30 @@
+name: DependencyOverride
+products:
+ - name: DependencyOverride
+ productType: library
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ version: 3.0.0
+ - name: RemoteDependencyB
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
\ No newline at end of file
diff --git a/Tests/Resources/Packages/DependencyOverride/DependencyOverridePackage.swift b/Tests/Resources/Packages/DependencyOverride/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/DependencyOverride/DependencyOverridePackage.swift
rename to Tests/Resources/Packages/DependencyOverride/Package.swift
diff --git a/Tests/Resources/Packages/ExecutableProduct/ExecutableProduct.yml b/Tests/Resources/Packages/ExecutableProduct/ExecutableProduct.yml
new file mode 100755
index 0000000..039278f
--- /dev/null
+++ b/Tests/Resources/Packages/ExecutableProduct/ExecutableProduct.yml
@@ -0,0 +1,29 @@
+name: ExcutableProduct
+products:
+ - name: ExcutableProduct
+ productType: executable
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
\ No newline at end of file
diff --git a/Tests/Resources/Packages/ExecutableProduct/ExecutableProductPackage.swift b/Tests/Resources/Packages/ExecutableProduct/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/ExecutableProduct/ExecutableProductPackage.swift
rename to Tests/Resources/Packages/ExecutableProduct/Package.swift
diff --git a/Tests/Resources/Packages/MultipleProducts/MultipleProducts.yml b/Tests/Resources/Packages/MultipleProducts/MultipleProducts.yml
new file mode 100755
index 0000000..c50d988
--- /dev/null
+++ b/Tests/Resources/Packages/MultipleProducts/MultipleProducts.yml
@@ -0,0 +1,33 @@
+name: MultipleProducts
+products:
+ - name: ProductA
+ productType: library
+ targets:
+ - TargetA
+ - name: ProductA
+ productType: library
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
\ No newline at end of file
diff --git a/Tests/Resources/Packages/MultipleProducts/MultipleProductsPackage.swift b/Tests/Resources/Packages/MultipleProducts/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/MultipleProducts/MultipleProductsPackage.swift
rename to Tests/Resources/Packages/MultipleProducts/Package.swift
diff --git a/Tests/Resources/Packages/PluginProduct/PluginProductPackage.swift b/Tests/Resources/Packages/PluginProduct/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/PluginProduct/PluginProductPackage.swift
rename to Tests/Resources/Packages/PluginProduct/Package.swift
diff --git a/Tests/Resources/Packages/PluginProduct/PluginProduct.yml b/Tests/Resources/Packages/PluginProduct/PluginProduct.yml
new file mode 100755
index 0000000..13d7dce
--- /dev/null
+++ b/Tests/Resources/Packages/PluginProduct/PluginProduct.yml
@@ -0,0 +1,38 @@
+name: SingleProduct
+products:
+ - name: Plugin
+ productType: plugin
+ targets:
+ - TargetA
+ - name: Library
+ productType: library
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ plugins:
+ - name: Plugin
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
+ plugins:
+ - name: Plugin
+ package: PluginTest
\ No newline at end of file
diff --git a/Tests/Resources/Packages/RevisionProduct/RevisionProductPackage.swift b/Tests/Resources/Packages/RevisionProduct/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/RevisionProduct/RevisionProductPackage.swift
rename to Tests/Resources/Packages/RevisionProduct/Package.swift
diff --git a/Tests/Resources/Packages/RevisionProduct/RevisionProduct.yml b/Tests/Resources/Packages/RevisionProduct/RevisionProduct.yml
new file mode 100755
index 0000000..57eb6d2
--- /dev/null
+++ b/Tests/Resources/Packages/RevisionProduct/RevisionProduct.yml
@@ -0,0 +1,31 @@
+name: RevisionProduct
+products:
+ - name: RevisionProduct
+ productType: library
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ - name: RemoteDependencyC
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ - name: RemoteDependencyC
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
\ No newline at end of file
diff --git a/Tests/Resources/Packages/SingleProduct/SingleProductPackage.swift b/Tests/Resources/Packages/SingleProduct/Package.swift
similarity index 100%
rename from Tests/Resources/Packages/SingleProduct/SingleProductPackage.swift
rename to Tests/Resources/Packages/SingleProduct/Package.swift
diff --git a/Tests/Resources/Packages/SingleProduct/SingleProduct.yml b/Tests/Resources/Packages/SingleProduct/SingleProduct.yml
new file mode 100755
index 0000000..6461548
--- /dev/null
+++ b/Tests/Resources/Packages/SingleProduct/SingleProduct.yml
@@ -0,0 +1,29 @@
+name: SingleProduct
+products:
+ - name: SingleProduct
+ productType: library
+ targets:
+ - TargetA
+localDependencies:
+ - name: LocalDependencyA
+ path: ../LocalDependencies
+remoteDependencies:
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+targets:
+ - name: TargetA
+ targetType: target
+ dependencies:
+ - name: LocalDependencyA
+ - name: RemoteDependencyA
+ - name: RemoteDependencyB
+ sourcesPath: Framework/Sources
+ resourcesPath: Resources
+ - name: TargetATests
+ targetType: testTarget
+ dependencies:
+ - name: TargetA
+ isTarget: true
+ - name: RemoteDependencyB
+ sourcesPath: Tests/Sources
+ resourcesPath: Resources
\ No newline at end of file
diff --git a/Tests/Resources/TestRemoteDependencies.json b/Tests/Resources/TestDependencies.json
similarity index 100%
rename from Tests/Resources/TestRemoteDependencies.json
rename to Tests/Resources/TestDependencies.json
diff --git a/Tests/Resources/TestDependencies.yml b/Tests/Resources/TestDependencies.yml
new file mode 100644
index 0000000..cf5a4b6
--- /dev/null
+++ b/Tests/Resources/TestDependencies.yml
@@ -0,0 +1,13 @@
+dependencies:
+ - name: RemoteDependencyA
+ url: https://github.com/DependencyA
+ version: 1.0.0
+ - name: RemoteDependencyB
+ url: https://github.com/DependencyB
+ version: 2.0.0
+ - name: RemoteDependencyC
+ url: https://github.com/DependencyC
+ revision: abcde1235kjh
+ - name: RemoteDependencyD
+ url: https://github.com/DependencyD
+ branch: master
\ No newline at end of file