Guaka - Smart and beautiful POSIX compliant CLI framework for Swift.

Overview







SwiftPM compatible Swift Version Build Status codecov Platform License MIT Analytics

Guaka - Smart and beautiful POSIX compliant CLI framework for Swift.
It helps you create modern and familiar CLI apps in the vein of widely used projects such as: Docker, Kubernetes, OpenShift, Hugo and more!.

Guaka is both a swift library and a command line application that help generate Guaka projects. Inspired by the amazing Cobra package from the Golang's ecosystem.

Is it any good?

Yes

Why?

  • Simple and idiomatic API: No rocket science here! Full modern CLI apps in a few lines of code.
  • Easy to use: With the Guaka generator you can bootstrap your own CLI in matter of minutes.
  • Lightweight and portable: No libFoundation and friends, can be statically linked.
  • POSIX compliant: Short and long flags, flags can appear anywhere.
  • Safe and crash free: 100% safe code as in: unsafe code.
  • Tested: Close to 100% test coverage and 100% dog fooded (the Guaka CLI app is written in, yes you guessed, Guaka ;).
  • Documented: Lots of docs and samples.
  • Batteries included: We created a set cross-platform Swift libraries to work with files, regular expressions, launching processes, dealing with the environment variables and colorizing ouput so you can be productive instantaneously.

In this readme

Features

  • Easy to use API: Create a modern command line app in 2 lines.
  • Super customizable Commands and Flags; customize the usage, the short message, long message, example and others
  • POSIX-Compliant flags: Handles short and long flags (-f, --flag)
  • Commands can have code sub-commands. Allowing you to create CLI apps similar to git git remote show
  • Inheritable and non-inheritable flags. Create root flags that are inherited from all the command's sub-commands
  • Batteries included: With our Args, Colorizer, Env, FileUtils, Process, Prompt, Regex and Run cross-platform libraries you can be productive instantaneously.
  • Automatically generates help message for your commands, sub-commands and flags
  • Handles user input errors with useful help messages
  • Customizable help and error messages
  • Type safe flags: specify the type of the flag when defining it and Guaka will make sure the user inputs the correct flag type
  • Custom flag support; you can define your own flag types
  • Command and Flag deprecation; guaka will the user know that they are using deprecated command/flags
  • Command and Flag aliasing; you can alias a command or a flag to different names
  • Define code that runs before the command and after the command is executed
  • Aptly documented: lots of documentation in code (we tried)
  • Levenshtein distance for subcommand names

Planned Features:

  • Generate Markdown documentation
  • Man pages and bash/zsh/tcsh completions
  • Generate a command line application from a configuration (Yaml, Taml, Json)file
  • Carthage and CocoaPods support (maybe?)

Introduction

With Guaka you can build modern command line applications that are composed of Commands and Flags.

Each command represents an action with flags that represent switches or modifiers on this command. Also, each command can have a group of sub-commands under it.

With Guaka you can build command line applications with interfaces like the following:

> git checkout "NAME Of Branch"

git command CLI has a checkout subcommand that accepts a string as its argument.

> docker ps --all

docker command CLI has ps subcommand that accepts the --all flag.

Guaka also automatically generate the command line help for your command tree. This help is accessible by passing -h or --help to any of your commands:

> docker --help
> git checkout -h

The help displays the commands, subcommands, and flag information.

Command

The Command it the main object of a Guaka CLI project. It represents a verb with a block that will get executed.

In the docker ps --all example. We have a docker command that has a ps command as its sub-command.

└── docker
    ├── ps
    └── ...

The Command class has a lot of customization objects. At its minimum, a command must have the following:

let command = Command(usage: "command") { flags, args in
  // the flags passed to the command
  // args the positional arguments passed to the command
}

Check the Command documentation

Flag

A Flag represent an option or switch that a Command accepts. Guaka supports both short and long flag formats (inline with POSIX flags).

In docker ps --all. --all is a flag that ps accepts.

Flags have lots of costumization objects. The simplest way to create a flag and add it to ps would look like the following:

let flag = Flag(longName: "all", value: false, description: "Show all the stuff")
let command = Command(usage: "ps", flags: [flag]) { flags, args in
  flags.getBool(name: "all")
  // args the positional arguments passed to the command
}

Above we defined a Flag with all as longName and a default value of false.
To read this flag in the command we use flags.getBool(...) which returns the flag value.

Check the Flag documentation

Getting started

You can create you Guaka command line application using the guaka generator app or by manually creating a swift project.

Using Guaka generator

The easiest way to use guaka is by using guaka generator command line app. This CLI app helps you generate a Guaka project.

First lets install guaka using brew:

> brew install getGuaka/tap/guaka

As an alternative, you can install guaka using the installation script (This works on macOS and Linux):

> curl https://raw.githubusercontent.com/getGuaka/guaka-cli/master/scripts/install.sh -sSf | bash

(Note: For other installation options check Guaka Generator readme.)

Check that guaka is installed:

> guaka --version
 
Version x.x.x

To understand guaka generator, let's say we want to create the following command tree:

  • git checkout
  • git remote
  • git remote show
  • git remote --some-flag

guaka create

To create a new Guaka project you can run guaka create. This command creates a new swift project and the swift project files required to have a minimal Guaka project.

guaka create behaves differently based on the parameter that is passed it:

  • If nothing is passed, the project is created in the current working folder.
  • If a name is passed, a new folder with that name will be created. This folder will contain the Guaka project.
  • If an absolute or relative path is passed. Guaka will resolve the path and create the project there.

To create the git command we described above, we do the following:

> guaka create git

The generated Guaka swift project structure will look like:

├── Package.swift
└── Sources
    ├── main.swift
    ├── root.swift
    └── setup.swift

Let's run this newly created project.

> swift build

The generated built binary will be located under ./.build/debug/git.

> ./.build/debug/git --help

Which will print out:

Usage:
  git

Use "git [command] --help" for more information about a command.

guaka add

After running guaka create we have a skeleton Guaka project. This project will only have a root command.

You can add new sub-commands to your project you can use guaka add ....

Lets add the checkout and remote command. Both these commands are sub-commands of the root.

> guaka add checkout
> guaka add remote

Next, lets add a sub-command for remote:

> guaka add show --parent remote

The generated Guaka swift project structure will look like:

├── Package.swift
└── Sources
    ├── main.swift
    ├── root.swift
    ├── checkout.swift
    ├── remote.swift
    ├── show.swift
    └── setup.swift

Adding a flag

To add a flag we need to alter the command swift file. To add a flag to our sample Command (git remote --some-flag). We edit Sources/remote.swift.

Locate the command.add(flags: []) function call and edit it to look like this:

command.add(flags: [
  Flag(longName: "some-name", value: false, description: "...")
  ]
)

Now save the file and build it with swift build. Run the built binary ./.build/debug/git -h and check the created command structure.

Check add flag documentation

Manually implementing Guaka

Alternatively, you can create a Guaka command line app by implementing Guaka in a swift project.

Adding Guaka to the project dependencies

We start by creating a swift executable project:

swift package init --type executable

Add Guaka library to your Package.swift file

import PackageDescription

let package = Package(name: "YourPackage",
  dependencies: [
    .Package(url: "https://github.com/nsomar/Guaka.git", majorVersion: 0),
  ]
)

Run swift package fetch to fetch the dependencies.

Implementing the first command

Next, lets add our first command. Go to main.swift and type in the following:

import Guaka

let command = Command(usage: "hello") { _, args in
  print("You passed \(args) to your Guaka app!")
}

command.execute()

Run swift build to build your project. Congratulations! You have created your first Guaka app.

To run it execute:

> ./.build/debug/{projectName} "Hello from cli"

You should get:

You passed ["Hello from cli"] to your Guaka app!

Check the Command documentation

Adding a flag to the command

Lets proceed at adding a flag. Go to main.swift and change it to the following:

import Guaka

let version = Flag(longName: "version", value: false, description: "Prints the version")

let command = Command(usage: "hello", flags: [version]) { flags, args in
  if let hasVersion = flags.getBool(name: "version"),
     hasVersion == true {
    print("Version is 1.0.0")
    return
  }

  print("You passed \(args) to your Guaka app!")
}

command.execute()

The above adds a flag called version. Notices how we are getting the flag using flags.getBool.

Now lets test it by building and running the command:

> swift build
> ./.build/debug/{projectName} --version

Version is 1.0.0

Check add flag documentation

Adding a subcommand

To add a subcommand we alter main.swift. Add the following before calling command.execute()

// Create the command
...

let subCommand = Command(usage: "sub-command") { _, _ in
  print("Inside subcommand")
}

command.add(subCommand: subCommand)

command.execute()

Now build and run the command:

> swift build
> ./.build/debug/{projectName} sub-command

Inside subcommand

Check [add sub command](Check add flag documentation)

Displaying the command help message

Guaka automatically generates help for your commands. We can get the help by running:

> ./.build/debug/{projectName} --help

Usage:
  hello [flags]
  hello [command]

Available Commands:
  sub-command

Flags:
      --version   Prints the version

Use "hello [command] --help" for more information about a command.

Notice how the command the sub-command and flag info are displayed.

Read more about the help message

Cross-Platform utility libraries aka batteries

Writing a command line application is more than just parsing the command line arguments and flags.

Swift ecosystem is still very young and lacks of a cross-platform standard library. We did not wanted to make Guaka depend on libFoundation, so we rolled up our sleeves and built a few small cross-platform (as in whenever there is a usable C standard library) libraries. so you don't have to and can be productive instantaneously. Also , they are usable on their own. You are welcome to use them too! <3:

  • FileUtils: Help you work with files, directories and paths.
  • Regex: Match and capture regex.
  • Process: Launch external programs and capture their standard output and standard error.
  • Env: Read and write environment variables sent to your process.

Documentation

Command documentation

Command represents the main class in Guaka. It encapsulates a command or subcommand that Guaka defines.

For the full Command documentation

Usage and Run block

As a minimum, a command needs a usage string and a Run block. The usage string describes how this command can be used.

  • If the usage is a single string command-name; the command will have that name
  • If the usage is a string with spaces command-name args..; the command name is the first segment of the string.
let c = Command(usage: "command-name") { _, args in
}

The Run block gets called with two parameters. The Flags class which contains the flags passed to the command and the args which is an array of arguments passed to the command.

The Command constructor takes lots of parameters. However most of them have sensible defaults. Feel free to fill as much or as little of the parameters as you want:

Command(usage: "...",
        shortMessage: "...",
        longMessage: "...",
        flags: [],
        example: "...",
        parent: nil,
        aliases: [],
        deprecationStatus: .notDeprecated,
        run: {..})

At a minimum, you need to pass the usage and the run block. Refer to the code documentation for info about the parameters.

Check the Flags documentation

Adding Sub-commands to the command

Commands are organised in a tree structure. Each command can have zero, one or many sub-commands associated with it.

We can add a sub-command by calling command.add(subCommand: theSubCommand). If we wanted to add printCommand as a sub-command to rootCommand, we would do the following:

let rootCommand = //Create the root command
let printCommand = //Create the print command

rootCommand.add(subCommand: printCommand)

Alternatively, you can pass the rootCommand as the parent when creating the printCommand:

let rootCommand = //Create the root command
let printCommand = Command(usage: "print",
                           parent: rootCommand) { _, _ in
}

Our command line application will now respond to both:

> mainCommand
> mainCommand print

You can build your command trees in this fashion and create modern, complex, elegant command line applications.

Short and Long messages

The Command defines the shortMessage and the longMessage. These are two strings that get displayed when showing the Command help.

Command(usage: "print",
        shortMessage: "prints a string",
        longMessage: "This is the long mesage for the print command") { _, _ in
} 

The shortMessage is shown when the command is a sub-command.

> mainCommand -h

Usage:
  mainCommand [flags]
  mainCommand [command]

Available Commands:
  print    prints a string

Use "mainCommand [command] --help" for more information about a command.
Program ended with exit code: 0

The longMessage is shown when getting help of the current command

> mainCommand print -h

This is the long message for the print command

Usage:
  mainCommand print

Use "mainCommand print [command] --help" for more information about a command.
Program ended with exit code: 0

Command flags

You can add a Flag to a command in two ways.

You can pass the flags in the constructor:

let f = Flag(longName: "some-flag", value: "value", description: "flag information")

let otherCommand = Command(usage: "print",
        shortMessage: "prints a string",
        longMessage: "This is the long mesage for the print command",
        flags: [f]) { _, _ in
}

Alternatively, you can call command.add(flag: yourFlag).

Now the flag will be associated with the command. We can see it if we display the help of the command.

> mainCommand print -h

This is the long message for the print command

Usage:
  mainCommand print [flags]

Flags:
      --some-flag string  flag information (default value)

Use "mainCommand print [command] --help" for more information about a command.

Command example section

You can attach a textual example on how to use the command. You do that by setting the example variable in the Command (or by filling the example parameter in the constructor):

printCommand.example = "Use it like this `mainCommand print \"the string to print\""

Then we can see it in the command help:

> mainCommand print -h

This is the long message for the print command

Usage:
  mainCommand print

Examples:
Use it like this `mainCommand print "the string to print"

Use "mainCommand print [command] --help" for more information about a command.
Command aliases and deprecation

You can mark a command as deprecated by setting the deprecationStatus on the command.

printCommand.deprecationStatus = .deprecated("Dont use it")

When the user call this command, a deprecation message will be displayed.

Aliases help giving command alternative names. We can have both print and echo represent the same command:

printCommand.aliases = ["echo"]

Different kind of Run Hooks

The command can have different run Hooks. If they are set, they will be executed in this order.

  • inheritablePreRun
  • preRun
  • run
  • postRun
  • inheritablePostRun

When a command is about to execute. It will first search for its parent list. If any of its parents have an inheritablePreRun then Guaka will first execute that block.

Next the current command preRun is executed. Followed by the run and the postRun.

After that, as with the inheritablePreRun, Guaka will search for any parent that has an inheritablePostRun and execute that too.

All of inheritablePreRun, preRun, postRun and inheritablePostRun blocks return a boolean. If they return false then the command execution will end.

This allows you to create smart command trees where the parent of the command can decide if any of it sub-commands must continue executing.

For example. The parent command can define a version flag. If this flag is set, the parent will handle the call and return false from its inheritablePreRun. Doing that help us to not repeat the version handling in each sub-command.

The example bellow shows this use case:

// Create root command
let rootCommand = Command(usage: "main")  { _, _ in
  print("main called")
}

// Create sub command
let subCommand = Command(usage: "sub", parent: rootCommand) { _, _ in
  print("sub command called")
}

// Add version flag to the root
// We made the version flag inheritable 
// print will also have this flag as part of its flags
let version = Flag(longName: "version", value: false,
                   description: "Prints the version", inheritable: true)

rootCommand.add(flag: version)
rootCommand.inheritablePreRun = { flags, args in
  if
    let version = flags.getBool(name: "version"),
    version == true {
    print("Version is 0.0.1")
    return false
  }

  return true
}

rootCommand.execute()

Now we can get the version by calling:

> main --version
> main sub --version

Exiting early from a command

In some sitiuation you might want to exit early from a command you can use command.fail(statusCode: errorCode, errorMessage: "Error message")

let printCommand = Command(usage: "print",
                           parent: rootCommand) { _, _ in
    // Error happened
    printCommand.fail(statusCode: 1, errorMessage: "Some error happaned")
}

Flag documentation

A Flag represent an option or switch that a Command accepts. Guaka defines 4 types of flags; integer, boolean, string and custom types.

Check the full Flag documentation

Creating a flag with default value

To create a Flag with default value, we call do the following:

let f = Flag(longName: "version", value: false, description: "prints the version")

We created a flag that has a longName of version. Has a default value of false and has a description. This creates a POSIX compliant flag. To set this flag:

> myCommand --version
> myCommand --version=true
> myCommand --version true

Flag is a generic class, in the previous example, since we set false as its value, that creates a boolean Flag. If you try to pass a non-bool argument in the terminal, Guaka will display an error message.

The flag constructor, as with the command, defines lots of parameters. Most of them have sensible defaults, so feel free to pass as much, or little, as you need.

For example, we could set the flag short name by doing this:

Flag(shortName: "v", longName: "version", value: false, description: "prints the version")

Now we can either use -v or --version when calling the command.

Creating a flag with flag type

We can create a flag that has no default value. This type of flag can be marked as optional or required.

To create an optional flag

Flag(longName: "age", type: Int.self, description: "the color")

Here we defined a flag that has an int value. If we execute the command with a non-integer value, Guaka will inform us of an error.

A required flag can be created by passing true to the required argument in the Flag constructor:

Flag(longName: "age", type: Int.self, description: "the color", required: true)

Now if we call the command without setting the --age=VALUE. Guaka will display an error.

Reading the flag values

When the Command run block is called, a Flags argument will be sent to the block. This Flags argument contains the values for each flag the command defined.

This example illustrate flag reading:

// Create the flag
var uppercase = Flag(shortName: "u", longName: "upper",
                     value: false, description: "print in bold")

// Create the command
let printCommand = Command(usage: "print", parent: rootCommand) { flags, args in

  // Read the flag
  let isUppercase = flags.getBool(name: "upper") ?? false

  if isUppercase {
    print(args.joined().uppercased())
  } else {
    print(args.joined())
  }
}

// Add the flag
printCommand.add(flag: uppercase)

Let's execute this command:

print -u "Hello World" HELLO WORLD ">
> print "Hello World"

Hello World

> print -u "Hello World"

HELLO WORLD

Flags class defines methods to read all the different type of flags:

  • func getBool(name: String) -> Bool?
  • func getInt(name: String) -> Int?
  • func getString(name: String) -> String?
  • func get(name: String, type: T.Type) -> T?

Check the full Flags documentation

Inheritable flags

Flags that are set to a parent Command can be also inherited to the sub-commands by passing true to the inheritable argument in the flag constructor.

To create an inheritable flag:

var version = Flag(longName: "version", value: false,
                   description: "print in bold", inheritable: true)

rootCommand.add(flag: version)

This makes --version a flag that can be set in the rootCommand and any of its sub-commands.

Flag deprecation

As with a Command, a Flag can be set to be deprecated by setting it's deprecationStatus:

var version = Flag(longName: "version", value: false,
                   description: "print in bold", inheritable: true)
version.deprecationStatus = .deprecated("Dont use this flag")

Guaka will warn each time this flag is set.

Flag with custom types

Out of the box, you can create flags with integer, boolean and string values and types. If you however, want to define custom types for your flags, you can do it by implementing the FlagValue protocol.

Let's define a flag that has a User type:

// Create the enum
enum Language: FlagValue {
  case english, arabic, french, italian

  // Try to convert a string to a Language
  static func fromString(flagValue value: String) throws -> Language {
    switch value {
    case "english":
      return .english
    case "arabic":
      return .arabic
    case "french":
      return .french
    case "italian":
      return .italian
    default:

      // Wrong parameter passed. Throw an error
      throw FlagValueError.conversionError("Wrong language passed")
    }
  }

  static var typeDescription: String {
    return "the language to use"
  }
}

// Create the flag
var lang = Flag(longName: "lang", type: Language.self, description: "print in bold")

// Create the command
let printCommand = Command(usage: "print", parent: rootCommand) { flags, args in

  // Read the flag
  let lang = flags.get(name: "lang", type: Language.self)
  // Do something with it
}

// Add the flag
printCommand.add(flag: lang)

// Execute the command
printCommand.execute()

Notice that incase the argument is not correct we throw a FlagValueError.conversionError. This error will be printed to the console.

> print --lang undefined "Hello"

Error: wrong flag value passed for flag: 'lang' Wrong language passed
Usage:
  main print [flags]

Flags:
      --lang the language to use  print in bold 

Use "main print [command] --help" for more information about a command.

wrong flag value passed for flag: 'lang' Wrong language passed
exit status 255

Check the full FlagValue documentation and the FlagValueError documentation.

Help customization

Guaka allows you to customize the format of the generated help. You can do that by implementing the HelpGenerator and passing your class to GuakaConfig.helpGenerator.

The HelpGenerator protocol defines all the sections of the help message that you can subclass. HelpGenerator provides protocol extensions with defaults for all the section. That allows you to cherry-pick which sections of the help you want to alter.

Each of the variable and section in the HelpGenerator corresponds to a section in the printed help message. To get the documentation of each section, refer to the in-code documentation of HelpGenerator.

Say we only want to change the usageSection of the help, we would do the following:

struct CustomHelp: HelpGenerator {
  let commandHelp: CommandHelp

  init(commandHelp: CommandHelp) {
    self.commandHelp = commandHelp
  }

  var usageSection: String? {
    return "This is the usage section of \(commandHelp.name) command"
  }
}

GuakaConfig.helpGenerator = CustomHelp.self

Any HelpGenerator subclass will have a commandHelp variable which is an instance of CommandHelp structure. This structure contains all the info available for a command.

Check the full HelpGenerator documentation

Tests

Tests can be found here.

Run them with

swift test

Future work

  • Levenshtein distance for subcommand names
  • Generate Markdown documentation
  • Man pages and bash/zsh/tcsh completions
  • Generate a command line application from a configuration (Yaml, Taml, Json)file
  • Carthage and Cocoapod support

For a list of task planned, head to the Guaka GitHub project

Contributing

Just send a PR! We don't bite ;)

Comments
  • Multiple longNames

    Multiple longNames

    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

    opened by Ponyboy47 4
  • Improve method of failing a command

    Improve method of failing a command

    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
    }
    
    opened by a2 4
  • Move projects to Guaka github profile

    Move projects to Guaka github profile

    Move them here https://github.com/getGuaka

    • [x] Environment: https://github.com/oarrabi/env
    • [x] File: https://github.com/oarrabi/FileSystem
    • [x] Regex: https://github.com/oarrabi/regex
    • [x] Runing apps: https://github.com/oarrabi/Runner
    • [ ] Main: https://github.com/nsomar/Guaka
    opened by nsomar 4
  • Additions

    Additions

    Added global fail methods Discourage direct usage of printToConsole by deprecating the method (compiles with a warning) Added another get method to Flags to get arrays of values of a flag

    opened by Ponyboy47 3
  • How to properly fail a command?

    How to properly fail a command?

    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

    opened by Ponyboy47 3
  • Don't recomend getting more information when there are no subcommands

    Don't recomend getting more information when there are no subcommands

    Currently, if you run a command with --help and that command has no subcommands, there will be a message printed by the default HelpGenerator that says: Use "executable commandName [command] --help" for more information about a command.

    This is not necessary if there are no subcommands.

    opened by Ponyboy47 2
  • Support options being used multiple times

    Support options being used multiple times

    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.

    opened by Ponyboy47 2
  • homebrew install fails with 404 error

    homebrew install fails with 404 error

    $ 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

    opened by loyaltyarm 2
  • Add badges

    Add badges

    Below the title please add badges this help a lot.

    If your component is compatible:

    • with SwiftPM add SwiftPM compatible

    Indicated:

    • the Swift version Swift Version
    • Platforms supported Plaforms
    • License License MIT
    opened by TofPlay 2
  • Manually implementing Guaka issue

    Manually implementing Guaka issue

    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

    opened by pb-z 2
  • License for this project

    License for this project

    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.

    opened by rbukovansky 2
  • Inheritable flags. (Feature Request)

    Inheritable flags. (Feature Request)

    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.

    opened by codefriar 0
  • Escape values

    Escape values

    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

    opened by yonaskolb 1
  • Rework the landing page getguaka.com

    Rework the landing page getguaka.com

    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.

    help wanted 
    opened by nsomar 2
  • Consider overloading the Flags subscript instead of including types in method names

    Consider overloading the Flags subscript instead of including types in method names

    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:

    1. When called nakedly, a type annotation may need used to disambiguate the method.
    2. When the type is able to be inferred, the call site avoids adding redundant type information.

    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. :)

    opened by ianterrell 1
Owner
Omar Abdelhafith
Software Engineer @facebook, computer geek, rock-metal fan, who believes in parallel universes and UFOs. In ❤️ with Swift/Objc and Elixir.
Omar Abdelhafith
Swift-cli - Example of building command-line tools in Swift

swift-cli Example of building command-line tools in Swift Step 1: Create CLI wit

Noah Gift 2 Jan 17, 2022
An extremely simple CLI tool that was created to diagnose and further understand an issue in DriverKit causing kIOHIDOptionsTypeSeizeDevice to behave incorrectly when used in DriverKit system extensions.

IOKitHIDKeyboardTester This tool is NOT useful to, or intended for general users. IOKitHIDKeyboardTester is an extremely simple (one-file!) CLI tool t

Karen/あけみ 6 Jul 22, 2022
A CLI too powered by Swift to provision environments using an up.toml manifest file

tuist-up tuist up was originally a Tuist built-in command to provision environments by reading the requirements in a manifest file Setup.swift. Althou

Tuist 5 Mar 31, 2022
A starting point to create CLI utilities with swift

cli tuist template A starting point to create CLI utilities with swift Installation Just create a Tuist folder and a Templates folder inside it. Creat

humdrum 6 May 3, 2022
A Tuist Template to quickly create CLI apps in Swift

macOS CLI Template Motivation I'm writing more and more Swift CLI apps these days. And as I solve more problems with this litte tools, I find that I'm

Diego Freniche 21 Dec 28, 2022
A cli program written in swift (with async/await) that removes the unnecessary parts of xcframeworks.

xctrim A cli program written in swift (with async/await) that removes the unnecessary parts of xcframeworks. Usecase Say you downloaded firebase sdk a

Mustafa Yusuf 32 Jul 18, 2022
Snpm - Snippet Manager CLI With Swift

Snippet Manager CLI Usage foo@bar:~$ snpm --help OVERVIEW: Utill for searching s

Gleb Zavyalov 5 Jan 5, 2023
ConcurrentTest - Swift macOS CLI example on how to NOT do concurrent coding

Swift Concurrent Test Open the .xcodeproj file in Xcode, and try running it seve

Thisura Dodangoda 0 Jan 21, 2022
A silly CLI for replacing macosx.internal SDK settings in .xcodeproj files with macosx

fix-macos-internal-sdk A silly CLI for replacing macosx.internal SDK settings in .xcodeproj files with macosx.

Keith Smiley 14 Dec 6, 2022
CLI tool for macOS that transcribes speech from the microphone using Apple’s speech recognition API, SFSpeechRecognizer. (help.)

CLI tool for macOS that uses SFSpeechRecognizer to transcribe speech from the microphone. The recognition result will be written to the standard output as JSON string.

Thai Pangsakulyanont 23 Sep 20, 2022
Phiole - Allow to write or read from standards stream or files for script or CLI application

No longer maintained! Phiole - Φole Simple object to wrap three NSFileHandle: 'output', 'error' to write and 'input' to read There is of course a defa

Eric Marchand 8 Sep 10, 2018
A CLI utility to check or uncheck Open Using Rosetta preference for Apple Silicon macs.

SetArchPrefForURL A CLI utility to "check" or "uncheck" "Open Using Rosetta" preference for Apple Silicon macs. Usage: SetArchPrefForURL <path-to-the-

Tapan Thaker 19 Oct 31, 2022
A CLI to Alcatraz, the Xcode package manager.

Azkaban This project is deprecated in favor of Editor Extensions support in Xcode 8+. A CLI to Alcatraz, the Xcode package manager. Usage Install a pl

Boris Bügling 64 Jun 3, 2021
Compose beautiful command line interfaces in Swift

Commander is a small Swift framework allowing you to craft beautiful command line interfaces in a composable way. Usage Simple Hello World i

Kyle Fuller 1.5k Dec 29, 2022
Progress.swift ⌛ Add beautiful progress bars to your loops.

Progress.swift ⌛ Just wrap the SequenceType in your loop with the Progress SequenceType and you'll automatically get beautiful progress bars. Updating

Justus Kandzi 304 Dec 1, 2022
SwiftCLI - A powerful framework for developing CLIs in Swift

SwiftCLI A powerful framework for developing CLIs, from the simplest to the most complex, in Swift.

Jake Heiser 793 Jan 4, 2023
SwiftShell - A Swift framework for shell scripting.

Run shell commands | Parse command line arguments | Handle files and directories Swift 5.1 - 5.3 | Swift 4 | Swift 3 | Swift 2 SwiftShell A library fo

Kare Morstol 973 Jan 2, 2023
This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "make it black" and change the background color of the view in the frame.

VoiceOperationSample This app is a sample app that recognizes specific voice commands such as "make it red", "make it blue", "make it green", and "mak

Takuya Aso 3 Dec 3, 2021
Util for executing shell commands, and getting the results easily(data, string, and any decodable).

ShellExecutor Util for executing shell commands, and getting the results easily(data, string, and any decodable). Requirements Xcode 14.0+ Swift 5.7+

Yozone Wang 2 Jul 30, 2022