Skip to content

Commit

Permalink
Merge pull request #143 from onflow/feature/sharedlib
Browse files Browse the repository at this point in the history
Feature/sharedlib
  • Loading branch information
sideninja authored Apr 7, 2021
2 parents 84d46e0 + bea6140 commit ba57bac
Show file tree
Hide file tree
Showing 164 changed files with 9,368 additions and 4,046 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ index.html
# Ignore flow json config
flow.json
flow*.json
!tests/flow.json

# IDE related files
.idea
Expand Down
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ linters:
- ineffassign
- typecheck
- misspell
- goimports
- unused
linters-settings:
goimports:
# put imports beginning with prefix after 3rd-party package
local-prefixes: github.com/onflow/flow-cli
243 changes: 243 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,249 @@ We try to follow the coding guidelines from the Go community.
- Code should be commented
- Code should pass all tests: `make test`

# CLI Guidelines
This is a design guideline used for the development of the Flow CLI. The purpose of this guideline is to achieve consistency across new features and allow composability of commands that build the fundamentals of great CLI design.

> Whatever software you’re building, you can be absolutely certain that people will
> use it in ways you didn’t anticipate. Your software will become a part in a larger system
> — your only choice is over whether it will be a well-behaved part. - [Clig](https://clig.dev/)

# Human Interaction

Our interaction with the CLI is done by executing CLI commands and passing arguments
and flags which need to be consistent. Consistency will allow interactions to
be predictable and will with time feel natural even without resorting to reading instructions.

## Commands
Be consistent across commands and subcommands. In case we define a subcommand we
should use `noun verb` pattern. **Don’t have ambiguous or similarly-named commands.**
Naming and language should be the same as used in the rest of Flow's documentation.
Use only lowercase letters, and dashes if you really need to. Keep it short.
Users will be typing it all the time.

```
flow accounts
```

## Flags
Flags are **named parameters**, denoted with either a hyphen, and a
single-letter name (`-r`) or a double hyphen and a multiple-letter
name (`--recursive`). The longer version is preferred for better readability
and simpler learning experience in the beginning but after a while users will
probably start using the shorter version.

Every flag should have a shorter version and they should be both presented in the help.
Every flag that can have a default value should have a default value
so users don’t have to specify all of them when not needed.

Support flags following a value delimited by a space or
equal (=) sign (ex: `--username test` or `--username=test`)

```
flow accounts get --filter "address"
```

Use common naming for default falgs such as:

`--log`: Logging level.

`--output`: Output format (JSON, inline etc...).

`-h, --help`: Help. This should only mean help. See the help section.

`--version`: Version.


## Arguments
Arguments, or args, are positional parameters to a command.
There should be as few arguments as possible, because it is hard for users to remember the sequence of arguments.

Because they are relying on position, flags should be used where more than one argument would be required.

Arguments should be one word verbs following general naming guideline.
**Prefer flags to arguments**.

Use an argument for the value that command requires in order to run.

```
flow accounts get <address>
```

## Robustness
CLI should feel robust. Long operation should also provide output as they are processing.
**Responsivness is more important than speed.**

Warn before you make a non-additive change. Eventually, you’ll find that you can’t
avoid breaking an interface. Before you do, forewarn your users in the program itself:
when they pass the flag you’re looking to deprecate, tell them it’s going to change soon.
Make sure there’s a way they can modify their usage today to make it
future-proof, and tell them how to do it.

# Computer Interaction

The CLI will respond to execution of commands by providing some output.
Output should with the same reasons as input be consistent and predictable,
even further it should follow some industry standards and thus allow great strength
of CLI use: composability. Output should be served in the right dosage. We shouldn't
output lines and lines of data because we will shadow the important part the same
way we shouldn't let the CLI command run in background not providing any output for
minutes as it is working because the user will start to doubt if the command is broken.
It's important to get this balance right.

## Interaction
Commands must be stateless and idempotent, meaning they can be run without relying on external state.
If we have some external state, like the configuration, each command must include an option to
define it on the fly, but it must also work without it by first relying on the externally
saved state in the configuration, and if not found, relying on a default value.
When relying on a default value results in an error, there should be an explanation for the user that a configuration should be created.

We try to use default values first to get that “works like magic” feeling.

Never require a prompt for user input. Always offer a flag to provide that input.
However, if a user doesn't provide required input we can offer a prompt as an alternative.


## Output

“Expect the output of every program to become the input to another, as yet unknown, program.” — Doug McIlroy

Output should use **stdout**. Output of commands should be presented in a clear formatted way for
users to easily scan it, but it must also be compatible with `grep` command often used in
command chaining. Output should also be possible in json by using `--output json` flag.

Default command response should be to the stdout and not saved to a file. Anytime we want
the output to be saved to a file we should explicitly specify so by using `--save filename.txt`
flag and providing the path.


```
Address 179b6b1cb6755e31
Balance 0
Keys 2
Key 0 Public Key c8a2a318b9099cc6c872a0ec3dcd9f59d17837e4ffd6cd8a1f913ddfa769559605e1ad6ad603ebb511f5a6c8125f863abc2e9c600216edaa07104a0fe320dba7
Weight 1000
Signature Algorithm ECDSA_P256
Hash Algorithm SHA3_256
Code
pub contract Foo {
pub var bar: String
init() {
self.bar = "Hello, World!"
}
}
```

```
{"Address":"179b6b1cb6755e31","Balance":0,"Code":"CnB1YiBjb250cmFjdCBGb28gewoJcHViIHZhciBiYXI6IFN0cmluZwoKCWluaXQoKSB7CgkJc2VsZi5iYXIgPSAiSGVsbG8sIFdvcmxkISIKCX0KfQo=","Keys":[{"Index":0,"PublicKey":{},"SigAlgo":2,"HashAlgo":3,"Weight":1000,"SequenceNumber":0,"Revoked":false}],"Contracts":null}
```

## Error
Error should be human-readable, avoid printing out stack trace. It should give some
pointers as to what could have gone wrong and how to fix it.
Maybe try involving an example. However allow `--log debug` flag for complete error info
with stack. Error messages should be sent to `stderr`

```
❌ Error while dialing dial tcp 127.0.0.1:3569: connect: connection refused"
🙏 Make sure your emulator is running or connection address is correct.
```

## Saving
Saving output to files should be optional and never a default action unless it is
clear from the command name the output will be saved and that is
required for later use (example: `flow init`). Output can be piped in files, and
our CLI should resort to that for default saving. If there is a specific format
that needs to be saved or converted to we should be able to display that format as
well hence again allowing us to pipe it to files.

## Help
Main help screen must contain a list of all commands with their own description,
it must also include examples for commands. Help screen should include link to the documentation website.

Commands should have a description that is not too long (less than 100 characters).
Help should be outputed if command is ran without any arguments. Help outline should be:

```Description:
<description>
Usage:
<command> <action>
Examples:
An optional section of example(s) on how to run the command.
Commands:
<command>
<commandDescription>
<command>
<commandDescription>
Flags:
--<flag>
<flagDescription>
--<flag>
<flagDescription>
```

## Progress
Show progress for longer running commands visually. Visual feedback on longer
running commands is important so users don’t get confused if command is running
or the CLI is hang up resulting in user quitting.

```
Loading 0x1fd892083b3e2a4c...⠼
```

## Feedback
Commands should provide feedback if there is no result to be presented.
If a command completes an operation that has no result to be shown it
should write out that command was executed. This is meant to assure the user
of the completion of the command.
If user is executing a destructive command they should be asked for approval
but this command should allow passing confirmation with a flag `--yes`.

```
💾 result saved to: account.txt
```

## Exit
CLI should return zero status unless it is shut down because of an error.
This will allow chaining of commands and interfacing with other cli. If a
command is long running then provide description how to exit it (e.g. "Press Ctrl+C to stop").
```
exit status 1
```

## Colors
Base color should be white with yellow reserved for warnings and red for errors.
Blue color can be used to accent some commands but it shouldn’t be
used too much as it will confuse the user.


## Inspiration and Reference
https://clig.dev/

https://blog.developer.atlassian.com/10-design-principles-for-delightful-clis/

https://devcenter.heroku.com/articles/cli-style-guide

https://eng.localytics.com/exploring-cli-best-practices/










## Additional Notes

Thank you for your interest in contributing to the Flow CLI!
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ install-tools:
test:
GO111MODULE=on go test -coverprofile=$(COVER_PROFILE) $(if $(JSON_OUTPUT),-json,) ./...

.PHONY: test-e2e
test-e2e:
GO111MODULE=on E2E=1 go test tests/e2e_test.go

.PHONY: test-e2e-emulator
test-e2e-emulator:
flow -f tests/flow.json emulator start

.PHONY: coverage
coverage:
ifeq ($(COVER), true)
Expand Down
65 changes: 34 additions & 31 deletions cmd/flow/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,44 +22,47 @@ package main
import (
"github.com/spf13/cobra"

cli "github.com/onflow/flow-cli/flow"
"github.com/onflow/flow-cli/flow/accounts"
"github.com/onflow/flow-cli/flow/blocks"
"github.com/onflow/flow-cli/flow/cadence"
"github.com/onflow/flow-cli/flow/collections"
"github.com/onflow/flow-cli/flow/emulator"
"github.com/onflow/flow-cli/flow/events"
"github.com/onflow/flow-cli/flow/initialize"
"github.com/onflow/flow-cli/flow/keys"
"github.com/onflow/flow-cli/flow/project"
"github.com/onflow/flow-cli/flow/scripts"
"github.com/onflow/flow-cli/flow/transactions"
"github.com/onflow/flow-cli/flow/version"
"github.com/onflow/flow-cli/internal/accounts"
"github.com/onflow/flow-cli/internal/blocks"
"github.com/onflow/flow-cli/internal/cadence"
"github.com/onflow/flow-cli/internal/collections"
"github.com/onflow/flow-cli/internal/command"
"github.com/onflow/flow-cli/internal/config"
"github.com/onflow/flow-cli/internal/emulator"
"github.com/onflow/flow-cli/internal/events"
"github.com/onflow/flow-cli/internal/keys"
"github.com/onflow/flow-cli/internal/project"
"github.com/onflow/flow-cli/internal/scripts"
"github.com/onflow/flow-cli/internal/transactions"
"github.com/onflow/flow-cli/internal/version"
"github.com/onflow/flow-cli/pkg/flowcli/util"
)

var cmd = &cobra.Command{
Use: "flow",
TraverseChildren: true,
}
func main() {
var cmd = &cobra.Command{
Use: "flow",
TraverseChildren: true,
}

func init() {
cmd.AddCommand(project.Cmd)
cmd.AddCommand(initialize.Cmd)
cmd.AddCommand(accounts.Cmd)
cmd.AddCommand(blocks.Cmd)
cmd.AddCommand(collections.Cmd)
cmd.AddCommand(keys.Cmd)
cmd.AddCommand(emulator.Cmd)
cmd.AddCommand(events.Cmd)
// hot commands
config.InitCommand.AddToParent(cmd)

// structured commands
cmd.AddCommand(cadence.Cmd)
cmd.AddCommand(version.Cmd)
cmd.AddCommand(emulator.Cmd)
cmd.AddCommand(accounts.Cmd)
cmd.AddCommand(scripts.Cmd)
cmd.AddCommand(transactions.Cmd)
cmd.AddCommand(version.Cmd)
cmd.PersistentFlags().StringSliceVarP(&cli.ConfigPath, "config-path", "f", cli.ConfigPath, "Path to flow configuration file")
}
cmd.AddCommand(keys.Cmd)
cmd.AddCommand(events.Cmd)
cmd.AddCommand(blocks.Cmd)
cmd.AddCommand(collections.Cmd)
cmd.AddCommand(project.Cmd)

command.InitFlags(cmd)

func main() {
if err := cmd.Execute(); err != nil {
cli.Exit(1, err.Error())
util.Exit(1, err.Error())
}
}
Loading

0 comments on commit ba57bac

Please sign in to comment.