nsomar / guaka Goto Github PK
View Code? Open in Web Editor NEWThe smartest and most beautiful (POSIX compliant) Command line framework for Swift 🤖
License: MIT License
The smartest and most beautiful (POSIX compliant) Command line framework for Swift 🤖
License: MIT License
I'd really love a way to have child commands inherit flags from their parent.
For instance:
myCli
parentCommand
-c flag
fooCommand understands -c, even though it's defined on parentCommand.
This would be useful for defining common command line params to an entire 'namespace'.
Another example: a 'config' namespace / parent command has a common flag -f for config file that config set and config get could both use.
At the moments the flags are Int, String and Bool.
Maybe we can open the flags to have custom types and then convert them to these types.
At the moment the help message is generated with a default style.
This task will implement a template with placeholder (similar to mustache) to print help in other styles
Move them here https://github.com/getGuaka
Add generation cli application
This application will be able to
guaka new "name"
guaka add "name"
guaka add "name" -p "parent"
guaka add flag --longname "name" --shortname "name" --type ...
The current website lacks some of the information about what libraries are part of Guaka
This website http://piotrmurach.github.io/tty/ does a great job at highlighting its component. Maybe we can do something similar to it.
It would be nice if the tooling "guaka create" created an Xcode project with a working unit test.
Confirm with Jose
getInt(name:)
and getBool(name:)
etc could use overloads instead of unique names to distinguish the types. This impacts usage at the call site in the following ways:
In addition to overloading "get
" methods, it seems intuitive to access these via subscript. Below is a little playground snippet exploring this idea and showing some call site usage side by side.
struct Flag {
var value: Any
}
struct Flags {
let flagsDict: [String: Flag]
init(flags: [String: Flag]) {
self.flagsDict = flags
}
public subscript(name: String) -> Flag? {
return flagsDict[name]
}
public subscript(name: String) -> Bool? {
return flagsDict[name]?.value as? Bool
}
public func getBool(name: String) -> Bool? {
return flagsDict[name]?.value as? Bool
}
}
func log(_ verbose: Bool?, _ message: String) {
if verbose ?? false {
print(message)
}
}
let flags = Flags(flags: ["verbose": Flag(value: true)])
// type inferred subscript:
_ = {
let verbose: Bool? = flags["verbose"]
log(flags["verbose"], "log message")
}()
// compared to:
_ = {
let verbose = flags.getBool(name: "verbose")
log(flags.getBool(name: "verbose"), "log message")
}()
Awesome library BTW. :)
Flag with value ["1", "2", "3"] and if the user passes anything else it will throw an error
Hi, it seems that Guaka can be build on Linux, but tests are not working. This needs to happen to get swift test
running:
Tests/LinuxMain.swift
you need to change:@testable import ProcessTests
to
@testable import GuakaTests
*Tests
in files of directory Tests/GuakaTests
need to have static variable called allTests
like this one: static var allTests : [(String, (CommandArgumentsTests) -> () throws -> Void)] {
return [
("testEmptySignature", testEmptySignature),
("testRequiredArguments", testRequiredArguments),
("testOptionalArguments", testOptionalArguments),
("testExtraneousArguments", testExtraneousArguments),
("testNonTerminalWithRequiredArguments", testNonTerminalWithRequiredArguments),
("testNonTerminalWithOptionalArguments", testNonTerminalWithOptionalArguments),
("testParameterPlacement", testParameterPlacement),
("testCombinedRequiredAndOptionalArguments", testCombinedRequiredAndOptionalArguments),
("testEmptyOptionalCollectedParameter", testEmptyOptionalCollectedParameter),
("testQuotedArguments", testQuotedArguments)
]
}
Tests/LinuxMain.swift
needs to have similar definition of XTCMain
like this one:XCTMain([
testCase(CommandArgumentsTests.allTests),
testCase(CommandMessageGeneratorTests.allTests),
testCase(OptionsTests.allTests),
testCase(RouterTests.allTests),
testCase(SwiftCLITests.allTests)
])
This all will make sure, that swift test
on Linux will run all tests.
I would submit PR for this, but I'm not sure if there is order to those tests if any, so I wouldn't like to mess it up.
PS: Examples taken from similar framework called SwiftCLI
: https://github.com/jakeheis/SwiftCLI
Create a landing page for guaka
Basically, conditionally conform Array to FlagValue:
extension Array: FlagValue where Element: FlagValue {
// Implementation...
}
Allow array types to be specified multiple times on the command line, where each time it's specified you just append the item to the array.
This is a pretty common feature used by some video converting tools (ffmpeg, handbrake, etc).
ie: transcode_video supports multiple 'target' options, some for file/encoding size and others for encoding speed. So this is a perfectly valid command:
transcode-video --target big --target quick --output /path/to/output /path/to/file
The --target big
option will perform a higher quality encoding, resulting in a larger file size and slower encode. The --target quick
option performs other optimizations that speed up the encoding without noticeably reducing the quality and only increasing the file size by a small amount.
I think we can come up with a better icon
The documentation says to fail a command like so:
let printCommand = Command(usage: "print",
parent: rootCommand) { _, _ in
// Error happened
printCommand.fail(statusCode: 1, errorMessage: "Some error happaned")
}
However this code in my application results in a compilation error:
let checkConfig = Command(usage: "check",
shortMessage: "Validate your config",
parent: config,
aliases: ["validate"]
) { flags, args in
// validateConfig is a ConditionalRun used before most other commands to ensure the config is valid
if validateConfig(flags: flags, args: args) {
print("The config is valid")
} else {
// Error: variable used within its own initial value
checkConfig.fail(statusCode: 1, errorMessage: "The config is not valid")
}
}
I'm using the version 0.2.0 on Swift 4.2
The current API works like this:
Looks like this:
let show = try! Command(
name: "show",
flags: [
Flag(longName: "foo", value: "-", inheritable: false),
Flag(longName: "bar", value: "-", inheritable: false),
Flag(longName: "yy", value: true, inheritable: false),
],
commands: []) { flags, args in
print("Running git with \(flags) and \(args)")
}
let remote = try! Command(
name: "remote",
flags: [
Flag(longName: "foo", value: "-", inheritable: true),
Flag(longName: "remote", value: true, inheritable: true),
Flag(longName: "bar", value: "-", inheritable: false),
Flag(longName: "xx", value: true, inheritable: false),
],
commands: [show]) { flags, args in
print("Running git with \(flags) and \(args)")
}
let rebase = try! Command(
name: "rebase",
flags: [
Flag(longName: "varvar", value: false, shortName: "v", inheritable: true),
],
commands: []) { flags, args in
print("Running git with \(flags) and \(args)")
}
let git = try! Command(
name: "git",
flags: [
Flag(longName: "debug", type: Bool.self, required: true),
Flag(longName: "verbose", value: false, shortName: "v", inheritable: true),
Flag(longName: "togge", value: false, shortName: "t", inheritable: false),
Flag(longName: "root", value: 1, shortName: "r", inheritable: false),
],
commands: [rebase, remote]) { flags, args in
print("Running git with \(flags) and \(args)")
}
It looks like we need to do the opposite, start from sub, and then implement super, which is opposite of the way of thinking,
The main constraint here is that commands array now its immutable. It has to change to mutable to accomodate the generator process.
Whats your idea on this @goyox86
If we have this tree of commands
git
and we pass git rbase
we want Guaka to tell us, hey did you mean rebase?
Things to document
Review the public interface of the library and make sure that it makes sense
Document the public interface
Not sure if this is part of the POSIX spec or not, but would it be desirable to allow a Flag to have multiple longNames? There are certain abbreviations I use in my scripts for command names, but I like to expose/permit the unabbreviated version as well.
ie: Both --abr value
and --average-bit-rate value
would be valid
Flag("a") to be aliased to Flag("b")
Hello,
Let’s say I’m writing the git
CLI for example purpose.
I want to handle the --version
flag for all commands and subcommands. For this, I will create an inheritablePreRun
on the root command git
, which will process the --version
flag if it is set.
Then, after a little bit of coding, I’ll have to write the git submodule
commands (git submodule update
, git submodule foreach
, etc.).
For each of this commands, I’ll need to read the submodule config file (.gitmodules
).
To avoid re-implementing the logic of the parsing and error processing (command failure, etc.), I’ll want to implement an inheritablePreRun
on the git submodule
command.
However, once I do that, the root inheritablePreRun
will be ignored, thus dropping my handling of the --version
flag!
I think inheritablePreRun
s should be executed recursively from the root to the current command being executed.
Any thoughts?
Currently, fail(statusCode:errorMessage:)
is defined on Command
, but the default run
signature does not pass the command in, so the only way to actually fail a command is to do something like this:
let command = Command(usage: "foo", run: nil)
command.run = { flags, args in
command.fail(statusCode: 1, errorMessage: "bar")
}
Ideally it would be nice to do something like:
let command = Command(usage: "foo") { cmd, flags, args in
cmd.fail(statusCode: 1, errorMessage: "bar")
}
or even to allow the run
handler to throw
, in which case Guaka would print some error (or even use description
if the error is CustomStringConvertible
?)
enum FooError: Error, CustomStringConvertible {
case bar
var description: String {
switch self {
case .bar:
return "bar"
}
}
}
let command = Command(usage: "foo") { flags, args in
throw FooError.bar
}
Make sure that the flags and their types are type safe
Maybe experiment into making the flag a generic? Or add a class that contains all the flags and is then passed to the command.
This class will have methods like getInt(flagName:)
getString
getBool
When I run guaka create --help
, it looks like it outputs the content of a different command:
Usage: guaka new [name or path]
Mark flags as deprecated and then print a message when using them to use another flag instead
Show which flags are required in the help message
Such as
-f, --flag int (required)?
Hi, this seems to be very good framework for building CLI applications, but I can't find anywhere how it is licensed (LICENSE file is missing). Could you please add it?
Thanks.
We need the possibility to let the root command execute a subcommand. In our use case the root command does nothing. So it would be nice that if no subcommand is specified, a default subcommand is used.
Example:
We have a root command called rootCommand
with the sub commands foo
and bar
. foo
should be the default.
We want to call:
> rootCommand "Hello World"
instead of:
> rootCommand foo "Hello World"
This here is an idea of generating a command line application from a configuration file
So the config file will have all the commands and flags required to generate a cli app
When we choose a command from a tree of commands we dont execute anything on the super commands. This story investigate the need of adding super command run calling
$ brew install oarrabi/tap/guaka
==> Installing guaka from oarrabi/tap
==> Downloading https://github.com/oarrabi/Guaka-Generator/releases/download/0.1.1/guaka-generator-0.1.1-darwin-X64.tar.bz2
curl: (22) The requested URL returned error: 404 Not Found
Error: Failed to download resource "guaka"
Download failed: https://github.com/oarrabi/Guaka-Generator/releases/download/0.1.1/guaka-generator-0.1.1-darwin-X64.tar.bz2
The homebrew installation advertised at http://docs.getguaka.com/#getting-started fails with 404 Not Found
error (see above)
Other methods of installation, such as the command below, seem to be working:
curl https://raw.githubusercontent.com/oarrabi/Guaka-Generator/master/scripts/install.sh -sSf | bash
//cc @oarrabi
Followed along with the "Getting Started - Manually implementing Guaka"
But couldn't make it work.
Run ./.build/debug/abc "Hello from cli"
then the issue is:
hello: 'Hello from cli' is not a hello command. See 'hello --help'.
Did you mean this?
Hello from cli
From Package.swift
import PackageDescription
let package = Package(name: "abc",
dependencies: [
.Package(url: "https://github.com/oarrabi/Guaka.git", majorVersion: 0),
]
)
From main.swift
import Guaka
let command = Command(usage: "hello") { _, args in
print("You passed (args) to your Guaka app!")
}command.execute()
After run swift build
:
Compile Swift Module 'StringScanner' (5 sources)
Compile Swift Module 'Guaka' (24 sources)
Compile Swift Module 'abc' (1 sources)
Linking ./.build/debug/abc
Add preRun and postRun callbacks that will be invoked after and before run is called
Is there a way to escape a flag?
I wanted to have a command that passes along some arbitrary arguments and flags in a flag to a different script mycommand --args "--flag value"
, but Guaka complains as it doesn't see "--flag value"
as a single string
Using fastlane maybe?
The current one is kinda broken...
Is there a way to run async code?
I want to make a network request but the app exits before the network request returns
When building on Swift 4.2, warnings that the printToConsole
method should only be used in tests. This should probably be replaced with just print
.
.build/checkouts/Guaka.git--3899120711634822110/Sources/Guaka/Command/Command+Execution.swift:149:9: warning: 'printToConsole' is deprecated: This method is for enabling testability and as such should only be used in tests.
printToConsole(helpGenerator.errorString(forError: error))
^
.build/checkouts/Guaka.git--3899120711634822110/Sources/Guaka/Command/Command+Execution.swift:151:9: warning: 'printToConsole' is deprecated: This method is for enabling testability and as such should only be used in tests.
printToConsole(helpGenerator.errorString(forError: .unknownError))
^
.build/checkouts/Guaka.git--3899120711634822110/Sources/Guaka/Command/Command+Execution.swift:155:7: warning: 'printToConsole' is deprecated: This method is for enabling testability and as such should only be used in tests.
printToConsole(message)
^
.build/checkouts/Guaka.git--3899120711634822110/Sources/Guaka/Command/Command+Execution.swift:167:7: warning: 'printToConsole' is deprecated: This method is for enabling testability and as such should only be used in tests.
printToConsole(deprecationMessage)
^
.build/checkouts/Guaka.git--3899120711634822110/Sources/Guaka/Command/Command+Execution.swift:173:18: warning: 'printToConsole' is deprecated: This method is for enabling testability and as such should only be used in tests.
.forEach { printToConsole($0) }
^
.build/checkouts/Guaka.git--3899120711634822110/Sources/Guaka/Command/Command.swift:386:5: warning: 'printToConsole' is deprecated: This method is for enabling testability and as such should only be used in tests.
printToConsole(errorMessage ?? helpMessage)
^
Environment: https://github.com/oarrabi/env
File: https://github.com/oarrabi/FileSystem
Regex: https://github.com/oarrabi/regex
Runing apps: https://github.com/oarrabi/Runner
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.