Skip to content

Commit

Permalink
Merge pull request #115 from phase2/feature/spinners
Browse files Browse the repository at this point in the history
Add Spinner implementation for longer-running operations.
  • Loading branch information
febbraro authored Nov 17, 2017
2 parents 5242e0c + 140b317 commit e11e398
Show file tree
Hide file tree
Showing 27 changed files with 510 additions and 304 deletions.
126 changes: 126 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# CONTRIBUTING

Thank you for considering contributing to the Outrigger CLI!

## Quality Contributions

* Make sure your branch will compile.
* Make sure your branch passes our static analysis checks.
* Make sure your branch conforms with go fmt standards.
* Manually test your changes.

## User Interactions

One of the key goals of this project is to promote a positive developer
experience. Every interaction should be thought of with the following points:

* Are you providing the user with enough context about what's being asked or being done?
* Does the user expect to wait? Might the user think the tool stalled?
* Is there black box business happening that could be made more transparent?

We have a slightly complex logging API to support addressing these concerns.
(See ./util/logging.go)

Here are a few conventions:

* **Starting a task that could take more than 5 seconds:**
* `cmd.out.Spin("Preparing the sauce")`
* **Use the correct method to log operational results: (Pick one)**
* `cmd.out.Info("Sauce is Ready.")`
* `cmd.out.Warning("Sauce is burnt on the bottom.")`
* `cmd.out.Error("Discard this sauce and try again.")`
* **Going to send some contextual notes to the user**:
1. `cmd.out.NoSpin()` if currently using the spinner.
2. `cmd.out.Info("Sauce exists.")`
4. `cmd.out.Verbose("The ingredients of the sauce include tomato, salt, black pepper, garlic...")`
* **Command has executed and is successful. Please no notification:**
```
cmd.out.Info("Enjoy your dinner.")
return cmd.Success("")
```
* **Command has executed and is successful. Get a notification too!**
```
return cmd.Success("Enjoy your dinner.")
```
* **Command failed:**
```
message := "Cooking sauce is hard, we failed"
cmd.out.Error("%s: %s", message, err.Error())
return cmd.Failure(message)
```

## Development Environment Setup

### Developing with Docker

You can use the Docker integration within this repository to facilitate development in lieu of setting up a
local golang environment. Using docker-compose, run the following commands:

```bash
docker-compose run --rm install
docker-compose run --rm compile
```

This will produce a working OSX binary at `build/darwin/rig`.

If you change a dependency in `Gopkg.toml` you can update an individual package dependency with:

```bash
docker-compose run --rm update [package]
```

If you want to update all packages use:

```bash
docker-compose run --rm update
```

If you want to run the static analysis checks:

```bash
docker-compose run --rm lint
```

If you want to run go fmt against the codebase:
```bash
docker-compose run --rm base go fmt ./...
```

### Developing Locally

Install go from homebrew using the flag to include common cross-compiler targets (namely Darwin, Linux, and Windows)

```bash
brew install go --with-cc-common
brew install dep
brew tap goreleaser/tap
brew install goreleaser/tap/goreleaser
```

Setup `$GOPATH` and `$PATH` in your favorite shell (`~/.bashrc` or `~/.zshrc`)

```bash
export GOPATH=$HOME/Projects
export PATH=$PATH:$GOPATH/bin
```

Checkout the code into your `$GOPATH` in `$GOPATH/src/github.com/phase2/rig`

Get all the dependencies

```bash
# Install the project dependencies into $GOPATH
cd $GOPATH/src/github.com/phase2/rig
dep ensure
```

#### Building Rig

If you want to build `rig` locally for your target platform, simply run the following command:

```bash
GOARCH=amd64 GOOS=darwin go build -o build/darwin/rig cmd/main.go
```

This command targets an OS/Architecture (Darwin/Mac and 64bit) and puts the resultant file in the `build/darwin/`
with the name `rig`. Change `GOARCH` and `GOOS` if you need to target a different platform
7 changes: 7 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@
name = "github.com/martinlindhe/notify"
branch = "master"

[[constraint]]
name = "github.com/slok/gospinner"
version = "0.1.0"

101 changes: 17 additions & 84 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,80 +1,28 @@
# Rig - Outrigger CLI [![Build Status](https://travis-ci.org/phase2/rig.svg?branch=develop)](https://travis-ci.org/phase2/rig)

> A CLI for managing the Outrigger container-driven development stack.
> A CLI for managing the Outrigger's container-driven development stack.
See the [documentation for more details](http://docs.outrigger.sh).
See the [CONTRIBUTING.md](./CONTRIBUTING.md) for developer documentation.

Use this readme when you want to develop the Outrigger CLI.
## Built on Dependencies

Setup
------

Install go from homebrew using the flag to include common cross-compiler targets (namely Darwin, Linux, and Windows)

```bash
brew install go --with-cc-common
brew install dep
brew tap goreleaser/tap
brew install goreleaser/tap/goreleaser
```

Setup `$GOPATH` and `$PATH` in your favorite shell (`~/.bashrc` or `~/.zshrc`)

```bash
export GOPATH=$HOME/Projects
export PATH=$PATH:$GOPATH/bin
```

Checkout the code into your `$GOPATH` in `$GOPATH/src/github.com/phase2/rig`

Get all the dependencies

```bash
# Install the project dependencies into $GOPATH
cd $GOPATH/src/github.com/phase2/rig
dep ensure
```

Developing Locally
-------------------

If you want to build `rig` locally for your target platform, simply run the following command:

```bash
GOARCH=amd64 GOOS=darwin go build -o build/darwin/rig cmd/main.go
```

This command targets an OS/Architecture (Darwin/Mac and 64bit) and puts the resultant file in the `build/darwin/`
with the name `rig`. Change `GOARCH` and `GOOS` if you need to target a different platform

Developing with Docker
-----------------------

You can use the Docker integration within this repository to facilitate development in lieu of setting up a
local golang environment. Using docker-compose, run the following commands:

```bash
docker-compose run --rm install
docker-compose run --rm compile
```

This will produce a working OSX binary at `build/darwin/rig`.

If you change a dependency in `Gopkg.toml` you can update an individual package dependency with:

```bash
docker-compose run --rm update [package]
```

If you want to update all packages use:

```bash
docker-compose run --rm update
```
We make use of a few key libraries to do all the fancy stuff that the `rig` CLI will do.

* https://github.com/urfave/cli
* The entire CLI framework from helps text to flags.
This was an easy cli to build b/c of this library.
* https://github.com/fatih/color
* All the fancy terminal color output
* https://github.com/bitly/go-simplejson
* The JSON parse and access library used primarily with the output
of `docker-machine inspect`
* https://gopkg.in/yaml.v2
* The YAML library for parsing/reading YAML files
* https://github.com/martinlindhe/notify
* Cross-platform desktop notifications

Release
-------
## Release Intructions

We use [GoReleaser](https://goreleaser.com) to handle nearly all of our release concerns. GoReleaser will handle

Expand All @@ -90,18 +38,3 @@ To create a new release of rig:
* Run `docker-compose run --rm goreleaser`
* ...
* Profit!


Dependencies
-------------

We make use of a few key libraries to do all the fancy stuff that the `rig` CLI will do.

* https://github.com/urfave/cli
* The entire CLI framework from helps text to flags. This was an easy cli to build b/c of this library
* https://github.com/fatih/color
* All the fancy terminal color output
* https://github.com/bitly/go-simplejson
* The JSON parse and access library used primarily with the output of `docker-machine inspect`
* https://gopkg.in/yaml.v2
* The YAML library for parsing/reading YAML files
17 changes: 13 additions & 4 deletions commands/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,25 @@ func (cmd *BaseCommand) Before(c *cli.Context) error {

// Success encapsulates the functionality for reporting command success
func (cmd *BaseCommand) Success(message string) error {
// Handle success messaging.
if message != "" {
cmd.out.Info.Println(message)
cmd.out.Info(message)
util.NotifySuccess(cmd.context, message)
}

// If there is an active spinner wrap it up.
cmd.out.NoSpin()

return nil
}

// Error encapsulates the functionality for reporting command failure
func (cmd *BaseCommand) Error(message string, errorName string, exitCode int) error {
// Failure encapsulates the functionality for reporting command failure
func (cmd *BaseCommand) Failure(message string, errorName string, exitCode int) error {
// Make sure any running spinner halts.
cmd.out.NoSpin()
// Handle error messaging.
util.NotifyError(cmd.context, message)

return cli.NewExitError(fmt.Sprintf("ERROR: %s [%s] (%d)", message, errorName, exitCode), exitCode)
}

Expand All @@ -64,6 +73,6 @@ func (cmd *BaseCommand) NewContext(name string, flags []cli.Flag, parent *cli.Co
// SetContextFlag set a flag on the provided context
func (cmd *BaseCommand) SetContextFlag(ctx *cli.Context, name string, value string) {
if err := ctx.Set(name, value); err != nil {
cmd.out.Error.Fatal(err)
cmd.out.Channel.Error.Fatal(err)
}
}
2 changes: 1 addition & 1 deletion commands/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (cmd *Config) Run(c *cli.Context) error {
os.Stdout.Write(output)
}
} else {
return cmd.Error(fmt.Sprintf("No machine named '%s' exists.", cmd.machine.Name), "MACHINE-NOT-FOUND", 12)
return cmd.Failure(fmt.Sprintf("No machine named '%s' exists.", cmd.machine.Name), "MACHINE-NOT-FOUND", 12)
}

return cmd.Success("")
Expand Down
26 changes: 17 additions & 9 deletions commands/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,16 @@ func (cmd *Dashboard) Commands() []cli.Command {
// Run executes the `rig dashboard` command
func (cmd *Dashboard) Run(ctx *cli.Context) error {
if cmd.machine.IsRunning() || util.SupportsNativeDocker() {
cmd.out.Info.Println("Launching Dashboard")
return cmd.LaunchDashboard(cmd.machine)
cmd.out.Info("Launching Dashboard")
err := cmd.LaunchDashboard(cmd.machine)
if err != nil {
// Success may be presumed to only execute once per command execution.
// This allows calling LaunchDashboard() from start.go without success.
return cmd.Success("")
}
}

return cmd.Error(fmt.Sprintf("Machine '%s' is not running.", cmd.machine.Name), "MACHINE-STOPPED", 12)
return cmd.Failure(fmt.Sprintf("Machine '%s' is not running.", cmd.machine.Name), "MACHINE-STOPPED", 12)
}

// LaunchDashboard launches the dashboard, stopping it first for a clean automatic update
Expand All @@ -49,12 +54,16 @@ func (cmd *Dashboard) LaunchDashboard(machine Machine) error {
// except to indicate the age of the image before update in the next section.
_, seconds, err := util.ImageOlderThan(dashboardImageName, 86400*30)
if err == nil {
cmd.out.Verbose.Printf("Local copy of the dashboardImageName '%s' was originally published %0.2f days ago.", dashboardImageName, seconds/86400)
cmd.out.Verbose("Local copy of the dashboardImageName '%s' was originally published %0.2f days ago.", dashboardImageName, seconds/86400)
}

cmd.out.Verbose.Printf("Attempting to update %s", dashboardImageName)
// Updating the dashboard is rarely of interest to users so uses verbose logging.
// Per our user interaction practices, we would normally use a spinner here.
cmd.out.Verbose("Attempting to update %s", dashboardImageName)
if err := util.StreamCommand("docker", "pull", dashboardImageName); err != nil {
cmd.out.Verbose.Println("Failed to update dashboard image. Will use local cache if available.")
cmd.out.Verbose("Failed to update dashboard image. Will use local cache if available.")
} else {
cmd.out.Verbose("Successfully updated dashboard.")
}

dockerAPIVersion, _ := util.GetDockerServerAPIVersion()
Expand All @@ -71,16 +80,15 @@ func (cmd *Dashboard) LaunchDashboard(machine Machine) error {
}

util.ForceStreamCommand("docker", args...)

if util.IsMac() {
util.Command("open", "http://dashboard.outrigger.vm").Run()
} else if util.IsWindows() {
util.Command("start", "http://dashboard.outrigger.vm").Run()
} else {
cmd.out.Info.Println("Outrigger Dashboard is now available at http://dashboard.outrigger.vm")
cmd.out.Info("Outrigger Dashboard is now available at http://dashboard.outrigger.vm")
}

return cmd.Success("")
return nil
}

// StopDashboard stops and removes the dashboard container
Expand Down
Loading

0 comments on commit e11e398

Please sign in to comment.