Skip to content

Commit

Permalink
Merge pull request #25 from Ether-CLI/develop
Browse files Browse the repository at this point in the history
Template List, Command Group, And Auto Committing
  • Loading branch information
calebkleveter authored May 25, 2018
2 parents 8179912 + e8902b2 commit 4045282
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 44 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## [2018.05.25]

### Added
- `install-commit` configuration key.
- `remove-commit` configuration key.
- `signed-commits` configuration key.
- Template command group.
- `template list` command.
- Default answer `n` for pre-release version install question.

## [2018.05.22]

### Fixed
Expand Down
8 changes: 4 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,17 @@
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "bad7c297427b5efedb96c4044f9e57b42881e9ea",
"version": "1.7.0"
"revision": "80363d9d8c1c572d228350ea87132bfa49c8ca03",
"version": "1.7.1"
}
},
{
"package": "swift-nio-ssl",
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
"state": {
"branch": null,
"revision": "38955a5f806a952daf2b16fbfe9aa529749cf1dd",
"version": "1.1.0"
"revision": "0adc938bc8de3d3829b842f9767d81c7480b8403",
"version": "1.1.1"
}
},
{
Expand Down
4 changes: 4 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import PackageDescription

let package = Package(
name: "Ether",
products: [
.executable(name: "Executable", targets: ["Executable"]),
.library(name: "Ether", targets: ["Helpers", "Ether"])
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.3"),
.package(url: "https://github.com/vapor/console.git", from: "3.0.2"),
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ dependencies: [

**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:
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 be 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`
Fixes the install process when an error occurs during `install`, such as a git conflict.

ether fix-install

Expand Down
43 changes: 39 additions & 4 deletions Sources/Ether/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,15 @@ import Bits

public class Configuration: Command {
public var arguments: [CommandArgument] = [
CommandArgument.argument(name: "key", help: ["The configuration JSON key to set"]),
CommandArgument.argument(name: "value", help: ["The new value for the key passed in"])
CommandArgument.argument(name: "key", help: [
"The configuration JSON key to set",
"Valid keys are:",
"- access-token: The GitHub access token to use for interacting the the GraphQL API. You can create on at https://github.com/settings/token",
"- install-commit: The commit message to use on package install. Use &0 as package name placeholder",
"- remove-commit: The commit message to use on when a package is removed. Use &0 as package name placeholder",
"- signed-commits: If set to a truthy value (true, yes, y, 1), auto-commits will pass in the '-S' flag"
]),
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] = []
Expand All @@ -44,7 +51,7 @@ public class Configuration: Command {
_ = setter.start(on: context.container)

let key = try context.argument("key")
let value = try context.argument("value")
let value = context.arguments["value"]
let user = try Process.execute("whoami")

var configuration = try Configuration.get()
Expand Down Expand Up @@ -92,9 +99,15 @@ public class Configuration: Command {

public struct Config: Codable, Reflectable {
public var accessToken: String?
public var installCommit: String?
public var removeCommit: String?
public var signedCommits: String?

static let properties: [String: WritableKeyPath<Config, String?>] = [
"access-token": \.accessToken
"access-token": \.accessToken,
"install-commit": \.installCommit,
"remove-commit": \.removeCommit,
"signed-commits": \.signedCommits
]

func token()throws -> String {
Expand All @@ -112,4 +125,26 @@ public struct Config: Codable, Reflectable {
}
return token
}

func signed() -> Bool {
switch (self.signedCommits ?? "n").lowercased() {
case "true", "yes", "y", "1": return true
default: return false
}
}

func commit(with message: String?, on context: CommandContext, replacements: [String] = [])throws {
if var commit = message {
for (index, value) in replacements.enumerated() {
commit = commit.replacingOccurrences(of: "&\(index)", with: value)
}

var commitOptions = ["commit", "-m", commit.description]
if self.signed() { commitOptions.insert("-S", at: 1) }

_ = try Process.execute("git", "add", "Package.swift", "Package.resolved")
let commitMessage = try Process.execute("git", commitOptions)
context.console.print(commitMessage)
}
}
}
7 changes: 5 additions & 2 deletions Sources/Ether/Install.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ public final class Install: Command {
}

context.console.output("📦 \(newPinCount - oldPinCount) packages installed", style: .plain, newLine: true)

let config = try Configuration.get()
try config.commit(with: config.installCommit, on: context, replacements: [name])
}
}

Expand Down Expand Up @@ -179,9 +182,9 @@ public final class Install: Command {

while true {
answer = context.console.ask(
ConsoleText(stringLiteral:"The latest version found (\(first)) is a pre-release. Would you like to use an earlier stable release? (y/n)")
ConsoleText(stringLiteral:"The latest version found (\(first)) is a pre-release. Would you like to use an earlier stable release? (y/N)")
).lowercased()
if answer == "y" || answer == "n" { break }
if answer == "y" || answer == "n" || answer == "" { break }
}

if answer == "y" {
Expand Down
4 changes: 4 additions & 0 deletions Sources/Ether/Remove.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public final class Remove: Command {
}

context.console.print("📦 \(removed) packages removed")

let config = try Configuration.get()
try config.commit(with: config.removeCommit, on: context, replacements: [name])

return context.container.eventLoop.newSucceededFuture(result: ())
}
}
34 changes: 34 additions & 0 deletions Sources/Ether/Template/Template.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 Command

public let template = Commands(
commands: [
"create": TemplateCreate(),
"remove": TemplateRemove(),
"list": TemplateList()
],
defaultCommand: "list"
).group(help: [
"For saving and deleting template projects"
])
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,21 @@ import Foundation
import Helpers
import Command

public final class Template: Command {
public final class TemplateCreate: Command {
public var arguments: [CommandArgument] = [
CommandArgument.argument(name: "name", help: ["The name used to identify the template"])
]

public var options: [CommandOption] = [
CommandOption.flag(name: "remove", short: "r", help: ["Deletes the template"])
// TODO: Add `github` flag to create remote repo and push.
]
public var options: [CommandOption] = []

public var help: [String] = ["Creates and stores a template for use as the starting point of a project."]

public init() {}

public func run(using context: CommandContext) throws -> EventLoopFuture<Void> {
let name = try context.argument("name")
let removeTemplate = context.options["remove"] == nil ? false : true
let manager = FileManager.default
let barTitle = removeTemplate ? "Deleting Template" : "Saving Template"

let temapletBar = context.console.loadingBar(title: barTitle)
let temapletBar = context.console.loadingBar(title: "Saving Template")
_ = temapletBar.start(on: context.container)

let user = try Process.execute("whoami")
Expand All @@ -55,16 +49,11 @@ public final class Template: Command {
)

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", "/Users/\(user)/Library/Application Support/Ether/Templates/\(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)", "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)"])
}
let directoryExists = FileManager.default.fileExists(atPath: "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)", isDirectory: &isDir)

if directoryExists { throw EtherError(identifier: "templateAlreadyExists", reason: "A template with the name '\(name)' was found") }
let current = FileManager.default.currentDirectoryPath + "/."
_ = try Process.execute("cp", ["-a", "\(current)", "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)"])

temapletBar.succeed()
return context.container.eventLoop.newSucceededFuture(result: ())
Expand Down
48 changes: 48 additions & 0 deletions Sources/Ether/Template/TemplateList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 Command

final class TemplateList: Command {
var arguments: [CommandArgument] = []
var options: [CommandOption] = []

var help: [String] = ["Lists all saved project templates"]

func run(using context: CommandContext) throws -> EventLoopFuture<Void> {

let user = try Process.execute("whoami")
try FileManager.default.createDirectory(
at: URL(string: "file:/Users/\(user)/Library/Application%20Support/Ether/Templates")!,
withIntermediateDirectories: true,
attributes: [:]
)

let projects = try Process.execute("ls", "/Users/\(user)/Library/Application Support/Ether/Templates/")
for project in projects.split(separator: "\n").map(String.init) {
context.console.info("- ", newLine: false)
context.console.print(project)
}

return context.container.eventLoop.newSucceededFuture(result: ())
}
}
60 changes: 60 additions & 0 deletions Sources/Ether/Template/TemplateRemove.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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 Helpers
import Command

public final class TemplateRemove: Command {
public var arguments: [CommandArgument] = [
CommandArgument.argument(name: "name", help: ["The name used to identify the template"])
]

public var options: [CommandOption] = []

public var help: [String] = ["Deletes a stored project template"]

public init() {}

public func run(using context: CommandContext) throws -> EventLoopFuture<Void> {
let name = try context.argument("name")

let temapletBar = context.console.loadingBar(title: "Deleting Template")
_ = temapletBar.start(on: context.container)

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 = FileManager.default.fileExists(atPath: "/Users/\(user)/Library/Application Support/Ether/Templates/\(name)", isDirectory: &isDir)

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)"])

temapletBar.succeed()
return context.container.eventLoop.newSucceededFuture(result: ())
}
}
Loading

0 comments on commit 4045282

Please sign in to comment.