Skip to content

Commit 8179912

Browse files
Merge pull request #24 from Ether-CLI/develop
Create Config File and Template Directory when they don't Exist
2 parents 226067f + f2522aa commit 8179912

File tree

8 files changed

+214
-42
lines changed

8 files changed

+214
-42
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## [2018.05.22]
2+
3+
### Fixed
4+
- Create config file and template directory if they don't exist instead of throwing an error.
5+
16
## [2018.05.18]
27

38
### Updated

README.md

+160-9
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,12 @@
88

99
[![Mentioned in Awesome Vapor](https://awesome.re/mentioned-badge.svg)](https://github.com/Cellane/awesome-vapor)
1010

11-
### What is it?
11+
## What is it?
1212

13-
Ether is a CLI the integrates with SPM (Swift Package Manager).
13+
Ether is a CLI the integrates with SPM (Swift Package Manager), similar to NPM's command-line tool.
1414

15-
### What features does it have?
1615

17-
You can see a whole list of the available commands [here](https://github.com/calebkleveter/Ether/wiki/Features)
18-
19-
### How do I install it?
16+
## How do I install it?
2017

2118
With Homebrew:
2219

@@ -31,10 +28,164 @@ If you don't have, want, or can't use Homebrew, you can run the following script
3128
curl https://raw.githubusercontent.com/calebkleveter/Ether/master/install.sh | bash
3229
```
3330

34-
### How do I make my package available?
31+
## What features does it have?
32+
33+
### Search:
34+
35+
Searches for available packages:
36+
37+
ether search <name>
38+
39+
Example result:
40+
41+
```
42+
Searching [Done]
43+
vapor/vapor: 💧 A server-side Swift web framework.
44+
License: MIT License
45+
Stars: 13906
46+
47+
vapor/toolbox: Simplifies common command line tasks when using Vapor
48+
License: MIT License
49+
Stars: 111
50+
51+
matthijs2704/vapor-apns: Simple APNS Library for Vapor (Swift)
52+
License: MIT License
53+
Stars: 289
54+
55+
vapor-community/postgresql: Robust PostgreSQL interface for Swift
56+
License: MIT License
57+
Stars: 99
58+
59+
...
60+
```
61+
62+
### Install
63+
64+
Installs a package with its dependencies:
65+
66+
ether install <name>
67+
68+
Example output:
69+
70+
```
71+
Installing Dependency [Done]
72+
📦 10 packages installed
73+
```
74+
75+
Package.swift:
76+
77+
```
78+
dependencies: [
79+
.package(url: "https://github.com/vapor/console.git", from: "3.0.0"),
80+
.package(url: "https://github.com/vapor/core.git", from: "3.0.0"),
81+
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0") <=== Added Package
82+
]
83+
```
84+
85+
**Note:**
86+
87+
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:
88+
89+
https://github.com/<NAME>.git
90+
91+
Note that this is case insensitive.
92+
93+
### Fix Install
94+
95+
Fixes the install process when an error occurs during `install`
96+
97+
ether fix-install
98+
99+
Example Output:
100+
101+
```
102+
This may take some time...
103+
Fixing Installation [Done]
104+
```
105+
106+
### Remove
107+
108+
Removes the package and its dependencies from the project:
109+
110+
ether remove <name>
111+
112+
Example output:
113+
114+
```
115+
Removing Dependency [Done]
116+
📦 2 packages removed
117+
```
118+
119+
### Update
120+
121+
Updates the packages. This only needs to be run if packages are manually added.
122+
123+
ether update
124+
125+
Example output:
126+
127+
Updating Packages [Done]
128+
129+
You can pass in the `--self` flag to update Ether to the latest version:
130+
131+
ether update --self
132+
133+
Example output:
134+
135+
Updating Ether [Done]
136+
137+
### Template
138+
139+
Saves the current state of the project as a template that can be used later when creating a new project.
140+
141+
ether template <name>
142+
143+
Example output:
144+
145+
Saving Template [Done]
146+
147+
### New
148+
149+
Creates a project. It can be a Swift package, a Swift executable, or a project from a previously saved template.
150+
151+
ether new <name>
152+
153+
Example output:
154+
155+
Generating Project [Done]
156+
157+
### Version Latest
158+
159+
Sets all packages to their latest versions:
160+
161+
ether version latest
162+
163+
Example output:
164+
165+
Updating Package Versions [Done]
166+
167+
### Version All
168+
169+
Outputs the name of each package installed and its version
170+
171+
ether version all
172+
173+
Example output:
174+
175+
```
176+
Getting Package Data [Done]
177+
Bits: v1.0.0
178+
Console: v2.1.0
179+
Core: v2.0.2
180+
Debugging: v1.0.0
181+
JSON: v2.0.2
182+
Node: v2.0.4
183+
```
184+
185+
## How do I make my package available?
35186

36187
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.
37188

38-
### What license is it under?
189+
## What license is it under?
39190

40-
Ether is under the MIT license.
191+
Ether is under the [MIT license](https://github.com/Ether-CLI/Ether/blob/master/LICENSE.md).

Sources/Ether/Configuration.swift

+30
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ public class Configuration: Command {
6565
let user = try Process.execute("whoami")
6666
let configuration: Data
6767

68+
try FileManager.default.createDirectory(
69+
at: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether")!,
70+
withIntermediateDirectories: true,
71+
attributes: [:]
72+
)
73+
74+
if !FileManager.default.fileExists(atPath: "/Users/\(user)/Library/Application Support/Ether/config.json") {
75+
FileManager.default.createFile(
76+
atPath: "/Users/\(user)/Library/Application Support/Ether/config.json",
77+
contents: nil,
78+
attributes: [:]
79+
)
80+
}
81+
6882
let contents = try Data(contentsOf: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether/config.json")!)
6983
if contents.count > 0 {
7084
configuration = contents
@@ -82,4 +96,20 @@ public struct Config: Codable, Reflectable {
8296
static let properties: [String: WritableKeyPath<Config, String?>] = [
8397
"access-token": \.accessToken
8498
]
99+
100+
func token()throws -> String {
101+
guard let token = self.accessToken else {
102+
var error = EtherError(
103+
identifier: "noAccessToken",
104+
reason: "No access token in configuration"
105+
)
106+
error.suggestedFixes = [
107+
"Create a GitHub token at https://github.com/settings/tokens",
108+
"Run `ether config access-token <TOKEN>`",
109+
"The token should have permissions to access public repositorie"
110+
]
111+
throw error
112+
}
113+
return token
114+
}
85115
}

Sources/Ether/Install.swift

+1-6
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,7 @@ public final class Install: Command {
126126

127127
func package(with name: String, on context: CommandContext)throws -> Future<(url: String, version: String, products: [String])> {
128128
let client = try context.container.make(Client.self)
129-
guard let token = try Configuration.get().accessToken else {
130-
throw EtherError(
131-
identifier: "noAccessToken",
132-
reason: "No access token in configuration. Run `ether config access-token <TOKEN>`. The token should have permissions to access public repositories"
133-
)
134-
}
129+
let token = try Configuration.get().token()
135130

136131
let fullName: Future<String>
137132
if name.contains("/") {

Sources/Ether/Search.swift

+1-7
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,11 @@ public final class Search: Command {
4848
let client = try context.container.make(Client.self)
4949
let name = try context.argument("name")
5050
let maxResults = context.options["max-results"] ?? "20"
51-
let config = try Configuration.get()
5251

5352
guard let max = Int(maxResults), max <= 100 && max > 0 else {
5453
throw EtherError(identifier: "badMaxResults", reason: "`max-results` value must be an integer, less than or equal to 100, and greater than 0")
5554
}
56-
guard let token = config.accessToken else {
57-
throw EtherError(
58-
identifier: "noAccessToken",
59-
reason: "No access token in configuration. Run `ether config access-token <TOKEN>`. The token should have permissions to access public repositories"
60-
)
61-
}
55+
let token = try Configuration.get().token()
6256

6357
let response = client.get("https://package.vapor.cloud/packages/search?name=\(name)&limit=\(max)", headers: ["Authorization": "Bearer \(token)"])
6458
return response.flatMap(to: [PackageDescription].self) { response in

Sources/Ether/Template.swift

+15-14
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,23 @@ public final class Template: Command {
4747
let temapletBar = context.console.loadingBar(title: barTitle)
4848
_ = temapletBar.start(on: context.container)
4949

50-
if #available(OSX 10.12, *) {
51-
var isDir : ObjCBool = true
52-
let directoryName = manager.homeDirectoryForCurrentUser.absoluteString
53-
let defaultPath = String("\(directoryName)Library/Application Support/Ether/Templates".dropFirst(7))
54-
let directoryExists = manager.fileExists(atPath: "\(defaultPath)/\(name)", isDirectory: &isDir)
50+
let user = try Process.execute("whoami")
51+
try FileManager.default.createDirectory(
52+
at: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether/Templates")!,
53+
withIntermediateDirectories: true,
54+
attributes: [:]
55+
)
56+
57+
var isDir : ObjCBool = true
58+
let directoryExists = manager.fileExists(atPath: "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)", isDirectory: &isDir)
5559

56-
if removeTemplate {
57-
if !directoryExists { throw EtherError(identifier: "templateNotFound", reason: "No template with the name '\(name)' was found") }
58-
_ = try Process.execute("rm", ["-rf", "\(defaultPath)/\(name)"])
59-
} else {
60-
if directoryExists { throw EtherError(identifier: "templateAlreadyExists", reason: "A template with the name '\(name)' was found") }
61-
let current = manager.currentDirectoryPath + "/."
62-
_ = try Process.execute("cp", ["-a", "\(current)", "\(defaultPath)/\(name)"])
63-
}
60+
if removeTemplate {
61+
if !directoryExists { throw EtherError(identifier: "templateNotFound", reason: "No template with the name '\(name)' was found") }
62+
_ = try Process.execute("rm", ["-rf", "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)"])
6463
} else {
65-
throw EtherError(identifier: "unsupportedOS", reason: "This command is not supported in macOS versions older then 10.12")
64+
if directoryExists { throw EtherError(identifier: "templateAlreadyExists", reason: "A template with the name '\(name)' was found") }
65+
let current = manager.currentDirectoryPath + "/."
66+
_ = try Process.execute("cp", ["-a", "\(current)", "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)"])
6667
}
6768

6869
temapletBar.succeed()

Sources/Ether/VersionLatest.swift

+1-6
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,7 @@ public final class VersionLatest: Command {
4545
let tagPattern = try NSRegularExpression(pattern: "v?\\d+(?:\\.\\d+)?(?:\\.\\d+)?", options: [])
4646
let client = try context.container.make(Client.self)
4747

48-
guard let token = try Configuration.get().accessToken else {
49-
throw EtherError(
50-
identifier: "noAccessToken",
51-
reason: "No access token in configuration. Run `ether config access-token <TOKEN>`. The token should have permissions to access public repositories"
52-
)
53-
}
48+
let token = try Configuration.get().token()
5449

5550
let packageNames = try Manifest.current.dependencies().compactMap { dependency -> (fullName: String, url: String)? in
5651
guard let result = namePattern.firstMatch(in: dependency.url, options: [], range: NSMakeRange(0, dependency.url.utf8.count)) else { return nil }

Sources/Helpers/EtherError.swift

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import Debugging
2525
public struct EtherError: Error, Debuggable {
2626
public let identifier: String
2727
public let reason: String
28+
public var suggestedFixes: [String] = []
2829

2930
public init(identifier: String, reason: String) {
3031
self.identifier = identifier

0 commit comments

Comments
 (0)