Skip to content

Commit

Permalink
Merge pull request #24 from Ether-CLI/develop
Browse files Browse the repository at this point in the history
Create Config File and Template Directory when they don't Exist
  • Loading branch information
calebkleveter authored May 22, 2018
2 parents 226067f + f2522aa commit 8179912
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 42 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
169 changes: 160 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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 <name>

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 <name>

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/<NAME>.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 <name>

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 <name>

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 <name>

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.
Ether is under the [MIT license](https://github.com/Ether-CLI/Ether/blob/master/LICENSE.md).
30 changes: 30 additions & 0 deletions Sources/Ether/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -82,4 +96,20 @@ public struct Config: Codable, Reflectable {
static let properties: [String: WritableKeyPath<Config, String?>] = [
"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 <TOKEN>`",
"The token should have permissions to access public repositorie"
]
throw error
}
return token
}
}
7 changes: 1 addition & 6 deletions Sources/Ether/Install.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 <TOKEN>`. The token should have permissions to access public repositories"
)
}
let token = try Configuration.get().token()

let fullName: Future<String>
if name.contains("/") {
Expand Down
8 changes: 1 addition & 7 deletions Sources/Ether/Search.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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
Expand Down
29 changes: 15 additions & 14 deletions Sources/Ether/Template.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
7 changes: 1 addition & 6 deletions Sources/Ether/VersionLatest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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 }
Expand Down
1 change: 1 addition & 0 deletions Sources/Helpers/EtherError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 8179912

Please sign in to comment.