Skip to content

Commit

Permalink
refactor dependencies (#124)
Browse files Browse the repository at this point in the history
Co-authored-by: Trajan0x <trajan0x@users.noreply.github.com>
  • Loading branch information
trajan0x and trajan0x authored Aug 23, 2022
1 parent 2a8074b commit 904c560
Show file tree
Hide file tree
Showing 67 changed files with 6,229 additions and 89 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ jobs:
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: password
options: --health-cmd="mysqladmin ping" --health-interval=1s --health-timeout=1s --health-retries=30

steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
submodules: 'recursive'

# todo: consider making this a service. You'd need another way to expose the private keys for the test
- name: Run rinkeby
run: docker run -p 8045:8545 -d -v /tmp/keys/:/tmp/keys/ --name rinkeby --restart always trufflesuite/ganache-cli ganache-cli --accounts 10 --account_keys_path /tmp/keys/rinkeby --chainId 4 # --fork https://rinkeby-light.eth.linkpool.io (no need to actually fork)

- name: Go modules cache
uses: actions/cache@v2
with:
Expand Down Expand Up @@ -79,6 +82,8 @@ jobs:
MYSQL_ROOT_PASSWORD: password
MYSQL_PORT: ${{ job.services.mariadb.ports[3306] }}
GOMAXPROCS: 18
GANACHE_KEYS: /tmp/keys/rinkeby
GANACHE_RPC_URL: http://0.0.0.0:8045

- name: Generate ignore list
# generate a list of files to ignore on goveralls
Expand Down Expand Up @@ -128,6 +133,7 @@ jobs:
agents: 'agents/**'
scribe: 'scribe/**'
tools: 'tools/**'
core: 'core/**'
ethergo: 'ethergo/**'
- name: Check For Solidity Changes
id: filter_solidity
Expand Down Expand Up @@ -223,7 +229,7 @@ jobs:
with:
working-directory: ${{matrix.package}}/
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: v1.46.2
version: v1.48.0
# Path to your GolangCI-Lint config within the repo (optional)
config: ${{ env.GITHUB_WORKSPACE }}/.golangci.yml
# see: https://github.com/golangci/golangci-lint/issues/2654
Expand Down
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ linters:
# no need to disallow returning interfaces
- ireturn
- nonamedreturns
- contextcheck
- nosnakecase
fast: false

issues:
Expand Down
1 change: 1 addition & 0 deletions core/Makefile
2 changes: 2 additions & 0 deletions core/commandline/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package commandline contains common utilities for use across clis
package commandline
5 changes: 5 additions & 0 deletions core/commandline/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package commandline

import "github.com/ipfs/go-log"

var logger = log.Logger("synapse-common-flags")
55 changes: 55 additions & 0 deletions core/commandline/loglevel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package commandline

import (
"fmt"
"github.com/ipfs/go-log"
"github.com/synapsecns/synapse-node/pkg/common"
"github.com/urfave/cli/v2"
"go.uber.org/zap/zapcore"
"strings"
)

// generic flags

// LogLevel sets the log level for the node.
var LogLevel = cli.StringFlag{
Name: "log-level",
Usage: fmt.Sprintf("set the log level for the application to one of %s", LogLevelOptions()),
Value: zapcore.WarnLevel.String(),
DefaultText: "",
}

// LogLevelOptions generates log level options and returns them as a string.
func LogLevelOptions() (res string) {
for _, level := range common.LogLevels {
res += fmt.Sprintf("\"%s\" ", level.String())
}
return res
}

// IsValidLogLevel determines if a log level is valid.
func IsValidLogLevel(level string) bool {
parsedLevel, err := log.LevelFromString(strings.ToUpper(level))
if err != nil {
return false
}

for _, level := range common.LogLevels {
if parsedLevel == log.LogLevel(level) {
return true
}
}
return false
}

// SetLogLevel parses the cli context and sets the global log level accordingly.
// Note: for this to work the log level has to be set in the command.
func SetLogLevel(c *cli.Context) {
err := log.SetLogLevel("*", c.String(LogLevel.Name))
if err != nil || !IsValidLogLevel(c.String(LogLevel.Name)) {
fmt.Printf("could not set log level to %s, using default %s \n", LogLevel.Name, LogLevel.Value)
_ = log.SetLogLevel("*", LogLevel.Value)
} else {
logger.Infof("log level set to %s", c.String(LogLevel.Name))
}
}
39 changes: 39 additions & 0 deletions core/commandline/loglevel_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package commandline_test

import (
"fmt"
"github.com/ipfs/go-log"
"github.com/synapsecns/sanguine/core/commandline"
"github.com/urfave/cli/v2"
"go.uber.org/zap/zapcore"
"os"
)

var logger = log.Logger("synapse-logger-example")

func ExampleSetLogLevel() {
app := cli.NewApp()
app.Commands = cli.Commands{
{
Name: "test",
Description: "I'm a test command",
Flags: []cli.Flag{
&commandline.LogLevel,
},
Action: func(c *cli.Context) error {
commandline.SetLogLevel(c)
logger.Debug("I won't be shown if level is set to warn")
logger.Warn("I will be shown if level is set to warn")
return nil
},
},
}
fmt.Printf("running ./example %s --%s %s", app.Commands[0].Name, commandline.LogLevel.Name, zapcore.WarnLevel.String())

err := app.Run([]string{os.Args[0], app.Commands[0].Name, commandline.LogLevel.Name, zapcore.WarnLevel.String()})
if err != nil {
panic(err)
}

// output: running ./example test --log-level warn
}
218 changes: 218 additions & 0 deletions core/commandline/shell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package commandline

import (
"context"
"fmt"
"github.com/c-bata/go-prompt"
"github.com/c-bata/go-prompt/completer"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
"os"
"os/signal"
"os/user"
"strings"
"syscall"
)

const shellCommandName = "shell"

// GenerateShellCommand generates the shell command with a list of commands that the shell should take.
// TODO: this needs a more comprehensive test suite.
func GenerateShellCommand(shellCommands []*cli.Command) *cli.Command {
// explicitly exclude shell if included
capturedCommands := pruneShellCommands(shellCommands)
return &cli.Command{
Name: shellCommandName,
Usage: "start an interactive shell. Note: certain commands (reliant on authentication) are only available via the shell",
Flags: []cli.Flag{
&LogLevel,
},
Action: func(c *cli.Context) (err error) {
SetLogLevel(c)

console := cli.NewApp()
console.Commands = capturedCommands
console.Action = func(c *cli.Context) error {
fmt.Printf("Command not found. Type 'help' for a list of commands or \"%s\", \"%s\" or \"%s\" to exit.\n", quitCommand, exitCommand, quitCommandShort)
return nil
}

if c.Args().Len() == 0 {
err := console.RunContext(c.Context, strings.Fields("cmd help"))
if err != nil {
return errors.Wrap(err, "could not show help")
}
}

// warn user about sigterms
sigs := make(chan os.Signal)
go func() {
for range sigs {
fmt.Printf("\n(type \"%s\", \"%s\" or \"%s\" to exit)\n\n >", quitCommand, exitCommand, quitCommandShort)
}
}()
//nolint: govet
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
defer func() {
signal.Stop(sigs)
close(sigs)
}()

interactive := newInteractiveClient(c.Context, capturedCommands, console)
for {
p := prompt.New(
interactive.executor,
interactive.completor,
prompt.OptionCompletionWordSeparator(completer.FilePathCompletionSeparator),
prompt.OptionMaxSuggestion(3),
prompt.OptionLivePrefix(livePrefix),
)
p.Run()
}
},
}
}

// pruneShellCommands gets a list of commands including the shell command.
func pruneShellCommands(commands []*cli.Command) (prunedCommands []*cli.Command) {
// initialize shell commands
for _, command := range commands {
if command.Name != shellCommandName {
prunedCommands = append(prunedCommands, command)
}
}
return prunedCommands
}

// ShellCmd is used to launch an interactive shell. This is useful for repeatedly interacting with the cli or testing.

// livePrefix generates a prefix with the current user directory.
// this is useful for file relative commands like creating a new geth account.
func livePrefix() (prefix string, useLivePrefix bool) {
pwd, err := os.Getwd()
useLivePrefix = true
if err != nil {
prefix += " $ "
return
}

prefix += " " + pwd + " $ "

u, err := user.Current()
if err != nil {
return
}

prefix = strings.ReplaceAll(prefix, u.HomeDir, "~")
return
}

// interactiveClient object.
type interactiveClient struct {
// cli app
app *cli.App
// ctx is the parent context
//nolint: containedctx
ctx context.Context
// shellCommands are all shell commands supported by the interactive client
shellCommands []*cli.Command
}

// newInteractiveClient creates a new interactive client.
func newInteractiveClient(ctx context.Context, shellCommands []*cli.Command, app *cli.App) *interactiveClient {
return &interactiveClient{
app: app,
shellCommands: shellCommands,
ctx: ctx,
}
}

// completor handles autocompletion for the interactive client.
func (i *interactiveClient) completor(in prompt.Document) []prompt.Suggest {
// commandPrompts are prompts for commands (no flags)
commandPrompts := []prompt.Suggest{
{
Text: "help",
Description: "Shows a list of commands or help for one command",
},
}

// flagPrompts promp flags for each command
var flagPrompts []prompt.Suggest

for _, command := range i.shellCommands {
commandPrompts = append(commandPrompts, prompt.Suggest{
Text: command.Name,
Description: command.Usage,
})

for _, flag := range command.Flags {
docFlag, ok := flag.(cli.DocGenerationFlag)
if ok {
flagPrompts = append(flagPrompts, prompt.Suggest{
Text: fmt.Sprintf("%s --%s ", command.Name, longestFlag(flag)),
Description: docFlag.GetUsage(),
})
}
}
}

// get the prompts for the current text. if the user has entered part of a command this will be
// command prompts. if the user has entered a whole command flags will be suggested
prompts := prompt.FilterHasPrefix(commandPrompts, in.CurrentLineBeforeCursor(), true)
if len(prompts) == 0 {
prompts = prompt.FilterHasPrefix(flagPrompts, in.CurrentLineBeforeCursor(), true)

// right now, autocomplete for the flags will not take into account the first word so
// autocompleting `start --config` will return `start start --config`. Here, since we've
// already done the matching we cut off hte command prefix
for i, currentPrompt := range prompts {
splitText := strings.Split(currentPrompt.Text, " ")
currentPrompt.Text = strings.Join(splitText[1:], " ")
prompts[i] = currentPrompt
}
}

return prompts
}

// longestFlag gets the longest flag from all flag names
// this is used to avoid short (non-descriptive) flags from coming up in the autocomplete.
func longestFlag(flag cli.Flag) (flagName string) {
maxLength := 0
for _, name := range flag.Names() {
flagLength := len(name)
if flagLength > maxLength {
maxLength = flagLength
flagName = name
}
}
return flagName
}

// executor handles executing interactive commands.
func (i *interactiveClient) executor(line string) {
if line == "" {
return
}

if line == quitCommand || line == quitCommandShort || line == exitCommand {
os.Exit(0)
}

if line == "shell" {
fmt.Println("cannot start a shell from within a shell!")
return
}

err := i.app.RunContext(i.ctx, strings.Fields("cmd "+line))
if err != nil {
fmt.Println("error: ", err)
}
}

const (
quitCommand = "quit"
quitCommandShort = "q"
exitCommand = "exit"
)
4 changes: 4 additions & 0 deletions core/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package core

// CI is the variable used to define the ci is running.
const CI = "CI"
2 changes: 2 additions & 0 deletions core/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package core contains the core dependencies
package core
Loading

0 comments on commit 904c560

Please sign in to comment.