From fea1384f57dde5b1d178028f41b653e3269381fe Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 22 May 2018 07:22:19 -0500 Subject: [PATCH 1/6] Create config file if one does not exist in Configuration.get() --- Sources/Ether/Configuration.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Sources/Ether/Configuration.swift b/Sources/Ether/Configuration.swift index 5bd99be..739d67e 100644 --- a/Sources/Ether/Configuration.swift +++ b/Sources/Ether/Configuration.swift @@ -65,6 +65,20 @@ public class Configuration: Command { let user = try Process.execute("whoami") let configuration: Data + try FileManager.default.createDirectory( + at: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether")!, + withIntermediateDirectories: true, + attributes: [:] + ) + + if !FileManager.default.fileExists(atPath: "/Users/\(user)/Library/Application Support/Ether/config.json") { + FileManager.default.createFile( + atPath: "/Users/\(user)/Library/Application Support/Ether/config.json", + contents: nil, + attributes: [:] + ) + } + let contents = try Data(contentsOf: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether/config.json")!) if contents.count > 0 { configuration = contents From 318c27b83c1527c57c77f20008ce0d8bc7ad0b28 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 22 May 2018 11:32:10 -0500 Subject: [PATCH 2/6] Created /Templates application support directory if none exists in Template command --- Sources/Ether/Template.swift | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Sources/Ether/Template.swift b/Sources/Ether/Template.swift index efb1dda..9114f3b 100644 --- a/Sources/Ether/Template.swift +++ b/Sources/Ether/Template.swift @@ -47,22 +47,23 @@ public final class Template: Command { let temapletBar = context.console.loadingBar(title: barTitle) _ = temapletBar.start(on: context.container) - if #available(OSX 10.12, *) { - var isDir : ObjCBool = true - let directoryName = manager.homeDirectoryForCurrentUser.absoluteString - let defaultPath = String("\(directoryName)Library/Application Support/Ether/Templates".dropFirst(7)) - let directoryExists = manager.fileExists(atPath: "\(defaultPath)/\(name)", isDirectory: &isDir) + let user = try Process.execute("whoami") + try FileManager.default.createDirectory( + at: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether/Templates")!, + withIntermediateDirectories: true, + attributes: [:] + ) + + var isDir : ObjCBool = true + let directoryExists = manager.fileExists(atPath: "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)", isDirectory: &isDir) - if removeTemplate { - if !directoryExists { throw EtherError(identifier: "templateNotFound", reason: "No template with the name '\(name)' was found") } - _ = try Process.execute("rm", ["-rf", "\(defaultPath)/\(name)"]) - } else { - if directoryExists { throw EtherError(identifier: "templateAlreadyExists", reason: "A template with the name '\(name)' was found") } - let current = manager.currentDirectoryPath + "/." - _ = try Process.execute("cp", ["-a", "\(current)", "\(defaultPath)/\(name)"]) - } + if removeTemplate { + if !directoryExists { throw EtherError(identifier: "templateNotFound", reason: "No template with the name '\(name)' was found") } + _ = try Process.execute("rm", ["-rf", "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)"]) } else { - throw EtherError(identifier: "unsupportedOS", reason: "This command is not supported in macOS versions older then 10.12") + if directoryExists { throw EtherError(identifier: "templateAlreadyExists", reason: "A template with the name '\(name)' was found") } + let current = manager.currentDirectoryPath + "/." + _ = try Process.execute("cp", ["-a", "\(current)", "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)"]) } temapletBar.succeed() From 3dcd2c2cad9dcbb77db9e5374792cf34a6cc57fa Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 22 May 2018 17:05:58 -0500 Subject: [PATCH 3/6] Added feature list to README --- README.md | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 159 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8c31fa0..a3424e3 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,12 @@ [![Mentioned in Awesome Vapor](https://awesome.re/mentioned-badge.svg)](https://github.com/Cellane/awesome-vapor) -### What is it? +## What is it? -Ether is a CLI the integrates with SPM (Swift Package Manager). +Ether is a CLI the integrates with SPM (Swift Package Manager), similar to NPM's command-line tool. -### What features does it have? -You can see a whole list of the available commands [here](https://github.com/calebkleveter/Ether/wiki/Features) - -### How do I install it? +## How do I install it? With Homebrew: @@ -31,10 +28,164 @@ If you don't have, want, or can't use Homebrew, you can run the following script curl https://raw.githubusercontent.com/calebkleveter/Ether/master/install.sh | bash ``` -### How do I make my package available? +## What features does it have? + +### Search: + +Searches for available packages: + + ether search + +Example result: + +``` +Searching [Done] +vapor/vapor: 💧 A server-side Swift web framework. +License: MIT License +Stars: 13906 + +vapor/toolbox: Simplifies common command line tasks when using Vapor +License: MIT License +Stars: 111 + +matthijs2704/vapor-apns: Simple APNS Library for Vapor (Swift) +License: MIT License +Stars: 289 + +vapor-community/postgresql: Robust PostgreSQL interface for Swift +License: MIT License +Stars: 99 + +... +``` + +### Install + +Installs a package with its dependencies: + + ether install + +Example output: + +``` +Installing Dependency [Done] +📦 10 packages installed +``` + +Package.swift: + +``` +dependencies: [ + .package(url: "https://github.com/vapor/console.git", from: "3.0.0"), + .package(url: "https://github.com/vapor/core.git", from: "3.0.0"), + .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0") <=== Added Package +] +``` + +**Note:** + +The install command has a rather interesting method of getting the proper package. Because you can have packages with the same name by different authors, Ether will run a search based on the argument you pass in and get the most stared result. If the name contains a slash (`/`), then the URL will created directly without a search like this: + + https://github.com/.git + +Note that this is case insensitive. + +### Fix Install + +Fixes the install process when an error occurs during `install` + + ether fix-install + +Example Output: + +``` +This may take some time... +Fixing Installation [Done] +``` + +### Remove + +Removes the package and its dependencies from the project: + + ether remove + +Example output: + +``` +Removing Dependency [Done] +📦 2 packages removed +``` + +### Update + +Updates the packages. This only needs to be run if packages are manually added. + + ether update + +Example output: + + Updating Packages [Done] + +You can pass in the `--self` flag to update Ether to the latest version: + + ether update --self + +Example output: + + Updating Ether [Done] + +### Template + +Saves the current state of the project as a template that can be used later when creating a new project. + + ether template + +Example output: + + Saving Template [Done] + +### New + +Creates a project. It can be a Swift package, a Swift executable, or a project from a previously saved template. + + ether new + +Example output: + + Generating Project [Done] + +### Version Latest + +Sets all packages to their latest versions: + + ether version latest + +Example output: + + Updating Package Versions [Done] + +### Version All + +Outputs the name of each package installed and its version + + ether version all + +Example output: + +``` +Getting Package Data [Done] +Bits: v1.0.0 +Console: v2.1.0 +Core: v2.0.2 +Debugging: v1.0.0 +JSON: v2.0.2 +Node: v2.0.4 +``` + +## How do I make my package available? If they are on GitHub, they already are! Ether uses GitHub's GraphQL API to fetch projects with a `Package.swift` file in the project root. -### What license is it under? +## What license is it under? Ether is under the MIT license. From 687262283ae3af277b6321e9957dfe235db72f80 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 22 May 2018 17:06:42 -0500 Subject: [PATCH 4/6] Added link to License in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3424e3..0728c92 100644 --- a/README.md +++ b/README.md @@ -188,4 +188,4 @@ If they are on GitHub, they already are! Ether uses GitHub's GraphQL API to fetc ## What license is it under? -Ether is under the MIT license. +Ether is under the [MIT license](https://github.com/Ether-CLI/Ether/blob/master/LICENSE.md). From bdb1d4275dc3dfd768f06ae1120b45a5ec335e8a Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 22 May 2018 17:37:59 -0500 Subject: [PATCH 5/6] Created Config.token() method for getting the saved access token --- Sources/Ether/Configuration.swift | 16 ++++++++++++++++ Sources/Ether/Install.swift | 7 +------ Sources/Ether/Search.swift | 8 +------- Sources/Ether/VersionLatest.swift | 7 +------ Sources/Helpers/EtherError.swift | 1 + 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Sources/Ether/Configuration.swift b/Sources/Ether/Configuration.swift index 739d67e..c8a1ff0 100644 --- a/Sources/Ether/Configuration.swift +++ b/Sources/Ether/Configuration.swift @@ -96,4 +96,20 @@ public struct Config: Codable, Reflectable { static let properties: [String: WritableKeyPath] = [ "access-token": \.accessToken ] + + func token()throws -> String { + guard let token = self.accessToken else { + var error = EtherError( + identifier: "noAccessToken", + reason: "No access token in configuration" + ) + error.suggestedFixes = [ + "Create a GitHub token at https://github.com/settings/tokens", + "Run `ether config access-token `", + "The token should have permissions to access public repositorie" + ] + throw error + } + return token + } } diff --git a/Sources/Ether/Install.swift b/Sources/Ether/Install.swift index 2559e10..8c1c6dd 100644 --- a/Sources/Ether/Install.swift +++ b/Sources/Ether/Install.swift @@ -126,12 +126,7 @@ public final class Install: Command { func package(with name: String, on context: CommandContext)throws -> Future<(url: String, version: String, products: [String])> { let client = try context.container.make(Client.self) - guard let token = try Configuration.get().accessToken else { - throw EtherError( - identifier: "noAccessToken", - reason: "No access token in configuration. Run `ether config access-token `. The token should have permissions to access public repositories" - ) - } + let token = try Configuration.get().token() let fullName: Future if name.contains("/") { diff --git a/Sources/Ether/Search.swift b/Sources/Ether/Search.swift index 90f3f51..8ffc49d 100644 --- a/Sources/Ether/Search.swift +++ b/Sources/Ether/Search.swift @@ -48,17 +48,11 @@ public final class Search: Command { let client = try context.container.make(Client.self) let name = try context.argument("name") let maxResults = context.options["max-results"] ?? "20" - let config = try Configuration.get() guard let max = Int(maxResults), max <= 100 && max > 0 else { throw EtherError(identifier: "badMaxResults", reason: "`max-results` value must be an integer, less than or equal to 100, and greater than 0") } - guard let token = config.accessToken else { - throw EtherError( - identifier: "noAccessToken", - reason: "No access token in configuration. Run `ether config access-token `. The token should have permissions to access public repositories" - ) - } + let token = try Configuration.get().token() let response = client.get("https://package.vapor.cloud/packages/search?name=\(name)&limit=\(max)", headers: ["Authorization": "Bearer \(token)"]) return response.flatMap(to: [PackageDescription].self) { response in diff --git a/Sources/Ether/VersionLatest.swift b/Sources/Ether/VersionLatest.swift index e44b3a0..0c761bf 100644 --- a/Sources/Ether/VersionLatest.swift +++ b/Sources/Ether/VersionLatest.swift @@ -45,12 +45,7 @@ public final class VersionLatest: Command { let tagPattern = try NSRegularExpression(pattern: "v?\\d+(?:\\.\\d+)?(?:\\.\\d+)?", options: []) let client = try context.container.make(Client.self) - guard let token = try Configuration.get().accessToken else { - throw EtherError( - identifier: "noAccessToken", - reason: "No access token in configuration. Run `ether config access-token `. The token should have permissions to access public repositories" - ) - } + let token = try Configuration.get().token() let packageNames = try Manifest.current.dependencies().compactMap { dependency -> (fullName: String, url: String)? in guard let result = namePattern.firstMatch(in: dependency.url, options: [], range: NSMakeRange(0, dependency.url.utf8.count)) else { return nil } diff --git a/Sources/Helpers/EtherError.swift b/Sources/Helpers/EtherError.swift index 53660fb..cbd3399 100644 --- a/Sources/Helpers/EtherError.swift +++ b/Sources/Helpers/EtherError.swift @@ -25,6 +25,7 @@ import Debugging public struct EtherError: Error, Debuggable { public let identifier: String public let reason: String + public var suggestedFixes: [String] = [] public init(identifier: String, reason: String) { self.identifier = identifier From f2522aadc1fed8977067ed8ce84f5119f9c28a39 Mon Sep 17 00:00:00 2001 From: Caleb Kleveter Date: Tue, 22 May 2018 17:39:15 -0500 Subject: [PATCH 6/6] Added entry to CHANGELOG for version 2018.05.22 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46bf3bf..f92b97f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [2018.05.22] + +### Fixed +- Create config file and template directory if they don't exist instead of throwing an error. + ## [2018.05.18] ### Updated