func updatePlugins() { l := log.New("") l.SetFlags(0) l.SetDebug(os.Getenv("ABOT_DEBUG") == "true") if err := core.LoadConf(); err != nil { l.Fatal(err) } plugins := buildPluginFile(l) l.Info("Updating plugins...") for path, version := range plugins.Dependencies { if version != "*" { continue } l.Infof("Updating %s...\n", path) outC, err := exec. Command("/bin/sh", "-c", "go get -u "+path). CombinedOutput() if err != nil { l.Info(string(outC)) l.Fatal(err) } } embedPluginConfs(plugins, l) updateGlockfileAndInstall(l) l.Info("Success!") }
func TestPluginGenerate(t *testing.T) { defer func() { if err := os.RemoveAll("__test"); err != nil { log.Info("failed to clean up __test dir.", err) } if err := os.RemoveAll("test_here"); err != nil { log.Info("failed to clean up __test dir.", err) } }() // generate plugin l := log.New("") l.SetFlags(0) if err := generatePlugin(l, "__test"); err != nil { t.Fatal(err) } if err := generatePlugin(l, "TestHere"); err != nil { t.Fatal(err) } }
func publishPlugin(c *cli.Context) error { p := filepath.Join(os.Getenv("HOME"), ".abot.conf") fi, err := os.Open(p) if err != nil { if err.Error() == fmt.Sprintf("open %s: no such file or directory", p) { login() publishPlugin(c) return nil } log.Fatal(err) } defer func() { if err = fi.Close(); err != nil { log.Fatal(err) } }() // Prepare request if len(c.Args().First()) == 0 { log.Fatal("missing plugin's `go get` path") } reqData := struct { Path string Secret string }{ Path: c.Args().First(), Secret: core.RandSeq(24), } byt, err := json.Marshal(reqData) if err != nil { log.Fatal(err) } u := os.Getenv("ITSABOT_URL") + "/api/plugins.json" req, err := http.NewRequest("POST", u, bytes.NewBuffer(byt)) if err != nil { log.Fatal(err) } // Populate req with login credentials from ~/.abot.conf scn := bufio.NewScanner(fi) var lineNum int for scn.Scan() { line := scn.Text() cookie := &http.Cookie{} switch lineNum { case 0: cookie.Name = "iaID" case 1: cookie.Name = "iaEmail" case 2: req.Header.Set("Authorization", "Bearer "+line) case 3: cookie.Name = "iaIssuedAt" default: log.Fatal("unknown line in abot.conf") } if lineNum != 2 { cookie.Value = url.QueryEscape(line) req.AddCookie(cookie) } lineNum++ } if err = scn.Err(); err != nil { log.Fatal(err) } cookie := &http.Cookie{} cookie.Name = "iaScopes" req.AddCookie(cookie) client := &http.Client{} resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer func() { if err = resp.Body.Close(); err != nil { log.Fatal(err) } }() if resp.StatusCode == 401 { login() publishPlugin(c) } else if resp.StatusCode != 202 { log.Info("something went wrong. status code", resp.StatusCode) var msg string if err = json.NewDecoder(resp.Body).Decode(&msg); err != nil { log.Fatal(err) } log.Fatal(msg) } // Make a websocket request to get updates about the publishing process uri, err := url.Parse(os.Getenv("ITSABOT_URL")) if err != nil { log.Fatal(err) } ws, err := websocket.Dial("ws://"+uri.Host+"/api/ws", "", os.Getenv("ABOT_URL")) if err != nil { log.Fatal(err) } if err = websocket.Message.Send(ws, reqData.Secret); err != nil { log.Fatal(err) } var msg socket.Msg l := log.New("") l.SetFlags(0) l.Info("> Establishing connection with server...") var established bool var lastMsg string for { websocket.JSON.Receive(ws, &msg) if !established { l.Info("OK") established = true } if msg.Content == lastMsg { log.Info("server hung up. please try again") if err = ws.Close(); err != nil { log.Info("failed to close socket.", err) } return nil } lastMsg = msg.Content if msg.Type == socket.MsgTypeFinishedSuccess || msg.Type == socket.MsgTypeFinishedFailed { if err = ws.Close(); err != nil { log.Info("failed to close socket.", err) } return nil } if len(msg.Content) < 2 { l.Info(msg.Content) continue } tmp := msg.Content[0:2] if tmp == "> " || tmp == "==" { l.Info("") } l.Info(msg.Content) } }
func main() { rand.Seed(time.Now().UnixNano()) log.SetDebug(os.Getenv("ABOT_DEBUG") == "true") if err := core.LoadEnvVars(); err != nil { log.Fatal(err) } app := cli.NewApp() app.Commands = []cli.Command{ { Name: "new", Usage: "generate a new abot", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) if len(c.Args()) != 1 { l.Fatal("usage: abot new {name}") } if strings.Contains(c.Args().First(), " ") { l.Fatal("name cannot include a space. use camelCase") } err := newAbot(l, c.Args().First(), c.Args().Get(1)) if err != nil { l.Fatalf("could not build new abot. %s", err) } l.Info("Success. Created " + c.Args().First()) return nil }, }, { Name: "server", Aliases: []string{"s"}, Usage: "run server", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) cmd := exec.Command("/bin/sh", "-c", "go build") out, err := cmd.CombinedOutput() if err != nil { l.Info(string(out)) return err } dir, err := os.Getwd() if err != nil { return err } _, file := filepath.Split(dir) cmd = exec.Command("/bin/sh", "-c", "./"+file) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err = cmd.Start(); err != nil { return err } return cmd.Wait() }, }, { Name: "install", Aliases: []string{"i"}, Usage: "download and install plugins listed in plugins.json", Action: func(c *cli.Context) error { errChan := make(chan errMsg) l := log.New("") l.SetFlags(0) l.SetDebug(os.Getenv("ABOT_DEBUG") == "true") go func() { select { case errC := <-errChan: if errC.err == nil { // Success l.Info(errC.msg) os.Exit(0) } // Plugins install failed, so remove incomplete plugins.go file errR := os.Remove("plugins.go") if errR != nil && !os.IsNotExist(errR) { l.Info("could not remove plugins.go file.", errR) } if len(errC.msg) > 0 { l.Fatalf("could not install plugins.\n%s\n%s", errC.msg, errC.err) } l.Fatalf("could not install plugins.\n%s", errC.err) } }() installPlugins(l, errChan) for { // Keep process running until a message is received } }, }, { Name: "search", Usage: "search plugins indexed on itsabot.org", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) args := c.Args() if len(args) == 0 || len(args) > 2 { l.Fatal(errors.New(`usage: abot plugin search {term}`)) } if err := searchPlugins(args.First()); err != nil { l.Fatalf("could not start console\n%s", err) } return nil }, }, { Name: "update", Aliases: []string{"u", "upgrade"}, Usage: "update and install plugins listed in plugins.json", Action: func(c *cli.Context) error { updatePlugins() return nil }, }, { Name: "publish", Aliases: []string{"p"}, Usage: "publish a plugin to itsabot.org", Action: func(c *cli.Context) error { publishPlugin(c) return nil }, }, { Name: "test", Aliases: []string{"t"}, Usage: "tests plugins", Action: func(c *cli.Context) error { lg.SetFlags(0) count, err := testPlugin() if err != nil { return err } if count == 0 { lg.Println("No tests found. Did you run \"abot install\"?") return nil } lg.Printf("Success (%d tests).\n", count) return nil }, }, { Name: "generate", Aliases: []string{"g"}, Usage: "generate plugin scaffolding", Action: func(c *cli.Context) error { l := log.New("") l.SetFlags(0) args := c.Args() if len(args) != 1 { l.Fatal(errors.New(`usage: abot generate {name}`)) } generatePlugin(l, args.First()) l.Info("Created", args.First(), "in", filepath.Join(os.Getenv("PWD"), args.First())) return nil }, }, { Name: "login", Aliases: []string{"l"}, Usage: "log into itsabot.org to enable publishing plugins", Action: func(c *cli.Context) error { login() return nil }, }, { Name: "console", Aliases: []string{"c"}, Usage: "communicate with a running abot server", Action: func(c *cli.Context) error { if err := startConsole(c); err != nil { l := log.New("") l.SetFlags(0) l.Fatalf("could not start console\n%s", err) } return nil }, }, { Name: "dbconsole", Aliases: []string{"dbc"}, Usage: "communicate with a running abot server", Action: func(c *cli.Context) error { cmd := exec.Command("/bin/sh", "-c", "psql "+os.Getenv("ABOT_DATABASE_URL")) cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin if err := cmd.Start(); err != nil { return err } return cmd.Wait() }, }, } app.Action = func(c *cli.Context) error { cli.ShowAppHelp(c) return nil } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }
// New builds a Plugin with its trigger, RPC, and configuration settings from // its plugin.json. func New(url string) (*dt.Plugin, error) { if err := core.LoadEnvVars(); err != nil { log.Fatal(err) } db, err := core.ConnectDB("") if err != nil { return nil, err } // Read plugin.json data from within plugins.go, unmarshal into struct c := dt.PluginConfig{} if len(os.Getenv("ABOT_PATH")) > 0 { p := filepath.Join(os.Getenv("ABOT_PATH"), "plugins.go") var scn *bufio.Scanner fi, err := os.OpenFile(p, os.O_RDONLY, 0666) if os.IsNotExist(err) { goto makePlugin } if err != nil { return nil, err } defer func() { if err = fi.Close(); err != nil { log.Info("failed to close file", fi.Name()) return } }() var found bool var data string scn = bufio.NewScanner(fi) for scn.Scan() { t := scn.Text() if !found && t != url { continue } else if t == url { found = true continue } else if len(t) >= 1 && t[0] == '}' { data += t break } data += t } if err = scn.Err(); err != nil { return nil, err } if len(data) > 0 { if err = json.Unmarshal([]byte(data), &c); err != nil { return nil, err } if len(c.Name) == 0 { return nil, ErrMissingPluginName } } } makePlugin: l := log.New(c.Name) l.SetDebug(os.Getenv("ABOT_DEBUG") == "true") plg := &dt.Plugin{ Trigger: &dt.StructuredInput{}, SetBranches: func(in *dt.Msg) [][]dt.State { return nil }, Events: &dt.PluginEvents{ PostReceive: func(cmd *string) {}, PreProcessing: func(cmd *string, u *dt.User) {}, PostProcessing: func(in *dt.Msg) {}, PreResponse: func(in *dt.Msg, resp *string) {}, }, Config: c, DB: db, Log: l, } plg.SM = dt.NewStateMachine(plg) return plg, nil }