-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a105e18
Showing
13 changed files
with
551 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# editorconfig.org | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false | ||
|
||
[*.go] | ||
indent_style = tab | ||
indent_size = 4 | ||
|
||
[Makefile] | ||
indent_style = tab | ||
indent_size = 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
mc | ||
*.exe | ||
*.*~ | ||
*.swp | ||
.env* |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
flag "github.com/bgentry/pflag" | ||
) | ||
|
||
type Command struct { | ||
Run func(cmd *Command, args []string) | ||
Flag flag.FlagSet | ||
|
||
Usage string // first word must be command name | ||
Category string | ||
Short string | ||
Long string | ||
} | ||
|
||
func (c *Command) PrintUsage() { | ||
if c.Runnable() { | ||
fmt.Fprintf(os.Stderr, "Usage: mc %s\n", c.FullUsage()) | ||
} | ||
fmt.Fprintf(os.Stderr, "Use 'mc help %s' for more information.\n", c.Name()) | ||
} | ||
|
||
func (c *Command) PrintLongUsage() { | ||
if c.Runnable() { | ||
fmt.Fprintf(os.Stderr, "Usage: mc %s\n", c.FullUsage()) | ||
} | ||
fmt.Println(strings.Trim(c.Long, "\n")) | ||
} | ||
|
||
func (c *Command) FullUsage() string { | ||
return c.Usage | ||
} | ||
|
||
func (c *Command) Name() string { | ||
name := c.Usage | ||
i := strings.Index(name, " ") | ||
if i >= 0 { | ||
name = name[:i] | ||
} | ||
return name | ||
} | ||
|
||
func (c *Command) Runnable() bool { | ||
return c.Run != nil | ||
} | ||
|
||
const extra = " (extra)" | ||
|
||
func (c *Command) List() bool { | ||
return c.Short != "" && !strings.HasSuffix(c.Short, extra) | ||
} | ||
|
||
func (c *Command) ListAsExtra() bool { | ||
return c.Short != "" && strings.HasSuffix(c.Short, extra) | ||
} | ||
|
||
func (c *Command) ShortExtra() string { | ||
return c.Short[:len(c.Short)-len(extra)] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// +build !release | ||
|
||
package main | ||
|
||
const ( | ||
Version = "dev" | ||
) | ||
|
||
var updater *Updater |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package main | ||
|
||
import ( | ||
"io" | ||
"log" | ||
"os" | ||
"text/template" | ||
) | ||
|
||
var cmdHelp = &Command{ | ||
Usage: "help [<topic>]", | ||
Category: "mc", | ||
Long: `Help shows usage for a command or other topic.`, | ||
} | ||
|
||
func init() { | ||
cmdHelp.Run = runHelp // breaks init loop | ||
} | ||
|
||
func runHelp(cmd *Command, args []string) { | ||
if len(args) == 0 { | ||
printUsageTo(os.Stdout) | ||
return | ||
} | ||
|
||
if len(args) != 1 { | ||
printFatal("too many arguments") | ||
} | ||
|
||
switch args[0] { | ||
default: | ||
} | ||
|
||
for _, cmd := range commands { | ||
if cmd.Name() == args[0] { | ||
cmd.PrintLongUsage() | ||
return | ||
} | ||
} | ||
|
||
log.Printf("Unknown help topic: %q. Run 'mc help'.\n", args[0]) | ||
os.Exit(2) | ||
} | ||
|
||
func maxStrLen(strs []string) (strlen int) { | ||
for i := range strs { | ||
if len(strs[i]) > strlen { | ||
strlen = len(strs[i]) | ||
} | ||
} | ||
return | ||
} | ||
|
||
var usageTemplate = template.Must(template.New("usage").Parse(` | ||
Usage: mc <command> [options] [arguments] | ||
Commands: | ||
{{range .Commands}}{{if .Runnable}}{{if .List}} | ||
{{.Name | printf (print "%-" $.MaxRunListName "s")}} {{.Short}}{{end}}{{end}}{{end}} | ||
{{range .Plugins}} | ||
{{.Name | printf (print "%-" $.MaxRunListName "s")}} {{.Short}} (plugin){{end}} | ||
Run 'mc help [command]' for details. | ||
Additional help topics: | ||
{{range .Commands}}{{if not .Runnable}} | ||
{{.Name | printf "%-8s"}} {{.Short}}{{end}}{{end}} | ||
{{if .Dev}}This dev build of mc cannot auto-update itself. | ||
{{end}}`[1:])) | ||
|
||
var extraTemplate = template.Must(template.New("usage").Parse(` | ||
Additional commands: | ||
{{range .Commands}}{{if .Runnable}}{{if .ListAsExtra}} | ||
{{.Name | printf (print "%-" $.MaxRunExtraName "s")}} {{.ShortExtra}}{{end}}{{end}}{{end}} | ||
Run 'mc help [command]' for details. | ||
`[1:])) | ||
|
||
func printUsageTo(w io.Writer) { | ||
var runListNames []string | ||
for i := range commands { | ||
if commands[i].Runnable() && commands[i].List() { | ||
runListNames = append(runListNames, commands[i].Name()) | ||
} | ||
} | ||
|
||
usageTemplate.Execute(w, struct { | ||
Commands []*Command | ||
Dev bool | ||
MaxRunListName int | ||
}{ | ||
commands, | ||
Version == "dev", | ||
maxStrLen(runListNames), | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"log" | ||
"os" | ||
"strings" | ||
|
||
"github.com/mgutz/ansi" | ||
"github.com/the-obsidian/mc/rollbar" | ||
"github.com/the-obsidian/mc/term" | ||
) | ||
|
||
var commands = []*Command{ | ||
//cmdInstall, | ||
//cmdVersion, | ||
cmdHelp, | ||
|
||
//helpCommands, | ||
//helpAbout, | ||
|
||
// unlisted | ||
//cmdUpdate, | ||
} | ||
|
||
func main() { | ||
log.SetFlags(0) | ||
|
||
args := os.Args[1:] | ||
if len(args) < 1 || strings.IndexRune(args[0], '-') == 0 { | ||
printUsageTo(os.Stderr) | ||
os.Exit(2) | ||
} | ||
|
||
// Perform updates as early as possible | ||
if args[0] == cmdUpdate.Name() { | ||
cmdUpdate.Run(cmdUpdate, args) | ||
return | ||
} else if updater != nil { | ||
defer updater.backgroundRun() | ||
} | ||
|
||
if !term.IsANSI(os.Stdout) { | ||
ansi.DisableColors(true) | ||
} | ||
|
||
for _, cmd := range commands { | ||
if cmd.Name() != args[0] || cmd.Run == nil { | ||
continue | ||
} | ||
|
||
defer recoverPanic() | ||
|
||
cmd.Flag.SetDisableDuplicates(true) // allow duplicate flag options | ||
cmd.Flag.Usage = func() { | ||
cmd.PrintUsage() | ||
} | ||
if err := cmd.Flag.Parse(args[1:]); err == flag.ErrHelp { | ||
cmdHelp.Run(cmdHelp, args[:1]) | ||
} else if err != nil { | ||
printError(err.Error()) | ||
os.Exit(2) | ||
} | ||
cmd.Run(cmd, cmd.Flag.Args()) | ||
return | ||
} | ||
|
||
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", args[0]) | ||
if g := suggest(args[0]); len(g) > 0 { | ||
fmt.Fprintf(os.Stderr, "Possible alternatives: %v\n", strings.Join(g, " ")) | ||
} | ||
fmt.Fprintf(os.Stderr, "Run 'mc help' for usage.\n") | ||
os.Exit(2) | ||
} | ||
|
||
var rollbarClient = &rollbar.Client{ | ||
AppName: "mc", | ||
AppVersion: Version, | ||
Endpoint: "https://api.rollbar.com/api/1/item/", | ||
Token: "c0fd71dc4a724934b0826c73ef3cd269", | ||
} | ||
|
||
func recoverPanic() { | ||
if Version == "dev" { | ||
return | ||
} | ||
|
||
if rec := recover(); rec != nil { | ||
message := "" | ||
switch rec := rec.(type) { | ||
case error: | ||
message = rec.Error() | ||
default: | ||
message = fmt.Sprintf("%v", rec) | ||
} | ||
if err := rollbarClient.Report(message); err != nil { | ||
printError("reporting crash failed: %s", err.Error()) | ||
panic(err) | ||
} | ||
printFatal("mc encountered and reported an internal client issue") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package rollbar | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"runtime" | ||
"time" | ||
) | ||
|
||
type Client struct { | ||
AppName string | ||
AppVersion string | ||
Endpoint string | ||
Token string | ||
} | ||
|
||
func (c *Client) Report(message string) error { | ||
body := c.buildBody() | ||
data := body["data"].(map[string]interface{}) | ||
data["body"] = c.errorBody(message, 2) | ||
|
||
jsonBody, err := json.Marshal(body) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := http.Post(c.Endpoint, "application/json", bytes.NewReader(jsonBody)) | ||
if err != nil { | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode/100 != 2 { // 200, 201, 202, etc | ||
return fmt.Errorf("unexpected status code %d", resp.StatusCode) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *Client) errorBody(message string, skip int) map[string]interface{} { | ||
return map[string]interface{}{ | ||
"trace": map[string]interface{}{ | ||
"frames": c.stacktraceFrames(3 + skip), | ||
"exception": map[string]interface{}{ | ||
"class": "", | ||
"message": message, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (c *Client) stacktraceFrames(skip int) []map[string]interface{} { | ||
frames := []map[string]interface{}{} | ||
|
||
for i := skip; ; i++ { | ||
pc, file, line, ok := runtime.Caller(i) | ||
if !ok { | ||
break | ||
} | ||
f := runtime.FuncForPC(pc) | ||
fname := "unknown" | ||
if f != nil { | ||
fname = f.Name() | ||
} | ||
frames = append(frames, map[string]interface{}{ | ||
"filename": file, | ||
"lineno": line, | ||
"method:": fname, | ||
}) | ||
} | ||
return frames | ||
} | ||
|
||
func (c *Client) buildBody() map[string]interface{} { | ||
timestamp := time.Now().UTC().Unix() | ||
|
||
return map[string]interface{}{ | ||
"access_token": c.Token, | ||
"data": map[string]interface{}{ | ||
"environment": "production", | ||
"timestamp": timestamp, | ||
"platform": "client", | ||
"language": "go", | ||
"notifier": map[string]interface{}{ | ||
"name": c.AppName, | ||
"version": c.AppVersion, | ||
}, | ||
"client": map[string]interface{}{ | ||
"arch": runtime.GOARCH, | ||
"platform": runtime.GOOS, | ||
}, | ||
}, | ||
} | ||
} |
Oops, something went wrong.