From 76f24a41f47438a14f0599462397c44b2b18b37f Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Fri, 15 Jun 2018 18:01:02 -0500 Subject: [PATCH 1/9] swift package update --- Package.resolved | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Package.resolved b/Package.resolved index 3935e71..7f01349 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,15 +1,6 @@ { "object": { "pins": [ - { - "package": "Console", - "repositoryURL": "https://github.com/vapor/console.git", - "state": { - "branch": null, - "revision": "5b9796d39f201b3dd06800437abd9d774a455e57", - "version": "3.0.2" - } - }, { "package": "Core", "repositoryURL": "https://github.com/vapor/core.git", @@ -73,15 +64,6 @@ "version": "3.0.1" } }, - { - "package": "Service", - "repositoryURL": "https://github.com/vapor/service.git", - "state": { - "branch": null, - "revision": "281a70b69783891900be31a9e70051b6fe19e146", - "version": "1.0.0" - } - }, { "package": "swift-nio", "repositoryURL": "https://github.com/apple/swift-nio.git", From 484a12480436e8df8202302e04d83678720ab3ec Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 23 Jun 2018 09:26:59 -0500 Subject: [PATCH 2/9] Reset manifest in created package directory instead of current directory --- Sources/Ether/New.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/Ether/New.swift b/Sources/Ether/New.swift index cec15f0..3d9b85f 100644 --- a/Sources/Ether/New.swift +++ b/Sources/Ether/New.swift @@ -60,7 +60,8 @@ public final class New: Command { let script = "mkdir \(name); cd \(name); swift package init --type=executable" _ = try Process.execute("bash", ["-c", script]) - try Manifest.current.reset() + let currentDir = try Process.execute("pwd") + try Manifest(path: "file:\(currentDir)/\(name)/Package.swift").reset() return true } return false @@ -88,6 +89,8 @@ public final class New: Command { let name = try context.argument("name") let script = "mkdir \(name); cd \(name); swift package init" _ = try Process.execute("bash", ["-c", script]) - try Manifest.current.reset() + + let currentDir = try Process.execute("pwd") + try Manifest(path: "file:\(currentDir)/\(name)/Package.swift").reset() } } From f583883ba8ee28a66c29994f4c3b7914e86d12e2 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 23 Jun 2018 09:52:53 -0500 Subject: [PATCH 3/9] Added --print flag to config command to output config key value --- Sources/Ether/Configuration.swift | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Sources/Ether/Configuration.swift b/Sources/Ether/Configuration.swift index b311206..3063c26 100644 --- a/Sources/Ether/Configuration.swift +++ b/Sources/Ether/Configuration.swift @@ -40,7 +40,9 @@ public class Configuration: Command { CommandArgument.argument(name: "value", help: ["The new value for the key passed in. If no value is passed in, the key will be removed from the config"]) ] - public var options: [CommandOption] = [] + public var options: [CommandOption] = [ + CommandOption.flag(name: "print", short: "p", help: ["Outputs config key value. 'value' argument must have a value passed in, but it will not be used."]) + ] public var help: [String] = ["Configure custom actions to occure when a command is run"] @@ -48,23 +50,33 @@ public class Configuration: Command { public func run(using context: CommandContext) throws -> EventLoopFuture { let setter = context.console.loadingBar(title: "Setting Configuration Key") - _ = setter.start(on: context.container) + let shouldPrint = context.options["print"] != nil let key = try context.argument("key") let value = context.arguments["value"] let user = try Process.execute("whoami") + if !shouldPrint { + _ = setter.start(on: context.container) + } + var configuration = try Configuration.get() guard let property = Config.properties[key] else { throw EtherError(identifier: "noSettingWithName", reason: "No configuration setting found with name '\(key)'") } - configuration[keyPath: property] = value + if shouldPrint { + context.console.print(configuration[keyPath: property] ?? "nil") + } else { + configuration[keyPath: property] = value + } try JSONEncoder().encode(configuration).write(to: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether/config.json")!) - setter.succeed() + if !shouldPrint { + setter.succeed() + } return context.container.eventLoop.newSucceededFuture(result: ()) } From a03de894bea2029dcc82454219dcc76f96ae392d Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 11 Aug 2018 12:13:12 -0500 Subject: [PATCH 4/9] swift package update --- Package.resolved | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Package.resolved b/Package.resolved index 7f01349..45a9d7d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/vapor/core.git", "state": { "branch": null, - "revision": "e5582911ed940289212a36321e15ec561d947dbe", - "version": "3.3.0" + "revision": "7f56a09995bf3c8df562be456bdcda405d9d0678", + "version": "3.4.1" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/vapor/database-kit.git", "state": { "branch": null, - "revision": "4ac3e7437f9b95ce32b4d970bafa590bb3579647", - "version": "1.1.0" + "revision": "7a01659316b9f033fa2150d5cd5e9d3c3e46c2e3", + "version": "1.3.0" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/Ether-CLI/Manifest.git", "state": { "branch": null, - "revision": "a200f9a6af998f787ac4ea7b92802f7b139d499f", - "version": "0.4.5" + "revision": "21cf15c2ce50a630aed3a7d353c77d3a6dc953b1", + "version": "0.4.6" } }, { @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/vapor/multipart.git", "state": { "branch": null, - "revision": "7778dcb62f3efa845e8e2808937bb347575ba7ce", - "version": "3.0.1" + "revision": "e57007c23a52b68e44ebdfc70cbe882a7c4f1ec3", + "version": "3.0.2" } }, { @@ -69,8 +69,8 @@ "repositoryURL": "https://github.com/apple/swift-nio.git", "state": { "branch": null, - "revision": "695afc5205aaa49fca092b94b479ff71c43d9d3c", - "version": "1.8.0" + "revision": "cf08e673dc41dc63d34065234c8fc432e8d334c4", + "version": "1.9.2" } }, { @@ -78,8 +78,8 @@ "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", "state": { "branch": null, - "revision": "0adc938bc8de3d3829b842f9767d81c7480b8403", - "version": "1.1.1" + "revision": "6617eb0d3afcb12170594968df01ca63afb58ac5", + "version": "1.2.0" } }, { @@ -105,8 +105,8 @@ "repositoryURL": "https://github.com/vapor/template-kit.git", "state": { "branch": null, - "revision": "43b57b5861d5181b906ac6411d28645e980bb638", - "version": "1.0.1" + "revision": "db35b1c35aabd0f5db3abca0cfda7becfe9c43e2", + "version": "1.1.0" } }, { @@ -114,8 +114,8 @@ "repositoryURL": "https://github.com/vapor/url-encoded-form.git", "state": { "branch": null, - "revision": "57cf7fb9c1a1014c50bc05123684a9139ad44127", - "version": "1.0.3" + "revision": "cbfe7ef6301557d3f2d0807a98165232ae06e1c6", + "version": "1.0.4" } }, { @@ -123,8 +123,8 @@ "repositoryURL": "https://github.com/vapor/validation.git", "state": { "branch": null, - "revision": "ab6c5a352d97c8687b91ed4963aef8e7cfe0795b", - "version": "2.0.0" + "revision": "156f8adeac3440e868da3757777884efbc6ec0cc", + "version": "2.1.0" } }, { @@ -132,8 +132,8 @@ "repositoryURL": "https://github.com/vapor/vapor.git", "state": { "branch": null, - "revision": "09faa90db7ca49d7f75d5f8e385abce93bc7afb3", - "version": "3.0.4" + "revision": "54632a6c1e7ecd9923c0d00b612de936de1c4745", + "version": "3.0.8" } }, { From 0c9deb6133609cd540126e1ece1f57d1fa6cf1e6 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 11 Aug 2018 13:23:13 -0500 Subject: [PATCH 5/9] Created 'test' command --- Sources/Ether/Test.swift | 162 ++++++++++++++++++++++++++++++++++ Sources/Executable/main.swift | 1 + 2 files changed, 163 insertions(+) create mode 100644 Sources/Ether/Test.swift diff --git a/Sources/Ether/Test.swift b/Sources/Ether/Test.swift new file mode 100644 index 0000000..08a3bd7 --- /dev/null +++ b/Sources/Ether/Test.swift @@ -0,0 +1,162 @@ +// The MIT License (MIT) +// +// Copyright (c) 2017 Caleb Kleveter +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Foundation +import Command + +public final class Test: Command { + public var arguments: [CommandArgument] = [] + + public var options: [CommandOption] = [ + CommandOption.flag(name: "verbose", short: "v", help: ["Outputs raw stdout from `swift test`"]), + CommandOption.flag(name: "release", short: "r", help: ["Runs tests in release mode"]) + ] + + public var help: [String] = ["Runs `swift test` with formated output"] + + public init() {} + + public func run(using context: CommandContext) throws -> EventLoopFuture { + let stdout = self.output() + let exit = Process.asyncExecute("swift", "test", on: context.container) { output in + switch output { + case let .stdout(data): self.log(data, on: context, with: stdout) + case let .stderr(data): self.log(data, on: context, with: stdout) + } + } + + return exit.transform(to: ()) + } + + private func log(_ data: Data, on context: CommandContext, with stdout: (String) -> ConsoleText?) { + guard let value = String(data: data, encoding: .utf8) else { return } + + if context.options["verbose"] == nil { + value.split(separator: "\n").map(String.init).forEach { text in + if let text = stdout(text) { + context.console.output(text, newLine: false) + } + } + } else { + context.console.print(value, newLine: false) + } + } + + private func output() -> (String) -> (ConsoleText?) { + var compiling = false + var suiteSuccess = false + var error: String? = nil + var measure: String? = nil + + let building = try! NSRegularExpression(pattern: "^(Compile|Linking)", options: []) + let testSuiteStart = try! NSRegularExpression(pattern: "Test Suite '(.*?)(?:.xctest)?' started at (.*)", options: []) + let testSuiteComplete = try! NSRegularExpression(pattern: "Test Suite '(.*?)(?:.xctest)?' (passed|failed) at (.*)", options: []) + let testSuiteCompleteData = try! NSRegularExpression( + pattern: "\\h+Executed (\\d+) tests, with (\\d+) failure \\((\\d+) unexpected\\) in (.+?) \\(.+?\\) seconds", + options: [] + ) + let testCaseCompleted = try! NSRegularExpression(pattern: "^Test Case '-\\[(.*?) (.*?)\\]' (passed|failed) \\((.*?) seconds\\).", options: []) + let testError = try! NSRegularExpression(pattern: ".*?\\.swift:\\d+: error: -\\[(.*?) (.*?)\\] : (.*)", options: []) + let unknownError = try! NSRegularExpression(pattern: ":0: error: -\\[(.*?) (.*?)\\] : (.*)", options: []) + let testMeasure = try! NSRegularExpression( + pattern: "^.*?\\.swift:\\d+: Test Case '-\\[(.*?) (.*?)\\]' measured \\[Time, seconds\\] average: (.*?), relative standard deviation: (.*?), values: \\[(.*?)\\], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: \"(.*?)\", baselineAverage: (.*?), maxPercentRegression: (.*?), maxPercentRelativeStandardDeviation: (.*?), maxRegression: (.*?), maxStandardDeviation: (.*?)", + options: [] + ) + + func text(for test: String) -> ConsoleText? { + if building.matches(in: test, options: [], range: test.range).count > 0 && compiling == false { + compiling = true + let output = ConsoleTextFragment(string: "Building...\n", style: ConsoleStyle(color: .brightBlack, background: nil, isBold: false)) + return [output] + + } else if let start = testSuiteStart.matches(in: test, options: [], range: test.range).first { + let suite = test.substring(at: start.range(at: 1)) ?? "N/A" + + let running = ConsoleTextFragment(string: "Running ", style: ConsoleStyle(color: .brightBlack, background: nil, isBold: false)) + let name = ConsoleTextFragment(string: suite, style: ConsoleStyle(color: .brightBlack, background: nil, isBold: true)) + let ssuite = ConsoleTextFragment(string: " Suite\n", style: ConsoleStyle(color: .brightBlack, background: nil, isBold: false)) + + suiteSuccess = false + return [running, name, ssuite] + + } else if let complete = testCaseCompleted.matches(in: test, options: [], range: test.range).first { + let name = test.substring(at: complete.range(at: 2)) ?? "N/A" + let time = test.substring(at: complete.range(at: 4)) ?? "N/A" + let success = test.substring(at: complete.range(at: 3)) ?? "failed" + let background: ConsoleColor = success == "passed" ? .custom(r: 66, g: 147, b: 68) : .custom(r: 209, g: 50, b: 39) + + let bullet = ConsoleTextFragment(string: "\n - ", style: ConsoleStyle(color: .brightBlack, background: nil, isBold: false)) + let status = ConsoleTextFragment( + string: success == "passed" ? " v " : " x ", + style: ConsoleStyle(color: .white, background: background, isBold: true) + ) + let text = ConsoleTextFragment(string: " " + name, style: ConsoleStyle(color: .brightBlack, background: nil, isBold: true)) + let data = ConsoleTextFragment(string: " \(success) in \(time) seconds\(measure ?? ".") \(error ?? "")\n", style: ConsoleStyle(color: .brightBlack, background: nil, isBold: false)) + + error = nil + measure = nil + + return [bullet, status, text, data] + + } else if let testMeasure = testMeasure.matches(in: test, options: [], range: test.range).first { + let average = test.substring(at: testMeasure.range(at: 3)) + measure = average == nil ? "." : ". Average measured time is \(average!) sec." + return nil + + } else if let testError = testError.matches(in: test, options: [], range: test.range).first { + let message = test.substring(at: testError.range(at: 3)) + error = message == nil ? "\n" : "\n \(message!)" + return nil + + } else if let uknownError = unknownError.matches(in: test, options: [], range: test.range).first { + let message = test.substring(at: uknownError.range(at: 3)) + error = message == nil ? "\n" : "\n \(message!)" + return nil + + } else if let complete = testSuiteComplete.matches(in: test, options: [], range: test.range).first { + let suite = test.substring(at: complete.range(at: 1)) ?? "N/A" + let success = test.substring(at: complete.range(at: 2)) ?? "failed" + + let name = ConsoleTextFragment(string: !suiteSuccess ? "\n" + suite : suite, style: ConsoleStyle(color: .brightBlack, background: nil, isBold: true)) + let message = ConsoleTextFragment(string: " suite \(success)\n", style: ConsoleStyle(color: .brightBlack, background: nil, isBold: false)) + + suiteSuccess = true + return [name, message] + + } else if let complete = testSuiteCompleteData.matches(in: test, options: [], range: test.range).first { + let run = test.substring(at: complete.range(at: 1)) ?? "0" + let failed = test.substring(at: complete.range(at: 2)) ?? "0" + + let output = ConsoleTextFragment( + string: " - Finished with \(failed) out of \(run) test cases failing\n", + style: ConsoleStyle(color: .brightBlack, background: nil, isBold: false) + ) + return [output] + + } else { + return nil + } + } + + return text + } +} diff --git a/Sources/Executable/main.swift b/Sources/Executable/main.swift index b114696..d66c37c 100644 --- a/Sources/Executable/main.swift +++ b/Sources/Executable/main.swift @@ -43,6 +43,7 @@ commands.use(New(), as: "new") commands.use(Remove(), as: "remove") commands.use(Search(), as: "search") commands.use(Update(), as: "update") +commands.use(Test(), as: "test") commands.use(template, as: "template") commands.use(versions, as: "version") From 9a541c48eef07d0bf96b995c03e0dc0e2c5c52c3 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 11 Aug 2018 16:27:27 -0500 Subject: [PATCH 6/9] Installed xcodeproj package --- Package.resolved | 45 +++++++++++++++++++++++++++++++++++++++++++++ Package.swift | 3 ++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Package.resolved b/Package.resolved index 45a9d7d..173e87f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "AEXML", + "repositoryURL": "https://github.com/tadija/AEXML.git", + "state": { + "branch": null, + "revision": "6eea665515d079c338690147082a8084a36484b0", + "version": "4.3.0" + } + }, { "package": "Core", "repositoryURL": "https://github.com/vapor/core.git", @@ -55,6 +64,15 @@ "version": "3.0.2" } }, + { + "package": "PathKit", + "repositoryURL": "https://github.com/kylef/PathKit.git", + "state": { + "branch": null, + "revision": "fa81fa9e3a9f59645159c4ea45c0c46ee6558f71", + "version": "0.9.1" + } + }, { "package": "Routing", "repositoryURL": "https://github.com/vapor/routing.git", @@ -64,6 +82,24 @@ "version": "3.0.1" } }, + { + "package": "ShellOut", + "repositoryURL": "https://github.com/JohnSundell/ShellOut.git", + "state": { + "branch": null, + "revision": "f1c253a34a40df4bfd268b09fdb101b059f6d52d", + "version": "2.1.0" + } + }, + { + "package": "Spectre", + "repositoryURL": "https://github.com/kylef/Spectre.git", + "state": { + "branch": null, + "revision": "e34d5687e1e9d865e3527dd58bc2f7464ef6d936", + "version": "0.8.0" + } + }, { "package": "swift-nio", "repositoryURL": "https://github.com/apple/swift-nio.git", @@ -144,6 +180,15 @@ "revision": "141cb4d3814dc8062cb0b2f43e72801b5dfcf272", "version": "1.0.1" } + }, + { + "package": "xcproj", + "repositoryURL": "https://github.com/tuist/xcodeproj.git", + "state": { + "branch": null, + "revision": "e787c68092e28fe2b34205fc1b62de431922c243", + "version": "4.3.0" + } } ] }, diff --git a/Package.swift b/Package.swift index 6a67fae..61bc9a9 100644 --- a/Package.swift +++ b/Package.swift @@ -9,6 +9,7 @@ let package = Package( .library(name: "Ether", targets: ["Helpers", "Ether"]) ], dependencies: [ + .package(url: "https://github.com/tuist/xcodeproj.git", from: "4.3.0"), .package(url: "https://github.com/vapor/vapor.git", from: "3.0.3"), .package(url: "https://github.com/vapor/console.git", from: "3.0.2"), .package(url: "https://github.com/vapor/core.git", from: "3.1.7"), @@ -16,7 +17,7 @@ let package = Package( ], targets: [ .target(name: "Helpers", dependencies: ["Core", "Console"]), - .target(name: "Ether", dependencies: ["Vapor", "Helpers", "Console", "Command", "Manifest", "Core"]), + .target(name: "Ether", dependencies: ["Vapor", "Helpers", "Console", "Command", "Manifest", "Core", "xcproj"]), .target(name: "Executable", dependencies: ["Vapor", "Ether", "Console"]) ] ) From 661ef787f5cc3ea0eed77478d02998d18f131275 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 11 Aug 2018 17:26:51 -0500 Subject: [PATCH 7/9] Support fatal errors messages in test output --- Sources/Ether/Test.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/Ether/Test.swift b/Sources/Ether/Test.swift index 08a3bd7..c9f5630 100644 --- a/Sources/Ether/Test.swift +++ b/Sources/Ether/Test.swift @@ -81,6 +81,7 @@ public final class Test: Command { pattern: "^.*?\\.swift:\\d+: Test Case '-\\[(.*?) (.*?)\\]' measured \\[Time, seconds\\] average: (.*?), relative standard deviation: (.*?), values: \\[(.*?)\\], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: \"(.*?)\", baselineAverage: (.*?), maxPercentRegression: (.*?), maxPercentRelativeStandardDeviation: (.*?), maxRegression: (.*?), maxStandardDeviation: (.*?)", options: [] ) + let fatal = try! NSRegularExpression(pattern: "Fatal error: (.*)", options: []) func text(for test: String) -> ConsoleText? { if building.matches(in: test, options: [], range: test.range).count > 0 && compiling == false { @@ -152,6 +153,11 @@ public final class Test: Command { ) return [output] + } else if let fatal = fatal.matches(in: test, options: [], range: test.range).first { + return [ + ConsoleTextFragment(string: "Fatal Error: ", style: ConsoleStyle(color: .brightRed, background: nil, isBold: true)), + ConsoleTextFragment(string: test.substring(at: fatal.range(at: 1)) ?? "") + ] } else { return nil } From fa86d7e5d7f453428f7b6caf151b40bbee413f40 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 11 Aug 2018 17:32:06 -0500 Subject: [PATCH 8/9] Updated CHANGLOG for 2018.08.11 release --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f395de8..56cb235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [2018.08.11] + +### Fixed +- `new` command no longer fails when cleaning new project's manifest. +- Dependency writing when last dependency has a trailing comma. +- White space when adding first dependency to manifest. + +### Added +- + ## [2018.06.15] ### Added From 528950b1f43a90b2dbf99e33f4771ebfca05f37b Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Sat, 11 Aug 2018 17:32:25 -0500 Subject: [PATCH 9/9] Updated release number in executable --- Sources/Executable/main.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Executable/main.swift b/Sources/Executable/main.swift index d66c37c..ff3da9d 100644 --- a/Sources/Executable/main.swift +++ b/Sources/Executable/main.swift @@ -24,7 +24,7 @@ import Console import Vapor import Ether -let version = "2018.05.25" +let version = "2018.08.11" let arguments = CommandLine.arguments if arguments.count == 2, arguments[1] == "--version" || arguments[1] == "-v" {